daux.io/index.php
2013-06-06 15:20:28 -05:00

939 lines
55 KiB
PHP

<?
/*
TM Docs
==================
Description
-----------
This is a tool for auto-generating documentation based on markdown files
located in the /docs folder of the project. To see all of the available
options and to read more about how to use the library, visit the github
repo page:
https://github.com/justinwalsh/tm-docs
Author
------
Justin Walsh (Todaymade): justin@todaymade.com, @justin_walsh
Bugs
----
To file bug reports please create an issue using the github issue tracker:
https://github.com/justinwalsh/tm-docs/issues
Please include with your report: (1) the example input; (2) the output you
expected; (3) the output Markdown actually produced.
Copyright and License
---------------------
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
This software is provided by the copyright holders and contributors "as
is" and any express or implied warranties, including, but not limited
to, the implied warranties of merchantability and fitness for a
particular purpose are disclaimed. In no event shall the copyright owner
or contributors be liable for any direct, indirect, incidental, special,
exemplary, or consequential damages (including, but not limited to,
procurement of substitute goods or services; loss of use, data, or
profits; or business interruption) however caused and on any theory of
liability, whether in contract, strict liability, or tort (including
negligence or otherwise) arising in any way out of the use of this
software, even if advised of the possibility of such damage.
*/
// Start of Markdown Class *************************************
//////////////////////////////////////////////////////////////
#
# Markdown Extra - A text-to-HTML conversion tool for web writers
#
# PHP Markdown & Extra
# Copyright (c) 2004-2009 Michel Fortin
# <http://michelf.com/projects/php-markdown/>
#
# Original Markdown
# Copyright (c) 2004-2006 John Gruber
# <http://daringfireball.net/projects/markdown/>
#
define('MARKDOWN_VERSION',"1.0.1n");define('MARKDOWNEXTRA_VERSION',"1.2.4");define('MARKDOWN_EMPTY_ELEMENT_SUFFIX'," />");define('MARKDOWN_TAB_WIDTH',4);define('MARKDOWN_FN_LINK_TITLE',"");define('MARKDOWN_FN_BACKLINK_TITLE',"");define('MARKDOWN_FN_LINK_CLASS',"");define('MARKDOWN_FN_BACKLINK_CLASS',"");define('MARKDOWN_WP_POSTS',true);define('MARKDOWN_WP_COMMENTS',true);define('MARKDOWN_PARSER_CLASS','MarkdownExtra_Parser');function Markdown($text){static $parser;if(!isset($parser)){$parser_class=MARKDOWN_PARSER_CLASS;$parser=new $parser_class;}return $parser->transform($text);}class Markdown_Parser{var $nested_brackets_depth=6;var $nested_brackets_re;var $nested_url_parenthesis_depth=4;var $nested_url_parenthesis_re;var $escape_chars='\`*_{}[]()>#+-.!';var $escape_chars_re;var $empty_element_suffix=MARKDOWN_EMPTY_ELEMENT_SUFFIX;var $tab_width=MARKDOWN_TAB_WIDTH;var $no_markup=false;var $no_entities=false;var $predef_urls=array();var $predef_titles=array();function Markdown_Parser(){$this->_initDetab();$this->prepareItalicsAndBold();$this->nested_brackets_re=str_repeat('(?>[^\[\]]+|\[',$this->nested_brackets_depth).str_repeat('\])*',$this->nested_brackets_depth);$this->nested_url_parenthesis_re=str_repeat('(?>[^()\s]+|\(',$this->nested_url_parenthesis_depth).str_repeat('(?>\)))*',$this->nested_url_parenthesis_depth);$this->escape_chars_re='['.preg_quote($this->escape_chars).']';asort($this->document_gamut);asort($this->block_gamut);asort($this->span_gamut);}var $urls=array();var $titles=array();var $html_hashes=array();var $in_anchor=false;function setup(){$this->urls=$this->predef_urls;$this->titles=$this->predef_titles;$this->html_hashes=array();$in_anchor=false;}function teardown(){$this->urls=array();$this->titles=array();$this->html_hashes=array();}function transform($text){$this->setup();$text=preg_replace('{^\xEF\xBB\xBF|\x1A}','',$text);$text=preg_replace('{\r\n?}',"\n",$text);$text.="\n\n";$text=$this->detab($text);$text=$this->hashHTMLBlocks($text);$text=preg_replace('/^[ ]+$/m','',$text);foreach($this->document_gamut as $method=>$priority){$text=$this->$method($text);}$this->teardown();return $text."\n";}var $document_gamut=array("stripLinkDefinitions"=>20,"runBasicBlockGamut"=>30,);function stripLinkDefinitions($text){$less_than_tab=$this->tab_width-1;$text=preg_replace_callback('{
^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?: # id = $1
[ ]*
\n? # maybe *one* newline
[ ]*
(?:
<(.+?)> # url = $2
|
(\S+?) # url = $3
)
[ ]*
\n? # maybe one newline
[ ]*
(?:
(?<=\s) # lookbehind for whitespace
["(]
(.*?) # title = $4
[")]
[ ]*
)? # title is optional
(?:\n+|\Z)
}xm',array(&$this,'_stripLinkDefinitions_callback'),$text);return $text;}function _stripLinkDefinitions_callback($matches){$link_id=strtolower($matches[1]);$url=$matches[2]==''?$matches[3]:$matches[2];$this->urls[$link_id]=$url;$this->titles[$link_id]=&$matches[4];return '';}function hashHTMLBlocks($text){if($this->no_markup)return $text;$less_than_tab=$this->tab_width-1;$block_tags_a_re='ins|del';$block_tags_b_re='p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'.'script|noscript|form|fieldset|iframe|math';$nested_tags_level=4;$attr='
(?> # optional tag attributes
\s # starts with whitespace
(?>
[^>"/]+ # text outside quotes
|
/+(?!>) # slash not followed by ">"
|
"[^"]*" # text inside double quotes (tolerate ">")
|
\'[^\']*\' # text inside single quotes (tolerate ">")
)*
)?
';$content=str_repeat('
(?>
[^<]+ # content without tag
|
<\2 # nested opening tag
'.$attr.' # attributes
(?>
/>
|
>',$nested_tags_level).'.*?'.str_repeat('
</\2\s*> # closing nested tag
)
|
<(?!/\2\s*> # other tags with a different name
)
)*',$nested_tags_level);$content2=str_replace('\2','\3',$content);$text=preg_replace_callback('{(?>
(?>
(?<=\n\n) # Starting after a blank line
| # or
\A\n? # the beginning of the doc
)
( # save in $1
# Match from `\n<tag>` to `</tag>\n`, handling nested tags
# in between.
[ ]{0,'.$less_than_tab.'}
<('.$block_tags_b_re.')# start tag = $2
'.$attr.'> # attributes followed by > and \n
'.$content.' # content, support nesting
</\2> # the matching end tag
[ ]* # trailing spaces/tabs
(?=\n+|\Z) # followed by a newline or end of document
| # Special version for tags of group a.
[ ]{0,'.$less_than_tab.'}
<('.$block_tags_a_re.')# start tag = $3
'.$attr.'>[ ]*\n # attributes followed by >
'.$content2.' # content, support nesting
</\3> # the matching end tag
[ ]* # trailing spaces/tabs
(?=\n+|\Z) # followed by a newline or end of document
| # Special case just for <hr />. It was easier to make a special
# case than to make the other regex more complicated.
[ ]{0,'.$less_than_tab.'}
<(hr) # start tag = $2
'.$attr.' # attributes
/?> # the matching end tag
[ ]*
(?=\n{2,}|\Z) # followed by a blank line or end of document
| # Special case for standalone HTML comments:
[ ]{0,'.$less_than_tab.'}
(?s:
<!-- .*? -->
)
[ ]*
(?=\n{2,}|\Z) # followed by a blank line or end of document
| # PHP and ASP-style processor instructions (<? and <%)
[ ]{0,'.$less_than_tab.'}
(?s:
<([?%]) # $2
.*?
\2>
)
[ ]*
(?=\n{2,}|\Z) # followed by a blank line or end of document
)
)}Sxmi',array(&$this,'_hashHTMLBlocks_callback'),$text);return $text;}function _hashHTMLBlocks_callback($matches){$text=$matches[1];$key=$this->hashBlock($text);return "\n\n$key\n\n";}function hashPart($text,$boundary='X'){$text=$this->unhash($text);static $i=0;$key="$boundary\x1A".++$i.$boundary;$this->html_hashes[$key]=$text;return $key;}function hashBlock($text){return $this->hashPart($text,'B');}var $block_gamut=array("doHeaders"=>10,"doHorizontalRules"=>20,"doLists"=>40,"doCodeBlocks"=>50,"doBlockQuotes"=>60,);function runBlockGamut($text){$text=$this->hashHTMLBlocks($text);return $this->runBasicBlockGamut($text);}function runBasicBlockGamut($text){foreach($this->block_gamut as $method=>$priority){$text=$this->$method($text);}$text=$this->formParagraphs($text);return $text;}function doHorizontalRules($text){return preg_replace('{
^[ ]{0,3} # Leading space
([-*_]) # $1: First marker
(?> # Repeated marker group
[ ]{0,2} # Zero, one, or two spaces.
\1 # Marker character
){2,} # Group repeated at least twice
[ ]* # Tailing spaces
$ # End of line.
}mx',"\n".$this->hashBlock("<hr$this->empty_element_suffix")."\n",$text);}var $span_gamut=array("parseSpan"=>-30,"doImages"=>10,"doAnchors"=>20,"doAutoLinks"=>30,"encodeAmpsAndAngles"=>40,"doItalicsAndBold"=>50,"doHardBreaks"=>60,);function runSpanGamut($text){foreach($this->span_gamut as $method=>$priority){$text=$this->$method($text);}return $text;}function doHardBreaks($text){return preg_replace_callback('/ {2,}\n/',array(&$this,'_doHardBreaks_callback'),$text);}function _doHardBreaks_callback($matches){return $this->hashPart("<br$this->empty_element_suffix\n");}function doAnchors($text){if($this->in_anchor)return $text;$this->in_anchor=true;$text=preg_replace_callback('{
( # wrap whole match in $1
\[
('.$this->nested_brackets_re.') # link text = $2
\]
[ ]? # one optional space
(?:\n[ ]*)? # one optional newline followed by spaces
\[
(.*?) # id = $3
\]
)
}xs',array(&$this,'_doAnchors_reference_callback'),$text);$text=preg_replace_callback('{
( # wrap whole match in $1
\[
('.$this->nested_brackets_re.') # link text = $2
\]
\( # literal paren
[ \n]*
(?:
<(.+?)> # href = $3
|
('.$this->nested_url_parenthesis_re.') # href = $4
)
[ \n]*
( # $5
([\'"]) # quote char = $6
(.*?) # Title = $7
\6 # matching quote
[ \n]* # ignore any spaces/tabs between closing quote and )
)? # title is optional
\)
)
}xs',array(&$this,'_doAnchors_inline_callback'),$text);$text=preg_replace_callback('{
( # wrap whole match in $1
\[
([^\[\]]+) # link text = $2; can\'t contain [ or ]
\]
)
}xs',array(&$this,'_doAnchors_reference_callback'),$text);$this->in_anchor=false;return $text;}function _doAnchors_reference_callback($matches){$whole_match=$matches[1];$link_text=$matches[2];$link_id=&$matches[3];if($link_id==""){$link_id=$link_text;}$link_id=strtolower($link_id);$link_id=preg_replace('{[ ]?\n}',' ',$link_id);if(isset($this->urls[$link_id])){$url=$this->urls[$link_id];$url=$this->encodeAttribute($url);$result="<a href=\"$url\"";if(isset($this->titles[$link_id])){$title=$this->titles[$link_id];$title=$this->encodeAttribute($title);$result.=" title=\"$title\"";}$link_text=$this->runSpanGamut($link_text);$result.=">$link_text</a>";$result=$this->hashPart($result);}else {$result=$whole_match;}return $result;}function _doAnchors_inline_callback($matches){$whole_match=$matches[1];$link_text=$this->runSpanGamut($matches[2]);$url=$matches[3]==''?$matches[4]:$matches[3];$title=&$matches[7];$url=$this->encodeAttribute($url);$result="<a href=\"$url\"";if(isset($title)){$title=$this->encodeAttribute($title);$result.=" title=\"$title\"";}$link_text=$this->runSpanGamut($link_text);$result.=">$link_text</a>";return $this->hashPart($result);}function doImages($text){$text=preg_replace_callback('{
( # wrap whole match in $1
!\[
('.$this->nested_brackets_re.') # alt text = $2
\]
[ ]? # one optional space
(?:\n[ ]*)? # one optional newline followed by spaces
\[
(.*?) # id = $3
\]
)
}xs',array(&$this,'_doImages_reference_callback'),$text);$text=preg_replace_callback('{
( # wrap whole match in $1
!\[
('.$this->nested_brackets_re.') # alt text = $2
\]
\s? # One optional whitespace character
\( # literal paren
[ \n]*
(?:
<(\S*)> # src url = $3
|
('.$this->nested_url_parenthesis_re.') # src url = $4
)
[ \n]*
( # $5
([\'"]) # quote char = $6
(.*?) # title = $7
\6 # matching quote
[ \n]*
)? # title is optional
\)
)
}xs',array(&$this,'_doImages_inline_callback'),$text);return $text;}function _doImages_reference_callback($matches){$whole_match=$matches[1];$alt_text=$matches[2];$link_id=strtolower($matches[3]);if($link_id==""){$link_id=strtolower($alt_text);}$alt_text=$this->encodeAttribute($alt_text);if(isset($this->urls[$link_id])){$url=$this->encodeAttribute($this->urls[$link_id]);$result="<img src=\"$url\" alt=\"$alt_text\"";if(isset($this->titles[$link_id])){$title=$this->titles[$link_id];$title=$this->encodeAttribute($title);$result.=" title=\"$title\"";}$result.=$this->empty_element_suffix;$result=$this->hashPart($result);}else {$result=$whole_match;}return $result;}function _doImages_inline_callback($matches){$whole_match=$matches[1];$alt_text=$matches[2];$url=$matches[3]==''?$matches[4]:$matches[3];$title=&$matches[7];$alt_text=$this->encodeAttribute($alt_text);$url=$this->encodeAttribute($url);$result="<img src=\"$url\" alt=\"$alt_text\"";if(isset($title)){$title=$this->encodeAttribute($title);$result.=" title=\"$title\"";}$result.=$this->empty_element_suffix;return $this->hashPart($result);}function doHeaders($text){$text=preg_replace_callback('{ ^(.+?)[ ]*\n(=+|-+)[ ]*\n+ }mx',array(&$this,'_doHeaders_callback_setext'),$text);$text=preg_replace_callback('{
^(\#{1,6}) # $1 = string of #\'s
[ ]*
(.+?) # $2 = Header text
[ ]*
\#* # optional closing #\'s (not counted)
\n+
}xm',array(&$this,'_doHeaders_callback_atx'),$text);return $text;}function _doHeaders_callback_setext($matches){if($matches[2]=='-'&&preg_match('{^-(?: |$)}',$matches[1]))return $matches[0];$level=$matches[2]{0}=='='?1:2;$block="<h$level>".$this->runSpanGamut($matches[1])."</h$level>";return "\n".$this->hashBlock($block)."\n\n";}function _doHeaders_callback_atx($matches){$level=strlen($matches[1]);$block="<h$level>".$this->runSpanGamut($matches[2])."</h$level>";return "\n".$this->hashBlock($block)."\n\n";}function doLists($text){$less_than_tab=$this->tab_width-1;$marker_ul_re='[*+-]';$marker_ol_re='\d+[.]';$marker_any_re="(?:$marker_ul_re|$marker_ol_re)";$markers_relist=array($marker_ul_re=>$marker_ol_re,$marker_ol_re=>$marker_ul_re,);foreach($markers_relist as $marker_re=>$other_marker_re){$whole_list_re='
( # $1 = whole list
( # $2
([ ]{0,'.$less_than_tab.'}) # $3 = number of spaces
('.$marker_re.') # $4 = first list item marker
[ ]+
)
(?s:.+?)
( # $5
\z
|
\n{2,}
(?=\S)
(?! # Negative lookahead for another list item marker
[ ]*
'.$marker_re.'[ ]+
)
|
(?= # Lookahead for another kind of list
\n
\3 # Must have the same indentation
'.$other_marker_re.'[ ]+
)
)
)
';if($this->list_level){$text=preg_replace_callback('{
^
'.$whole_list_re.'
}mx',array(&$this,'_doLists_callback'),$text);}else {$text=preg_replace_callback('{
(?:(?<=\n)\n|\A\n?) # Must eat the newline
'.$whole_list_re.'
}mx',array(&$this,'_doLists_callback'),$text);}}return $text;}function _doLists_callback($matches){$marker_ul_re='[*+-]';$marker_ol_re='\d+[.]';$marker_any_re="(?:$marker_ul_re|$marker_ol_re)";$list=$matches[1];$list_type=preg_match("/$marker_ul_re/",$matches[4])?"ul":"ol";$marker_any_re=($list_type=="ul"?$marker_ul_re:$marker_ol_re);$list.="\n";$result=$this->processListItems($list,$marker_any_re);$result=$this->hashBlock("<$list_type>\n".$result."</$list_type>");return "\n".$result."\n\n";}var $list_level=0;function processListItems($list_str,$marker_any_re){$this->list_level++;$list_str=preg_replace("/\n{2,}\\z/","\n",$list_str);$list_str=preg_replace_callback('{
(\n)? # leading line = $1
(^[ ]*) # leading whitespace = $2
('.$marker_any_re.' # list marker and space = $3
(?:[ ]+|(?=\n)) # space only required if item is not empty
)
((?s:.*?)) # list item text = $4
(?:(\n+(?=\n))|\n) # tailing blank line = $5
(?= \n* (\z | \2 ('.$marker_any_re.') (?:[ ]+|(?=\n))))
}xm',array(&$this,'_processListItems_callback'),$list_str);$this->list_level--;return $list_str;}function _processListItems_callback($matches){$item=$matches[4];$leading_line=&$matches[1];$leading_space=&$matches[2];$marker_space=$matches[3];$tailing_blank_line=&$matches[5];if($leading_line||$tailing_blank_line||preg_match('/\n{2,}/',$item)){$item=$leading_space.str_repeat(' ',strlen($marker_space)).$item;$item=$this->runBlockGamut($this->outdent($item)."\n");}else {$item=$this->doLists($this->outdent($item));$item=preg_replace('/\n+$/','',$item);$item=$this->runSpanGamut($item);}return "<li>".$item."</li>\n";}function doCodeBlocks($text){$text=preg_replace_callback('{
(?:\n\n|\A\n?)
( # $1 = the code block -- one or more lines, starting with a space/tab
(?>
[ ]{'.$this->tab_width.'} # Lines must start with a tab or a tab-width of spaces
.*\n+
)+
)
((?=^[ ]{0,'.$this->tab_width.'}\S)|\Z) # Lookahead for non-space at line-start, or end of doc
}xm',array(&$this,'_doCodeBlocks_callback'),$text);return $text;}function _doCodeBlocks_callback($matches){$codeblock=$matches[1];$codeblock=$this->outdent($codeblock);$codeblock=htmlspecialchars($codeblock,ENT_NOQUOTES);$codeblock=preg_replace('/\A\n+|\n+\z/','',$codeblock);$codeblock="<pre><code>$codeblock\n</code></pre>";return "\n\n".$this->hashBlock($codeblock)."\n\n";}function makeCodeSpan($code){$code=htmlspecialchars(trim($code),ENT_NOQUOTES);return $this->hashPart("<code>$code</code>");}var $em_relist=array(''=>'(?:(?<!\*)\*(?!\*)|(?<!_)_(?!_))(?=\S|$)(?![.,:;]\s)','*'=>'(?<=\S|^)(?<!\*)\*(?!\*)','_'=>'(?<=\S|^)(?<!_)_(?!_)',);var $strong_relist=array(''=>'(?:(?<!\*)\*\*(?!\*)|(?<!_)__(?!_))(?=\S|$)(?![.,:;]\s)','**'=>'(?<=\S|^)(?<!\*)\*\*(?!\*)','__'=>'(?<=\S|^)(?<!_)__(?!_)',);var $em_strong_relist=array(''=>'(?:(?<!\*)\*\*\*(?!\*)|(?<!_)___(?!_))(?=\S|$)(?![.,:;]\s)','***'=>'(?<=\S|^)(?<!\*)\*\*\*(?!\*)','___'=>'(?<=\S|^)(?<!_)___(?!_)',);var $em_strong_prepared_relist;function prepareItalicsAndBold(){foreach($this->em_relist as $em=>$em_re){foreach($this->strong_relist as $strong=>$strong_re){$token_relist=array();if(isset($this->em_strong_relist["$em$strong"])){$token_relist[]=$this->em_strong_relist["$em$strong"];}$token_relist[]=$em_re;$token_relist[]=$strong_re;$token_re='{('.implode('|',$token_relist).')}';$this->em_strong_prepared_relist["$em$strong"]=$token_re;}}}function doItalicsAndBold($text){$token_stack=array('');$text_stack=array('');$em='';$strong='';$tree_char_em=false;while(1){$token_re=$this->em_strong_prepared_relist["$em$strong"];$parts=preg_split($token_re,$text,2,PREG_SPLIT_DELIM_CAPTURE);$text_stack[0].=$parts[0];$token=&$parts[1];$text=&$parts[2];if(empty($token)){while($token_stack[0]){$text_stack[1].=array_shift($token_stack);$text_stack[0].=array_shift($text_stack);}break;}$token_len=strlen($token);if($tree_char_em){if($token_len==3){array_shift($token_stack);$span=array_shift($text_stack);$span=$this->runSpanGamut($span);$span="<strong><em>$span</em></strong>";$text_stack[0].=$this->hashPart($span);$em='';$strong='';}else {$token_stack[0]=str_repeat($token{0},3-$token_len);$tag=$token_len==2?"strong":"em";$span=$text_stack[0];$span=$this->runSpanGamut($span);$span="<$tag>$span</$tag>";$text_stack[0]=$this->hashPart($span);$$tag='';}$tree_char_em=false;}else if($token_len==3){if($em){for($i=0;$i<2;++$i){$shifted_token=array_shift($token_stack);$tag=strlen($shifted_token)==2?"strong":"em";$span=array_shift($text_stack);$span=$this->runSpanGamut($span);$span="<$tag>$span</$tag>";$text_stack[0].=$this->hashPart($span);$$tag='';}}else {$em=$token{0};$strong="$em$em";array_unshift($token_stack,$token);array_unshift($text_stack,'');$tree_char_em=true;}}else if($token_len==2){if($strong){if(strlen($token_stack[0])==1){$text_stack[1].=array_shift($token_stack);$text_stack[0].=array_shift($text_stack);}array_shift($token_stack);$span=array_shift($text_stack);$span=$this->runSpanGamut($span);$span="<strong>$span</strong>";$text_stack[0].=$this->hashPart($span);$strong='';}else {array_unshift($token_stack,$token);array_unshift($text_stack,'');$strong=$token;}}else {if($em){if(strlen($token_stack[0])==1){array_shift($token_stack);$span=array_shift($text_stack);$span=$this->runSpanGamut($span);$span="<em>$span</em>";$text_stack[0].=$this->hashPart($span);$em='';}else {$text_stack[0].=$token;}}else {array_unshift($token_stack,$token);array_unshift($text_stack,'');$em=$token;}}}return $text_stack[0];}function doBlockQuotes($text){$text=preg_replace_callback('/
( # Wrap whole match in $1
(?>
^[ ]*>[ ]? # ">" at the start of a line
.+\n # rest of the first line
(.+\n)* # subsequent consecutive lines
\n* # blanks
)+
)
/xm',array(&$this,'_doBlockQuotes_callback'),$text);return $text;}function _doBlockQuotes_callback($matches){$bq=$matches[1];$bq=preg_replace('/^[ ]*>[ ]?|^[ ]+$/m','',$bq);$bq=$this->runBlockGamut($bq);$bq=preg_replace('/^/m'," ",$bq);$bq=preg_replace_callback('{(\s*<pre>.+?</pre>)}sx',array(&$this,'_doBlockQuotes_callback2'),$bq);return "\n".$this->hashBlock("<blockquote>\n$bq\n</blockquote>")."\n\n";}function _doBlockQuotes_callback2($matches){$pre=$matches[1];$pre=preg_replace('/^ /m','',$pre);return $pre;}function formParagraphs($text){$text=preg_replace('/\A\n+|\n+\z/','',$text);$grafs=preg_split('/\n{2,}/',$text,-1,PREG_SPLIT_NO_EMPTY);foreach($grafs as $key=>$value){if(!preg_match('/^B\x1A[0-9]+B$/',$value)){$value=$this->runSpanGamut($value);$value=preg_replace('/^([ ]*)/',"<p>",$value);$value.="</p>";$grafs[$key]=$this->unhash($value);}else {$graf=$value;$block=$this->html_hashes[$graf];$graf=$block;$grafs[$key]=$graf;}}return implode("\n\n",$grafs);}function encodeAttribute($text){$text=$this->encodeAmpsAndAngles($text);$text=str_replace('"','&quot;',$text);return $text;}function encodeAmpsAndAngles($text){if($this->no_entities){$text=str_replace('&','&amp;',$text);}else {$text=preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/','&amp;',$text);;}$text=str_replace('<','&lt;',$text);return $text;}function doAutoLinks($text){$text=preg_replace_callback('{<((https?|ftp|dict):[^\'">\s]+)>}i',array(&$this,'_doAutoLinks_url_callback'),$text);$text=preg_replace_callback('{
<
(?:mailto:)?
(
(?:
[-!#$%&\'*+/=?^_`.{|}~\w\x80-\xFF]+
|
".*?"
)
\@
(?:
[-a-z0-9\x80-\xFF]+(\.[-a-z0-9\x80-\xFF]+)*\.[a-z]+
|
\[[\d.a-fA-F:]+\] # IPv4 & IPv6
)
)
>
}xi',array(&$this,'_doAutoLinks_email_callback'),$text);return $text;}function _doAutoLinks_url_callback($matches){$url=$this->encodeAttribute($matches[1]);$link="<a href=\"$url\">$url</a>";return $this->hashPart($link);}function _doAutoLinks_email_callback($matches){$address=$matches[1];$link=$this->encodeEmailAddress($address);return $this->hashPart($link);}function encodeEmailAddress($addr){$addr="mailto:".$addr;$chars=preg_split('/(?<!^)(?!$)/',$addr);$seed=(int)abs(crc32($addr)/strlen($addr));foreach($chars as $key=>$char){$ord=ord($char);if($ord<128){$r=($seed*(1+$key))%100;if($r>90&&$char!='@');else if($r<45)$chars[$key]='&#x'.dechex($ord).';';else $chars[$key]='&#'.$ord.';';}}$addr=implode('',$chars);$text=implode('',array_slice($chars,7));$addr="<a href=\"$addr\">$text</a>";return $addr;}function parseSpan($str){$output='';$span_re='{
(
\\\\'.$this->escape_chars_re.'
|
(?<![`\\\\])
`+ # code span marker
'.($this->no_markup?'':'
|
<!-- .*? --> # comment
|
<\?.*?\?> | <%.*?%> # processing instruction
|
<[/!$]?[-a-zA-Z0-9:_]+ # regular tags
(?>
\s
(?>[^"\'>]+|"[^"]*"|\'[^\']*\')*
)?
>
').'
)
}xs';while(1){$parts=preg_split($span_re,$str,2,PREG_SPLIT_DELIM_CAPTURE);if($parts[0]!=""){$output.=$parts[0];}if(isset($parts[1])){$output.=$this->handleSpanToken($parts[1],$parts[2]);$str=$parts[2];}else {break;}}return $output;}function handleSpanToken($token,&$str){switch($token{0}){case "\\":return $this->hashPart("&#".ord($token{1}).";");case "`":if(preg_match('/^(.*?[^`])'.preg_quote($token).'(?!`)(.*)$/sm',$str,$matches)){$str=$matches[2];$codespan=$this->makeCodeSpan($matches[1]);return $this->hashPart($codespan);}return $token;default:return $this->hashPart($token);}}function outdent($text){return preg_replace('/^(\t|[ ]{1,'.$this->tab_width.'})/m','',$text);}var $utf8_strlen='mb_strlen';function detab($text){$text=preg_replace_callback('/^.*\t.*$/m',array(&$this,'_detab_callback'),$text);return $text;}function _detab_callback($matches){$line=$matches[0];$strlen=$this->utf8_strlen;$blocks=explode("\t",$line);$line=$blocks[0];unset($blocks[0]);foreach($blocks as $block){$amount=$this->tab_width-$strlen($line,'UTF-8')%$this->tab_width;$line.=str_repeat(" ",$amount).$block;}return $line;}function _initDetab(){if(function_exists($this->utf8_strlen))return;$this->utf8_strlen=create_function('$text','return preg_match_all(
"/[\\\\x00-\\\\xBF]|[\\\\xC0-\\\\xFF][\\\\x80-\\\\xBF]*/",
$text, $m);');}function unhash($text){return preg_replace_callback('/(.)\x1A[0-9]+\1/',array(&$this,'_unhash_callback'),$text);}function _unhash_callback($matches){return $this->html_hashes[$matches[0]];}}class MarkdownExtra_Parser extends Markdown_Parser{var $fn_id_prefix="";var $fn_link_title=MARKDOWN_FN_LINK_TITLE;var $fn_backlink_title=MARKDOWN_FN_BACKLINK_TITLE;var $fn_link_class=MARKDOWN_FN_LINK_CLASS;var $fn_backlink_class=MARKDOWN_FN_BACKLINK_CLASS;var $predef_abbr=array();function MarkdownExtra_Parser(){$this->escape_chars.=':|';$this->document_gamut+=array("doFencedCodeBlocks"=>5,"stripFootnotes"=>15,"stripAbbreviations"=>25,"appendFootnotes"=>50,);$this->block_gamut+=array("doFencedCodeBlocks"=>5,"doTables"=>15,"doDefLists"=>45,);$this->span_gamut+=array("doFootnotes"=>5,"doAbbreviations"=>70,);parent::Markdown_Parser();}var $footnotes=array();var $footnotes_ordered=array();var $abbr_desciptions=array();var $abbr_word_re='';var $footnote_counter=1;function setup(){parent::setup();$this->footnotes=array();$this->footnotes_ordered=array();$this->abbr_desciptions=array();$this->abbr_word_re='';$this->footnote_counter=1;foreach($this->predef_abbr as $abbr_word=>$abbr_desc){if($this->abbr_word_re)$this->abbr_word_re.='|';$this->abbr_word_re.=preg_quote($abbr_word);$this->abbr_desciptions[$abbr_word]=trim($abbr_desc);}}function teardown(){$this->footnotes=array();$this->footnotes_ordered=array();$this->abbr_desciptions=array();$this->abbr_word_re='';parent::teardown();}var $block_tags_re='p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend';var $context_block_tags_re='script|noscript|math|ins|del';var $contain_span_tags_re='p|h[1-6]|li|dd|dt|td|th|legend|address';var $clean_tags_re='script|math';var $auto_close_tags_re='hr|img';function hashHTMLBlocks($text){list($text,)=$this->_hashHTMLBlocks_inMarkdown($text);return $text;}function _hashHTMLBlocks_inMarkdown($text,$indent=0,$enclosing_tag_re='',$span=false){if($text==='')return array('','');$newline_before_re='/(?:^\n?|\n\n)*$/';$newline_after_re='{
^ # Start of text following the tag.
(?>[ ]*<!--.*?-->)? # Optional comment.
[ ]*\n # Must be followed by newline.
}xs';$block_tag_re='{
( # $2: Capture hole tag.
</? # Any opening or closing tag.
(?> # Tag name.
'.$this->block_tags_re.' |
'.$this->context_block_tags_re.' |
'.$this->clean_tags_re.' |
(?!\s)'.$enclosing_tag_re.'
)
(?:
(?=[\s"\'/a-zA-Z0-9]) # Allowed characters after tag name.
(?>
".*?" | # Double quotes (can contain `>`)
\'.*?\' | # Single quotes (can contain `>`)
.+? # Anything but quotes and `>`.
)*?
)?
> # End of tag.
|
<!-- .*? --> # HTML Comment
|
<\?.*?\?> | <%.*?%> # Processing instruction
|
<!\[CDATA\[.*?\]\]> # CData Block
|
# Code span marker
`+
'.(!$span?' # If not in span.
|
# Indented code block
(?: ^[ ]*\n | ^ | \n[ ]*\n )
[ ]{'.($indent+4).'}[^\n]* \n
(?>
(?: [ ]{'.($indent+4).'}[^\n]* | [ ]* ) \n
)*
|
# Fenced code block marker
(?> ^ | \n )
[ ]{'.($indent).'}~~~+[ ]*\n
':'').' # End (if not is span).
)
}xs';$depth=0;$parsed="";do{$parts=preg_split($block_tag_re,$text,2,PREG_SPLIT_DELIM_CAPTURE);if($span){$void=$this->hashPart("",':');$newline="$void\n";$parts[0]=$void.str_replace("\n",$newline,$parts[0]).$void;}$parsed.=$parts[0];if(count($parts)<3){$text="";break;}$tag=$parts[1];$text=$parts[2];$tag_re=preg_quote($tag);if($tag{0}=="`"){$tag_re=preg_quote($tag);if(preg_match('{^(?>.+?|\n(?!\n))*?(?<!`)'.$tag_re.'(?!`)}',$text,$matches)){$parsed.=$tag.$matches[0];$text=substr($text,strlen($matches[0]));}else {$parsed.=$tag;}}else if($tag{0}=="\n"||$tag{0}==" "){$parsed.=$tag;}else if($tag{0}=="~"){$tag_re=preg_quote(trim($tag));if(preg_match('{^(?>.*\n)+?'.$tag_re.' *\n}',$text,$matches)){$parsed.=$tag.$matches[0];$text=substr($text,strlen($matches[0]));}else {$parsed.=$tag;}}else if(preg_match('{^<(?:'.$this->block_tags_re.')\b}',$tag)||(preg_match('{^<(?:'.$this->context_block_tags_re.')\b}',$tag)&&preg_match($newline_before_re,$parsed)&&preg_match($newline_after_re,$text))){list($block_text,$text)=$this->_hashHTMLBlocks_inHTML($tag.$text,"hashBlock",true);$parsed.="\n\n$block_text\n\n";}else if(preg_match('{^<(?:'.$this->clean_tags_re.')\b}',$tag)||$tag{1}=='!'||$tag{1}=='?'){list($block_text,$text)=$this->_hashHTMLBlocks_inHTML($tag.$text,"hashClean",false);$parsed.=$block_text;}else if($enclosing_tag_re!==''&&preg_match('{^</?(?:'.$enclosing_tag_re.')\b}',$tag)){if($tag{1}=='/')$depth--;else if($tag{strlen($tag)-2}!='/')$depth++;if($depth<0){$text=$tag.$text;break;}$parsed.=$tag;}else {$parsed.=$tag;}}while($depth>=0);return array($parsed,$text);}function _hashHTMLBlocks_inHTML($text,$hash_method,$md_attr){if($text==='')return array('','');$markdown_attr_re='
{
\s* # Eat whitespace before the `markdown` attribute
markdown
\s*=\s*
(?>
(["\']) # $1: quote delimiter
(.*?) # $2: attribute value
\1 # matching delimiter
|
([^\s>]*) # $3: unquoted attribute value
)
() # $4: make $3 always defined (avoid warnings)
}xs';$tag_re='{
( # $2: Capture hole tag.
</? # Any opening or closing tag.
[\w:$]+ # Tag name.
(?:
(?=[\s"\'/a-zA-Z0-9]) # Allowed characters after tag name.
(?>
".*?" | # Double quotes (can contain `>`)
\'.*?\' | # Single quotes (can contain `>`)
.+? # Anything but quotes and `>`.
)*?
)?
> # End of tag.
|
<!-- .*? --> # HTML Comment
|
<\?.*?\?> | <%.*?%> # Processing instruction
|
<!\[CDATA\[.*?\]\]> # CData Block
)
}xs';$original_text=$text;$depth=0;$block_text="";$parsed="";if(preg_match('/^<([\w:$]*)\b/',$text,$matches))$base_tag_name_re=$matches[1];do{$parts=preg_split($tag_re,$text,2,PREG_SPLIT_DELIM_CAPTURE);if(count($parts)<3){return array($original_text{0},substr($original_text,1));}$block_text.=$parts[0];$tag=$parts[1];$text=$parts[2];if(preg_match('{^</?(?:'.$this->auto_close_tags_re.')\b}',$tag)||$tag{1}=='!'||$tag{1}=='?'){$block_text.=$tag;}else {if(preg_match('{^</?'.$base_tag_name_re.'\b}',$tag)){if($tag{1}=='/')$depth--;else if($tag{strlen($tag)-2}!='/')$depth++;}if($md_attr&&preg_match($markdown_attr_re,$tag,$attr_m)&&preg_match('/^1|block|span$/',$attr_m[2].$attr_m[3])){$tag=preg_replace($markdown_attr_re,'',$tag);$this->mode=$attr_m[2].$attr_m[3];$span_mode=$this->mode=='span'||$this->mode!='block'&&preg_match('{^<(?:'.$this->contain_span_tags_re.')\b}',$tag);if(preg_match('/(?:^|\n)( *?)(?! ).*?$/',$block_text,$matches)){$strlen=$this->utf8_strlen;$indent=$strlen($matches[1],'UTF-8');}else {$indent=0;}$block_text.=$tag;$parsed.=$this->$hash_method($block_text);preg_match('/^<([\w:$]*)\b/',$tag,$matches);$tag_name_re=$matches[1];list($block_text,$text)=$this->_hashHTMLBlocks_inMarkdown($text,$indent,$tag_name_re,$span_mode);if($indent>0){$block_text=preg_replace("/^[ ]{1,$indent}/m","",$block_text);}if(!$span_mode)$parsed.="\n\n$block_text\n\n";else $parsed.="$block_text";$block_text="";}else $block_text.=$tag;}}while($depth>0);$parsed.=$this->$hash_method($block_text);return array($parsed,$text);}function hashClean($text){return $this->hashPart($text,'C');}function doHeaders($text){$text=preg_replace_callback('{
(^.+?) # $1: Header text
(?:[ ]+\{\#([-_:a-zA-Z0-9]+)\})? # $2: Id attribute
[ ]*\n(=+|-+)[ ]*\n+ # $3: Header footer
}mx',array(&$this,'_doHeaders_callback_setext'),$text);$text=preg_replace_callback('{
^(\#{1,6}) # $1 = string of #\'s
[ ]*
(.+?) # $2 = Header text
[ ]*
\#* # optional closing #\'s (not counted)
(?:[ ]+\{\#([-_:a-zA-Z0-9]+)\})? # id attribute
[ ]*
\n+
}xm',array(&$this,'_doHeaders_callback_atx'),$text);return $text;}function _doHeaders_attr($attr){if(empty($attr))return "";return " id=\"$attr\"";}function _doHeaders_callback_setext($matches){if($matches[3]=='-'&&preg_match('{^- }',$matches[1]))return $matches[0];$level=$matches[3]{0}=='='?1:2;$attr=$this->_doHeaders_attr($id=&$matches[2]);$block="<h$level$attr>".$this->runSpanGamut($matches[1])."</h$level>";return "\n".$this->hashBlock($block)."\n\n";}function _doHeaders_callback_atx($matches){$level=strlen($matches[1]);$attr=$this->_doHeaders_attr($id=&$matches[3]);$block="<h$level$attr>".$this->runSpanGamut($matches[2])."</h$level>";return "\n".$this->hashBlock($block)."\n\n";}function doTables($text){$less_than_tab=$this->tab_width-1;$text=preg_replace_callback('
{
^ # Start of a line
[ ]{0,'.$less_than_tab.'} # Allowed whitespace.
[|] # Optional leading pipe (present)
(.+) \n # $1: Header row (at least one pipe)
[ ]{0,'.$less_than_tab.'} # Allowed whitespace.
[|] ([ ]*[-:]+[-| :]*) \n # $2: Header underline
( # $3: Cells
(?>
[ ]* # Allowed whitespace.
[|] .* \n # Row content.
)*
)
(?=\n|\Z) # Stop at final double newline.
}xm',array(&$this,'_doTable_leadingPipe_callback'),$text);$text=preg_replace_callback('
{
^ # Start of a line
[ ]{0,'.$less_than_tab.'} # Allowed whitespace.
(\S.*[|].*) \n # $1: Header row (at least one pipe)
[ ]{0,'.$less_than_tab.'} # Allowed whitespace.
([-:]+[ ]*[|][-| :]*) \n # $2: Header underline
( # $3: Cells
(?>
.* [|] .* \n # Row content
)*
)
(?=\n|\Z) # Stop at final double newline.
}xm',array(&$this,'_DoTable_callback'),$text);return $text;}function _doTable_leadingPipe_callback($matches){$head=$matches[1];$underline=$matches[2];$content=$matches[3];$content=preg_replace('/^ *[|]/m','',$content);return $this->_doTable_callback(array($matches[0],$head,$underline,$content));}function _doTable_callback($matches){$head=$matches[1];$underline=$matches[2];$content=$matches[3];$head=preg_replace('/[|] *$/m','',$head);$underline=preg_replace('/[|] *$/m','',$underline);$content=preg_replace('/[|] *$/m','',$content);$separators=preg_split('/ *[|] */',$underline);foreach($separators as $n=>$s){if(preg_match('/^ *-+: *$/',$s))$attr[$n]=' align="right"';else if(preg_match('/^ *:-+: *$/',$s))$attr[$n]=' align="center"';else if(preg_match('/^ *:-+ *$/',$s))$attr[$n]=' align="left"';else $attr[$n]='';}$head=$this->parseSpan($head);$headers=preg_split('/ *[|] */',$head);$col_count=count($headers);$text="<table>\n";$text.="<thead>\n";$text.="<tr>\n";foreach($headers as $n=>$header)$text.=" <th$attr[$n]>".$this->runSpanGamut(trim($header))."</th>\n";$text.="</tr>\n";$text.="</thead>\n";$rows=explode("\n",trim($content,"\n"));$text.="<tbody>\n";foreach($rows as $row){$row=$this->parseSpan($row);$row_cells=preg_split('/ *[|] */',$row,$col_count);$row_cells=array_pad($row_cells,$col_count,'');$text.="<tr>\n";foreach($row_cells as $n=>$cell)$text.=" <td$attr[$n]>".$this->runSpanGamut(trim($cell))."</td>\n";$text.="</tr>\n";}$text.="</tbody>\n";$text.="</table>";return $this->hashBlock($text)."\n";}function doDefLists($text){$less_than_tab=$this->tab_width-1;$whole_list_re='(?>
( # $1 = whole list
( # $2
[ ]{0,'.$less_than_tab.'}
((?>.*\S.*\n)+) # $3 = defined term
\n?
[ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition
)
(?s:.+?)
( # $4
\z
|
\n{2,}
(?=\S)
(?! # Negative lookahead for another term
[ ]{0,'.$less_than_tab.'}
(?: \S.*\n )+? # defined term
\n?
[ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition
)
(?! # Negative lookahead for another definition
[ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition
)
)
)
)';$text=preg_replace_callback('{
(?>\A\n?|(?<=\n\n))
'.$whole_list_re.'
}mx',array(&$this,'_doDefLists_callback'),$text);return $text;}function _doDefLists_callback($matches){$list=$matches[1];$result=trim($this->processDefListItems($list));$result="<dl>\n".$result."\n</dl>";return $this->hashBlock($result)."\n\n";}function processDefListItems($list_str){$less_than_tab=$this->tab_width-1;$list_str=preg_replace("/\n{2,}\\z/","\n",$list_str);$list_str=preg_replace_callback('{
(?>\A\n?|\n\n+) # leading line
( # definition terms = $1
[ ]{0,'.$less_than_tab.'} # leading whitespace
(?![:][ ]|[ ]) # negative lookahead for a definition
# mark (colon) or more whitespace.
(?> \S.* \n)+? # actual term (not whitespace).
)
(?=\n?[ ]{0,3}:[ ]) # lookahead for following line feed
# with a definition mark.
}xm',array(&$this,'_processDefListItems_callback_dt'),$list_str);$list_str=preg_replace_callback('{
\n(\n+)? # leading line = $1
( # marker space = $2
[ ]{0,'.$less_than_tab.'} # whitespace before colon
[:][ ]+ # definition mark (colon)
)
((?s:.+?)) # definition text = $3
(?= \n+ # stop at next definition mark,
(?: # next term or end of text
[ ]{0,'.$less_than_tab.'} [:][ ] |
<dt> | \z
)
)
}xm',array(&$this,'_processDefListItems_callback_dd'),$list_str);return $list_str;}function _processDefListItems_callback_dt($matches){$terms=explode("\n",trim($matches[1]));$text='';foreach($terms as $term){$term=$this->runSpanGamut(trim($term));$text.="\n<dt>".$term."</dt>";}return $text."\n";}function _processDefListItems_callback_dd($matches){$leading_line=$matches[1];$marker_space=$matches[2];$def=$matches[3];if($leading_line||preg_match('/\n{2,}/',$def)){$def=str_repeat(' ',strlen($marker_space)).$def;$def=$this->runBlockGamut($this->outdent($def."\n\n"));$def="\n".$def."\n";}else {$def=rtrim($def);$def=$this->runSpanGamut($this->outdent($def));}return "\n<dd>".$def."</dd>\n";}function doFencedCodeBlocks($text){$less_than_tab=$this->tab_width;$text=preg_replace_callback('{
(?:\n|\A)
# 1: Opening marker
(
~{3,} # Marker: three tilde or more.
)
[ ]* \n # Whitespace and newline following marker.
# 2: Content
(
(?>
(?!\1 [ ]* \n) # Not a closing marker.
.*\n+
)+
)
# Closing marker.
\1 [ ]* \n
}xm',array(&$this,'_doFencedCodeBlocks_callback'),$text);return $text;}function _doFencedCodeBlocks_callback($matches){$codeblock=$matches[2];$codeblock=htmlspecialchars($codeblock,ENT_NOQUOTES);$codeblock=preg_replace_callback('/^\n+/',array(&$this,'_doFencedCodeBlocks_newlines'),$codeblock);$codeblock="<pre><code>$codeblock</code></pre>";return "\n\n".$this->hashBlock($codeblock)."\n\n";}function _doFencedCodeBlocks_newlines($matches){return str_repeat("<br$this->empty_element_suffix",strlen($matches[0]));}var $em_relist=array(''=>'(?:(?<!\*)\*(?!\*)|(?<![a-zA-Z0-9_])_(?!_))(?=\S|$)(?![.,:;]\s)','*'=>'(?<=\S|^)(?<!\*)\*(?!\*)','_'=>'(?<=\S|^)(?<!_)_(?![a-zA-Z0-9_])',);var $strong_relist=array(''=>'(?:(?<!\*)\*\*(?!\*)|(?<![a-zA-Z0-9_])__(?!_))(?=\S|$)(?![.,:;]\s)','**'=>'(?<=\S|^)(?<!\*)\*\*(?!\*)','__'=>'(?<=\S|^)(?<!_)__(?![a-zA-Z0-9_])',);var $em_strong_relist=array(''=>'(?:(?<!\*)\*\*\*(?!\*)|(?<![a-zA-Z0-9_])___(?!_))(?=\S|$)(?![.,:;]\s)','***'=>'(?<=\S|^)(?<!\*)\*\*\*(?!\*)','___'=>'(?<=\S|^)(?<!_)___(?![a-zA-Z0-9_])',);function formParagraphs($text){$text=preg_replace('/\A\n+|\n+\z/','',$text);$grafs=preg_split('/\n{2,}/',$text,-1,PREG_SPLIT_NO_EMPTY);foreach($grafs as $key=>$value){$value=trim($this->runSpanGamut($value));$is_p=!preg_match('/^B\x1A[0-9]+B|^C\x1A[0-9]+C$/',$value);if($is_p){$value="<p>$value</p>";}$grafs[$key]=$value;}$text=implode("\n\n",$grafs);$text=$this->unhash($text);return $text;}function stripFootnotes($text){$less_than_tab=$this->tab_width-1;$text=preg_replace_callback('{
^[ ]{0,'.$less_than_tab.'}\[\^(.+?)\][ ]?: # note_id = $1
[ ]*
\n? # maybe *one* newline
( # text = $2 (no blank lines allowed)
(?:
.+ # actual text
|
\n # newlines but
(?!\[\^.+?\]:\s)# negative lookahead for footnote marker.
(?!\n+[ ]{0,3}\S)# ensure line is not blank and followed
# by non-indented content
)*
)
}xm',array(&$this,'_stripFootnotes_callback'),$text);return $text;}function _stripFootnotes_callback($matches){$note_id=$this->fn_id_prefix.$matches[1];$this->footnotes[$note_id]=$this->outdent($matches[2]);return '';}function doFootnotes($text){if(!$this->in_anchor){$text=preg_replace('{\[\^(.+?)\]}',"F\x1Afn:\\1\x1A:",$text);}return $text;}function appendFootnotes($text){$text=preg_replace_callback('{F\x1Afn:(.*?)\x1A:}',array(&$this,'_appendFootnotes_callback'),$text);if(!empty($this->footnotes_ordered)){$text.="\n\n";$text.="<div class=\"footnotes\">\n";$text.="<hr".$this->empty_element_suffix."\n";$text.="<ol>\n\n";$attr=" rev=\"footnote\"";if($this->fn_backlink_class!=""){$class=$this->fn_backlink_class;$class=$this->encodeAttribute($class);$attr.=" class=\"$class\"";}if($this->fn_backlink_title!=""){$title=$this->fn_backlink_title;$title=$this->encodeAttribute($title);$attr.=" title=\"$title\"";}$num=0;while(!empty($this->footnotes_ordered)){$footnote=reset($this->footnotes_ordered);$note_id=key($this->footnotes_ordered);unset($this->footnotes_ordered[$note_id]);$footnote.="\n";$footnote=$this->runBlockGamut("$footnote\n");$footnote=preg_replace_callback('{F\x1Afn:(.*?)\x1A:}',array(&$this,'_appendFootnotes_callback'),$footnote);$attr=str_replace("%%",++$num,$attr);$note_id=$this->encodeAttribute($note_id);$backlink="<a href=\"#fnref:$note_id\"$attr>&#8617;</a>";if(preg_match('{</p>$}',$footnote)){$footnote=substr($footnote,0,-4)."&#160;$backlink</p>";}else {$footnote.="\n\n<p>$backlink</p>";}$text.="<li id=\"fn:$note_id\">\n";$text.=$footnote."\n";$text.="</li>\n\n";}$text.="</ol>\n";$text.="</div>";}return $text;}function _appendFootnotes_callback($matches){$node_id=$this->fn_id_prefix.$matches[1];if(isset($this->footnotes[$node_id])){$this->footnotes_ordered[$node_id]=$this->footnotes[$node_id];unset($this->footnotes[$node_id]);$num=$this->footnote_counter++;$attr=" rel=\"footnote\"";if($this->fn_link_class!=""){$class=$this->fn_link_class;$class=$this->encodeAttribute($class);$attr.=" class=\"$class\"";}if($this->fn_link_title!=""){$title=$this->fn_link_title;$title=$this->encodeAttribute($title);$attr.=" title=\"$title\"";}$attr=str_replace("%%",$num,$attr);$node_id=$this->encodeAttribute($node_id);return "<sup id=\"fnref:$node_id\">"."<a href=\"#fn:$node_id\"$attr>$num</a>"."</sup>";}return "[^".$matches[1]."]";}function stripAbbreviations($text){$less_than_tab=$this->tab_width-1;$text=preg_replace_callback('{
^[ ]{0,'.$less_than_tab.'}\*\[(.+?)\][ ]?: # abbr_id = $1
(.*) # text = $2 (no blank lines allowed)
}xm',array(&$this,'_stripAbbreviations_callback'),$text);return $text;}function _stripAbbreviations_callback($matches){$abbr_word=$matches[1];$abbr_desc=$matches[2];if($this->abbr_word_re)$this->abbr_word_re.='|';$this->abbr_word_re.=preg_quote($abbr_word);$this->abbr_desciptions[$abbr_word]=trim($abbr_desc);return '';}function doAbbreviations($text){if($this->abbr_word_re){$text=preg_replace_callback('{'.'(?<![\w\x1A])'.'(?:'.$this->abbr_word_re.')'.'(?![\w\x1A])'.'}',array(&$this,'_doAbbreviations_callback'),$text);}return $text;}function _doAbbreviations_callback($matches){$abbr=$matches[0];if(isset($this->abbr_desciptions[$abbr])){$desc=$this->abbr_desciptions[$abbr];if(empty($desc)){return $this->hashPart("<abbr>$abbr</abbr>");}else {$desc=$this->encodeAttribute($desc);return $this->hashPart("<abbr title=\"$desc\">$abbr</abbr>");}}else {return $matches[0];}}}define('MARKDOWNEXTRAEXTENDED_VERSION',"0.3");function MarkdownExtended($text,$default_claases=array()){$parser=new MarkdownExtraExtended_Parser($default_claases);return $parser->transform($text);}class MarkdownExtraExtended_Parser extends MarkdownExtra_Parser{var $block_tags_re='figure|figcaption|p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend';var $default_classes;function MarkdownExtraExtended_Parser($default_classes=array()){$default_classes=$default_classes;$this->block_gamut+=array("doFencedFigures"=>7,);parent::MarkdownExtra_Parser();}function transform($text){$text=parent::transform($text);return $text;}function doHardBreaks($text){return preg_replace_callback('/ *\n/',array(&$this,'_doHardBreaks_callback'),$text);}function doBlockQuotes($text){$text=preg_replace_callback('/
(?>^[ ]*>[ ]?
(?:\((.+?)\))?
[ ]*(.+\n(?:.+\n)*)
)+
/xm',array(&$this,'_doBlockQuotes_callback'),$text);return $text;}function _doBlockQuotes_callback($matches){$cite=$matches[1];$bq='> '.$matches[2];$bq=preg_replace('/^[ ]*>[ ]?|^[ ]+$/m','',$bq);$bq=$this->runBlockGamut($bq);$bq=preg_replace('/^/m'," ",$bq);$bq=preg_replace_callback('{(\s*<pre>.+?</pre>)}sx',array(&$this,'_doBlockQuotes_callback2'),$bq);$res="<blockquote";$res.=empty($cite)?">":" cite=\"$cite\">";$res.="\n$bq\n</blockquote>";return "\n".$this->hashBlock($res)."\n\n";}function doFencedCodeBlocks($text){$less_than_tab=$this->tab_width;$text=preg_replace_callback('{
(?:\n|\A)
# 1: Opening marker
(
~{3,}|`{3,} # Marker: three tilde or more.
)
[ ]?(\w+)?(?:,[ ]?(\d+))?[ ]* \n # Whitespace and newline following marker.
# 3: Content
(
(?>
(?!\1 [ ]* \n) # Not a closing marker.
.*\n+
)+
)
# Closing marker.
\1 [ ]* \n
}xm',array(&$this,'_doFencedCodeBlocks_callback'),$text);return $text;}function _doFencedCodeBlocks_callback($matches){$codeblock=$matches[4];$codeblock=htmlspecialchars($codeblock,ENT_NOQUOTES);$codeblock=preg_replace_callback('/^\n+/',array(&$this,'_doFencedCodeBlocks_newlines'),$codeblock);$cb=empty($matches[3])?"<pre><code":"<pre class=\"linenums:$matches[3]\"><code";$cb.=empty($matches[2])?">":" class=\"language-$matches[2]\">";$cb.="$codeblock</code></pre>";return "\n\n".$this->hashBlock($cb)."\n\n";}function doFencedFigures($text){$text=preg_replace_callback('{
(?:\n|\A)
# 1: Opening marker
(
={3,} # Marker: equal sign.
)
[ ]?(?:\[([^\]]+)\])?[ ]* \n # Whitespace and newline following marker.
# 3: Content
(
(?>
(?!\1 [ ]?(?:\[([^\]]+)\])?[ ]* \n) # Not a closing marker.
.*\n+
)+
)
# Closing marker.
\1 [ ]?(?:\[([^\]]+)\])?[ ]* \n
}xm',array(&$this,'_doFencedFigures_callback'),$text);return $text;}function _doFencedFigures_callback($matches){$topcaption=empty($matches[2])?null:$this->runBlockGamut($matches[2]);$bottomcaption=empty($matches[5])?null:$this->runBlockGamut($matches[5]);$figure=$matches[3];$figure=$this->runBlockGamut($figure);$figure=preg_replace('/^/m'," ",$figure);$figure=preg_replace_callback('{(\s*<pre>.+?</pre>)}sx',array(&$this,'_doBlockQuotes_callback2'),$figure);$res="<figure>";if(!empty($topcaption)){$res.="\n<figcaption>$topcaption</figcaption>";}$res.="\n$figure\n";if(!empty($bottomcaption)&&empty($topcaption)){$res.="<figcaption>$bottomcaption</figcaption>";}$res.="</figure>";return "\n".$this->hashBlock($res)."\n\n";}}
// End of Markdown Class *************************************
//////////////////////////////////////////////////////////////
$version = '1.0';
// TM-Docs Functions
function get_options() {
$options = array(
'title' => "Documentation",
'homepage' => false,
'theme' => 'spacelab',
'hightlight' => 'github',
'repo' => false,
'twitter' => array(),
'links' => array()
);
// Load User Config
$config_file = './docs/config.json';
if (file_exists($config_file)) {
$config = json_decode(file_get_contents($config_file), true);
$options = array_merge($options, $config);
}
// Homepage Redirect?
$path = url_path();
if ($path === '/') {
// Custom Homepage?
if ($options['homepage']) {
header('Location: '.$options['homepage']);
}
}
return $options;
}
function load_page($tree) {
$branch = find_branch($tree);
if (isset($branch['type']) && $branch['type'] == 'file') {
$html = '<div class="page-header"><h1>'. $branch['title'] . '</h1></div>';
$html .= MarkdownExtended(file_get_contents($branch['path']));
return $html;
} else {
return "Oh No. That page dosn't exist";
}
}
function find_branch($tree) {
$path = url_params();
foreach($path as $peice) {
if (isset($tree[$peice])) {
if ($tree[$peice]['type'] == 'folder') {
$tree = $tree[$peice]['tree'];
} else {
$tree = $tree[$peice];
}
} else {
return false;
}
}
return $tree;
}
function url_path() {
$url = parse_url($_SERVER['REQUEST_URI']);
$url = $url['path'];
return $url;
}
function url_params() {
$url = url_path();
$params = explode('/', trim($url, '/'));
return $params;
}
function clean_sort($text) {
// Remove .md file extension
$text = str_replace('.md', '', $text);
// Remove sort placeholder
$parts = explode('_', $text);
if (isset($parts[0]) && is_numeric($parts[0])) {
unset($parts[0]);
}
$text = implode('_', $parts);
return $text;
}
function clean_name($text) {
$text = str_replace('_', ' ', $text);
return $text;
}
function build_nav($tree, $url_params = false) {
if (!is_array($url_params)) {
$url_params = url_params();
}
$url_path = url_path();
$html = '<ul class="nav nav-list">';
foreach($tree as $key => $val) {
// Active Tree Node
if (isset($url_params[0]) && $url_params[0] == $val['clean']) {
array_shift($url_params);
// Final Node
if ($url_path == $val['url']) {
$html .= '<li class="active">';
} else {
$html .= '<li class="open">';
}
} else {
$html .= '<li>';
}
if ($val['type'] == 'folder') {
$html .= '<a href="#" class="aj-nav folder">'.$val['name'].'</a>';
$html .= build_nav($val['tree'], $url_params);
} else {
$html .= '<a href="'.$val['url'].'">'.$val['name'].'</a>';
}
$html .= '</li>';
}
$html .= '</ul>';
return $html;
}
function get_tree($path = '.', $clean_path = '', $title = ''){
$tree = array();
$ignore = array('config.json', 'cgi-bin', '.', '..');
$dh = @opendir($path);
$index = 0;
// Loop through the directory
while(false !== ($file = readdir($dh))){
// Check that this file is not to be ignored
if(!in_array($file, $ignore)) {
$full_path = "$path/$file";
$clean_sort = clean_sort($file);
$url = $clean_path . '/' . $clean_sort;
$clean_name = clean_name($clean_sort);
// Title
if (empty($title)) {
$full_title = $clean_name;
} else {
$full_title = $title . ': ' . $clean_name;
}
if(is_dir("$path/$file")) {
// Directory
$tree[$clean_sort] = array(
'type' => 'folder',
'name' => $clean_name,
'title' => $full_title,
'path' => $full_path,
'clean' => $clean_sort,
'url' => $url,
'tree'=> get_tree($full_path, $url, $full_title)
);
} else {
// File
$tree[$clean_sort] = array(
'type' => 'file',
'name' => $clean_name,
'title' => $full_title,
'path' => $full_path,
'clean' => $clean_sort,
'url' => $url,
);
}
}
$index++;
}
// Close the directory handle
closedir($dh);
return $tree;
}
// App Code
$options = get_options();
$tree = get_tree("docs");
?>
<!DOCTYPE html>
<html>
<head>
<!-- Bootstrap -->
<link rel="stylesheet" href="https://s3.amazonaws.com/todaymade/tools/tm-docs/themes/<?=$options['theme'];?>.css">
<style type="text/css">
html, body {
margin: 0;
padding: 0;
height: 100%;
}
.container-fluid {
margin: 0 auto;
height: 100%;
padding: 0;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.columns {
height: 100%;
}
.content-area, .article-tree {
overflow:auto;
height: 100%;
}
li ul {
display: none;
}
li.open > ul {
display: block;
}
a.folder {
font-weight: bold;
}
pre {
padding: 0;
}
.footer {
position: fixed;
bottom:0;
left: 0;
padding: 15px;
}
#github-ribbon {
position: absolute;
top: 0;
right: 0;
}
</style>
<!-- hightlight.js -->
<link rel="stylesheet" href="http://yandex.st/highlightjs/7.3/styles/<?=$options['hightlight'];?>.min.css">
<script src="http://yandex.st/highlightjs/7.3/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
<!-- Navigation -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.0/jquery.min.js"></script>
<script type='text/javascript'>
$(function() {
$('.aj-nav').click(function(e) {
e.preventDefault();
$(this).parent().siblings().find('ul').slideUp();
$(this).next().slideToggle();
});
// Bootstrap Table Class
$('table').addClass('table');
});
</script>
</head>
<body>
<? if ($options['repo']) { ?>
<a href="https://github.com/<?=$options['repo']; ?>" target="_blank" id="github-ribbon"><img src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a>
<? } ?>
<div class="container-fluid wrapper">
<div class="navbar navbar-static-top">
<div class="navbar-inner">
<a class="brand" href="/"><?=$options['title']; ?></a>
</div>
</div>
<div class="row-fluid columns content">
<div class="span2 article-tree">
<!-- Navigation -->
<? echo build_nav($tree); ?>
<!-- Links -->
<? if (!empty($options['links'])) { ?>
<div class="well">
<? foreach($options['links'] as $name => $url) { ?>
<a href="<?=$url;?>" target="_blank"><?=$name;?></a><br>
<? } ?>
</div>
<? } ?>
<!-- Twitter -->
<? if (!empty($options['twitter'])) { ?>
<? foreach($options['twitter'] as $handle) { ?>
<div class="extra twitter">
<iframe allowtransparency="true" frameborder="0" scrolling="no" style="width:162px; height:20px;" src="https://platform.twitter.com/widgets/follow_button.html?screen_name=<?=$handle;?>&amp;show_count=false"></iframe>
</div>
<? } ?>
<? } ?>
<!-- Footer -->
<div class="footer">Generate by <a href="https://github.com/justinwalsh/tm-docs">TM-Docs</a></div>
</div>
<div class="span10 content-area">
<? echo load_page($tree); ?>
</div>
</div>
</div>
</body>
</html>