From cbb2a3f4195587f397b958b7bbb5b21d20260296 Mon Sep 17 00:00:00 2001 From: Aaron Parecki Date: Mon, 22 Jan 2018 12:22:32 -0500 Subject: [PATCH] remove docs that are duplicated in quill and on the wiki adds screenshot for docs page --- composer.json | 3 +- controllers/controllers.php | 4 +- lib/markdown.php | 2953 ------------------------ public/images/teacup-screenshot.png | Bin 0 -> 88606 bytes views/creating-a-micropub-endpoint.php | 94 - views/docs.php | 28 +- 6 files changed, 7 insertions(+), 3075 deletions(-) delete mode 100644 lib/markdown.php create mode 100644 public/images/teacup-screenshot.png delete mode 100644 views/creating-a-micropub-endpoint.php diff --git a/composer.json b/composer.json index 6973539..659191e 100644 --- a/composer.json +++ b/composer.json @@ -15,8 +15,7 @@ "files": [ "lib/Savant.php", "lib/config.php", - "lib/helpers.php", - "lib/markdown.php" + "lib/helpers.php" ] } } diff --git a/controllers/controllers.php b/controllers/controllers.php index 03b3cc9..99d5802 100644 --- a/controllers/controllers.php +++ b/controllers/controllers.php @@ -117,10 +117,10 @@ $app->post('/prefs', function() use($app) { }); $app->get('/creating-a-token-endpoint', function() use($app) { - $app->redirect('http://indiewebcamp.com/token-endpoint', 301); + $app->redirect('https://indieweb.org/token-endpoint', 301); }); $app->get('/creating-a-micropub-endpoint', function() use($app) { - render('creating-a-micropub-endpoint', array('title' => 'Creating a Micropub Endpoint')); + $app->redirect('https://indieweb.org/Micropub', 301); }); $app->get('/docs', function() use($app) { diff --git a/lib/markdown.php b/lib/markdown.php deleted file mode 100644 index 8fba9ab..0000000 --- a/lib/markdown.php +++ /dev/null @@ -1,2953 +0,0 @@ - -# -# Original Markdown -# Copyright (c) 2004-2006 John Gruber -# -# - - -define( 'MARKDOWN_VERSION', "1.0.1o" ); # Sun 8 Jan 2012 -define( 'MARKDOWNEXTRA_VERSION', "1.2.5" ); # Sun 8 Jan 2012 - - -# -# Global default settings: -# - -# Change to ">" for HTML output -@define( 'MARKDOWN_EMPTY_ELEMENT_SUFFIX', " />"); - -# Define the width of a tab for code blocks. -@define( 'MARKDOWN_TAB_WIDTH', 4 ); - -# Optional title attribute for footnote links and backlinks. -@define( 'MARKDOWN_FN_LINK_TITLE', "" ); -@define( 'MARKDOWN_FN_BACKLINK_TITLE', "" ); - -# Optional class attribute for footnote links and backlinks. -@define( 'MARKDOWN_FN_LINK_CLASS', "" ); -@define( 'MARKDOWN_FN_BACKLINK_CLASS', "" ); - - -# -# WordPress settings: -# - -# Change to false to remove Markdown from posts and/or comments. -@define( 'MARKDOWN_WP_POSTS', true ); -@define( 'MARKDOWN_WP_COMMENTS', true ); - - - -### Standard Function Interface ### - -@define( 'MARKDOWN_PARSER_CLASS', 'MarkdownExtra_Parser' ); - -function Markdown($text) { -# -# Initialize the parser and return the result of its transform method. -# - # Setup static parser variable. - static $parser; - if (!isset($parser)) { - $parser_class = MARKDOWN_PARSER_CLASS; - $parser = new $parser_class; - } - - # Transform text using parser. - return $parser->transform($text); -} - - -### WordPress Plugin Interface ### - -/* -Plugin Name: Markdown Extra -Plugin URI: http://michelf.com/projects/php-markdown/ -Description: Markdown syntax allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by John Gruber. More... -Version: 1.2.5 -Author: Michel Fortin -Author URI: http://michelf.com/ -*/ - -if (isset($wp_version)) { - # More details about how it works here: - # - - # Post content and excerpts - # - Remove WordPress paragraph generator. - # - Run Markdown on excerpt, then remove all tags. - # - Add paragraph tag around the excerpt, but remove it for the excerpt rss. - if (MARKDOWN_WP_POSTS) { - remove_filter('the_content', 'wpautop'); - remove_filter('the_content_rss', 'wpautop'); - remove_filter('the_excerpt', 'wpautop'); - add_filter('the_content', 'mdwp_MarkdownPost', 6); - add_filter('the_content_rss', 'mdwp_MarkdownPost', 6); - add_filter('get_the_excerpt', 'mdwp_MarkdownPost', 6); - add_filter('get_the_excerpt', 'trim', 7); - add_filter('the_excerpt', 'mdwp_add_p'); - add_filter('the_excerpt_rss', 'mdwp_strip_p'); - - remove_filter('content_save_pre', 'balanceTags', 50); - remove_filter('excerpt_save_pre', 'balanceTags', 50); - add_filter('the_content', 'balanceTags', 50); - add_filter('get_the_excerpt', 'balanceTags', 9); - } - - # Add a footnote id prefix to posts when inside a loop. - function mdwp_MarkdownPost($text) { - static $parser; - if (!$parser) { - $parser_class = MARKDOWN_PARSER_CLASS; - $parser = new $parser_class; - } - if (is_single() || is_page() || is_feed()) { - $parser->fn_id_prefix = ""; - } else { - $parser->fn_id_prefix = get_the_ID() . "."; - } - return $parser->transform($text); - } - - # Comments - # - Remove WordPress paragraph generator. - # - Remove WordPress auto-link generator. - # - Scramble important tags before passing them to the kses filter. - # - Run Markdown on excerpt then remove paragraph tags. - if (MARKDOWN_WP_COMMENTS) { - remove_filter('comment_text', 'wpautop', 30); - remove_filter('comment_text', 'make_clickable'); - add_filter('pre_comment_content', 'Markdown', 6); - add_filter('pre_comment_content', 'mdwp_hide_tags', 8); - add_filter('pre_comment_content', 'mdwp_show_tags', 12); - add_filter('get_comment_text', 'Markdown', 6); - add_filter('get_comment_excerpt', 'Markdown', 6); - add_filter('get_comment_excerpt', 'mdwp_strip_p', 7); - - global $mdwp_hidden_tags, $mdwp_placeholders; - $mdwp_hidden_tags = explode(' ', - '

 
  • '); - $mdwp_placeholders = explode(' ', str_rot13( - 'pEj07ZbbBZ U1kqgh4w4p pre2zmeN6K QTi31t9pre ol0MP1jzJR '. - 'ML5IjmbRol ulANi1NsGY J7zRLJqPul liA8ctl16T K9nhooUHli')); - } - - function mdwp_add_p($text) { - if (!preg_match('{^$|^<(p|ul|ol|dl|pre|blockquote)>}i', $text)) { - $text = '

    '.$text.'

    '; - $text = preg_replace('{\n{2,}}', "

    \n\n

    ", $text); - } - return $text; - } - - function mdwp_strip_p($t) { return preg_replace('{}i', '', $t); } - - function mdwp_hide_tags($text) { - global $mdwp_hidden_tags, $mdwp_placeholders; - return str_replace($mdwp_hidden_tags, $mdwp_placeholders, $text); - } - function mdwp_show_tags($text) { - global $mdwp_hidden_tags, $mdwp_placeholders; - return str_replace($mdwp_placeholders, $mdwp_hidden_tags, $text); - } -} - - -### bBlog Plugin Info ### - -function identify_modifier_markdown() { - return array( - 'name' => 'markdown', - 'type' => 'modifier', - 'nicename' => 'PHP Markdown Extra', - 'description' => 'A text-to-HTML conversion tool for web writers', - 'authors' => 'Michel Fortin and John Gruber', - 'licence' => 'GPL', - 'version' => MARKDOWNEXTRA_VERSION, - 'help' => 'Markdown syntax allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by John Gruber. More...', - ); -} - - -### Smarty Modifier Interface ### - -function smarty_modifier_markdown($text) { - return Markdown($text); -} - - -### Textile Compatibility Mode ### - -# Rename this file to "classTextile.php" and it can replace Textile everywhere. - -if (strcasecmp(substr(__FILE__, -16), "classTextile.php") == 0) { - # Try to include PHP SmartyPants. Should be in the same directory. - @include_once 'smartypants.php'; - # Fake Textile class. It calls Markdown instead. - class Textile { - function TextileThis($text, $lite='', $encode='') { - if ($lite == '' && $encode == '') $text = Markdown($text); - if (function_exists('SmartyPants')) $text = SmartyPants($text); - return $text; - } - # Fake restricted version: restrictions are not supported for now. - function TextileRestricted($text, $lite='', $noimage='') { - return $this->TextileThis($text, $lite); - } - # Workaround to ensure compatibility with TextPattern 4.0.3. - function blockLite($text) { return $text; } - } -} - - - -# -# Markdown Parser Class -# - -class Markdown_Parser { - - # Regex to match balanced [brackets]. - # Needed to insert a maximum bracked depth while converting to PHP. - var $nested_brackets_depth = 6; - var $nested_brackets_re; - - var $nested_url_parenthesis_depth = 4; - var $nested_url_parenthesis_re; - - # Table of hash values for escaped characters: - var $escape_chars = '\`*_{}[]()>#+-.!'; - var $escape_chars_re; - - # Change to ">" for HTML output. - var $empty_element_suffix = MARKDOWN_EMPTY_ELEMENT_SUFFIX; - var $tab_width = MARKDOWN_TAB_WIDTH; - - # Change to `true` to disallow markup or entities. - var $no_markup = false; - var $no_entities = false; - - # Predefined urls and titles for reference links and images. - var $predef_urls = array(); - var $predef_titles = array(); - - - function Markdown_Parser() { - # - # Constructor function. Initialize appropriate member variables. - # - $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).']'; - - # Sort document, block, and span gamut in ascendent priority order. - asort($this->document_gamut); - asort($this->block_gamut); - asort($this->span_gamut); - } - - - # Internal hashes used during transformation. - var $urls = array(); - var $titles = array(); - var $html_hashes = array(); - - # Status flag to avoid invalid nesting. - var $in_anchor = false; - - - function setup() { - # - # Called before the transformation process starts to setup parser - # states. - # - # Clear global hashes. - $this->urls = $this->predef_urls; - $this->titles = $this->predef_titles; - $this->html_hashes = array(); - - $in_anchor = false; - } - - function teardown() { - # - # Called after the transformation process to clear any variable - # which may be taking up memory unnecessarly. - # - $this->urls = array(); - $this->titles = array(); - $this->html_hashes = array(); - } - - - function transform($text) { - # - # Main function. Performs some preprocessing on the input text - # and pass it through the document gamut. - # - $this->setup(); - - # Remove UTF-8 BOM and marker character in input, if present. - $text = preg_replace('{^\xEF\xBB\xBF|\x1A}', '', $text); - - # Standardize line endings: - # DOS to Unix and Mac to Unix - $text = preg_replace('{\r\n?}', "\n", $text); - - # Make sure $text ends with a couple of newlines: - $text .= "\n\n"; - - # Convert all tabs to spaces. - $text = $this->detab($text); - - # Turn block-level HTML blocks into hash entries - $text = $this->hashHTMLBlocks($text); - - # Strip any lines consisting only of spaces and tabs. - # This makes subsequent regexen easier to write, because we can - # match consecutive blank lines with /\n+/ instead of something - # contorted like /[ ]*\n+/ . - $text = preg_replace('/^[ ]+$/m', '', $text); - - # Run document gamut methods. - foreach ($this->document_gamut as $method => $priority) { - $text = $this->$method($text); - } - - $this->teardown(); - - return $text . "\n"; - } - - var $document_gamut = array( - # Strip link definitions, store in hashes. - "stripLinkDefinitions" => 20, - - "runBasicBlockGamut" => 30, - ); - - - function stripLinkDefinitions($text) { - # - # Strips link definitions from text, stores the URLs and titles in - # hash references. - # - $less_than_tab = $this->tab_width - 1; - - # Link defs are in the form: ^[id]: url "optional title" - $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 ''; # String that will replace the block - } - - - function hashHTMLBlocks($text) { - if ($this->no_markup) return $text; - - $less_than_tab = $this->tab_width - 1; - - # Hashify HTML blocks: - # We only want to do this for block-level HTML tags, such as headers, - # lists, and tables. That's because we still want to wrap

    s around - # "paragraphs" that are wrapped in non-block-level tags, such as anchors, - # phrase emphasis, and spans. The list of tags we're looking for is - # hard-coded: - # - # * List "a" is made of tags which can be both inline or block-level. - # These will be treated block-level when the start tag is alone on - # its line, otherwise they're not matched here and will be taken as - # inline later. - # * List "b" is made of tags which are always block-level; - # - $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'; - - # Regular expression for the content of a block tag. - $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). # end of opening tag - '.*?'. # last level nested tag content - str_repeat(' - # closing nested tag - ) - | - <(?!/\2\s*> # other tags with a different name - ) - )*', - $nested_tags_level); - $content2 = str_replace('\2', '\3', $content); - - # First, look for nested blocks, e.g.: - #

    - #
    - # tags for inner block must be indented. - #
    - #
    - # - # The outermost tags must start at the left margin for this to match, and - # the inner nested divs must be indented. - # We need to do this before the next, more liberal match, because the next - # match will start at the first `
    ` and stop at the first `
    `. - $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` to `\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 - # 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 - # the matching end tag - [ ]* # trailing spaces/tabs - (?=\n+|\Z) # followed by a newline or end of document - - | # Special case just for
    . 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 ( - ) - [ ]* - (?=\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') { - # - # Called whenever a tag must be hashed when a function insert an atomic - # element in the text stream. Passing $text to through this function gives - # a unique text-token which will be reverted back when calling unhash. - # - # The $boundary argument specify what character should be used to surround - # the token. By convension, "B" is used for block elements that needs not - # to be wrapped into paragraph tags at the end, ":" is used for elements - # that are word separators and "X" is used in the general case. - # - # Swap back any tag hash found in $text so we do not have to `unhash` - # multiple times at the end. - $text = $this->unhash($text); - - # Then hash the block. - static $i = 0; - $key = "$boundary\x1A" . ++$i . $boundary; - $this->html_hashes[$key] = $text; - return $key; # String that will replace the tag. - } - - - function hashBlock($text) { - # - # Shortcut function for hashPart with block-level boundaries. - # - return $this->hashPart($text, 'B'); - } - - - var $block_gamut = array( - # - # These are all the transformations that form block-level - # tags like paragraphs, headers, and list items. - # - "doHeaders" => 10, - "doHorizontalRules" => 20, - - "doLists" => 40, - "doCodeBlocks" => 50, - "doBlockQuotes" => 60, - ); - - function runBlockGamut($text) { - # - # Run block gamut tranformations. - # - # We need to escape raw HTML in Markdown source before doing anything - # else. This need to be done for each block, and not only at the - # begining in the Markdown function since hashed blocks can be part of - # list items and could have been indented. Indented blocks would have - # been seen as a code block in a previous pass of hashHTMLBlocks. - $text = $this->hashHTMLBlocks($text); - - return $this->runBasicBlockGamut($text); - } - - function runBasicBlockGamut($text) { - # - # Run block gamut tranformations, without hashing HTML blocks. This is - # useful when HTML blocks are known to be already hashed, like in the first - # whole-document pass. - # - foreach ($this->block_gamut as $method => $priority) { - $text = $this->$method($text); - } - - # Finally form paragraph and restore hashed blocks. - $text = $this->formParagraphs($text); - - return $text; - } - - - function doHorizontalRules($text) { - # Do Horizontal Rules: - 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("empty_element_suffix")."\n", - $text); - } - - - var $span_gamut = array( - # - # These are all the transformations that occur *within* block-level - # tags like paragraphs, headers, and list items. - # - # Process character escapes, code spans, and inline HTML - # in one shot. - "parseSpan" => -30, - - # Process anchor and image tags. Images must come first, - # because ![foo][f] looks like an anchor. - "doImages" => 10, - "doAnchors" => 20, - - # Make links out of things like `` - # Must come after doAnchors, because you can use < and > - # delimiters in inline links like [this](). - "doAutoLinks" => 30, - "encodeAmpsAndAngles" => 40, - - "doItalicsAndBold" => 50, - "doHardBreaks" => 60, - ); - - function runSpanGamut($text) { - # - # Run span gamut tranformations. - # - foreach ($this->span_gamut as $method => $priority) { - $text = $this->$method($text); - } - - return $text; - } - - - function doHardBreaks($text) { - # Do hard breaks: - return preg_replace_callback('/ {2,}\n/', - array(&$this, '_doHardBreaks_callback'), $text); - } - function _doHardBreaks_callback($matches) { - return $this->hashPart("empty_element_suffix\n"); - } - - - function doAnchors($text) { - # - # Turn Markdown link shortcuts into XHTML tags. - # - if ($this->in_anchor) return $text; - $this->in_anchor = true; - - # - # First, handle reference-style links: [link text] [id] - # - $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); - - # - # Next, inline-style links: [link text](url "optional title") - # - $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); - - # - # Last, handle reference-style shortcuts: [link text] - # These must come last in case you've also got [link text][1] - # or [link text](/foo) - # - $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 == "") { - # for shortcut links like [this][] or [this]. - $link_id = $link_text; - } - - # lower-case and turn embedded newlines into spaces - $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 = "titles[$link_id] ) ) { - $title = $this->titles[$link_id]; - $title = $this->encodeAttribute($title); - $result .= " title=\"$title\""; - } - - $link_text = $this->runSpanGamut($link_text); - $result .= ">$link_text"; - $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 = "encodeAttribute($title); - $result .= " title=\"$title\""; - } - - $link_text = $this->runSpanGamut($link_text); - $result .= ">$link_text"; - - return $this->hashPart($result); - } - - - function doImages($text) { - # - # Turn Markdown image shortcuts into tags. - # - # - # First, handle reference-style labeled images: ![alt text][id] - # - $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); - - # - # Next, handle inline images: ![alt text](url "optional title") - # Don't forget: encode * and _ - # - $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); # for shortcut links like ![this][]. - } - - $alt_text = $this->encodeAttribute($alt_text); - if (isset($this->urls[$link_id])) { - $url = $this->encodeAttribute($this->urls[$link_id]); - $result = "\"$alt_text\"";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 { - # If there's no such link ID, leave intact: - $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 = "\"$alt_text\"";encodeAttribute($title); - $result .= " title=\"$title\""; # $title already quoted - } - $result .= $this->empty_element_suffix; - - return $this->hashPart($result); - } - - - function doHeaders($text) { - # Setext-style headers: - # Header 1 - # ======== - # - # Header 2 - # -------- - # - $text = preg_replace_callback('{ ^(.+?)[ ]*\n(=+|-+)[ ]*\n+ }mx', - array(&$this, '_doHeaders_callback_setext'), $text); - - # atx-style headers: - # # Header 1 - # ## Header 2 - # ## Header 2 with closing hashes ## - # ... - # ###### Header 6 - # - $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) { - # Terrible hack to check we haven't found an empty list item. - if ($matches[2] == '-' && preg_match('{^-(?: |$)}', $matches[1])) - return $matches[0]; - - $level = $matches[2]{0} == '=' ? 1 : 2; - $block = "".$this->runSpanGamut($matches[1]).""; - return "\n" . $this->hashBlock($block) . "\n\n"; - } - function _doHeaders_callback_atx($matches) { - $level = strlen($matches[1]); - $block = "".$this->runSpanGamut($matches[2]).""; - return "\n" . $this->hashBlock($block) . "\n\n"; - } - - - function doLists($text) { - # - # Form HTML ordered (numbered) and unordered (bulleted) lists. - # - $less_than_tab = $this->tab_width - 1; - - # Re-usable patterns to match list item bullets and number markers: - $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) { - # Re-usable pattern to match any entirel ul or ol list: - $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.'[ ]+ - ) - ) - ) - '; // mx - - # We use a different prefix before nested lists than top-level lists. - # See extended comment in _ProcessListItems(). - - 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) { - # Re-usable patterns to match list item bullets and number markers: - $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 . ""); - return "\n". $result ."\n\n"; - } - - var $list_level = 0; - - function processListItems($list_str, $marker_any_re) { - # - # Process the contents of a single ordered or unordered list, splitting it - # into individual list items. - # - # The $this->list_level global keeps track of when we're inside a list. - # Each time we enter a list, we increment it; when we leave a list, - # we decrement. If it's zero, we're not in a list anymore. - # - # We do this because when we're not inside a list, we want to treat - # something like this: - # - # I recommend upgrading to version - # 8. Oops, now this line is treated - # as a sub-list. - # - # As a single paragraph, despite the fact that the second line starts - # with a digit-period-space sequence. - # - # Whereas when we're inside a list (or sub-list), that line will be - # treated as the start of a sub-list. What a kludge, huh? This is - # an aspect of Markdown's syntax that's hard to parse perfectly - # without resorting to mind-reading. Perhaps the solution is to - # change the syntax rules such that sub-lists must start with a - # starting cardinal number; e.g. "1." or "a.". - - $this->list_level++; - - # trim trailing blank lines: - $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)) - { - # Replace marker with the appropriate whitespace indentation - $item = $leading_space . str_repeat(' ', strlen($marker_space)) . $item; - $item = $this->runBlockGamut($this->outdent($item)."\n"); - } - else { - # Recursion for sub-lists: - $item = $this->doLists($this->outdent($item)); - $item = preg_replace('/\n+$/', '', $item); - $item = $this->runSpanGamut($item); - } - - return "
  • " . $item . "
  • \n"; - } - - - function doCodeBlocks($text) { - # - # Process Markdown `
    ` blocks.
    -  #
    -    $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);
    -
    -    # trim leading newlines and trailing newlines
    -    $codeblock = preg_replace('/\A\n+|\n+\z/', '', $codeblock);
    -
    -    $codeblock = "
    $codeblock\n
    "; - return "\n\n".$this->hashBlock($codeblock)."\n\n"; - } - - - function makeCodeSpan($code) { - # - # Create a code span markup for $code. Called from handleSpanToken. - # - $code = htmlspecialchars(trim($code), ENT_NOQUOTES); - return $this->hashPart("$code"); - } - - - var $em_relist = array( - '' => '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(?em_relist as $em => $em_re) { - foreach ($this->strong_relist as $strong => $strong_re) { - # Construct list of allowed token expressions. - $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; - - # Construct master expression from list. - $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) { - # - # Get prepared regular expression for seraching emphasis tokens - # in current context. - # - $token_re = $this->em_strong_prepared_relist["$em$strong"]; - - # - # Each loop iteration search for the next emphasis token. - # Each token is then passed to handleSpanToken. - # - $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)) { - # Reached end of text span: empty stack without emitting. - # any more emphasis. - 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) { - # Reached closing marker while inside a three-char emphasis. - if ($token_len == 3) { - # Three-char closing marker, close em and strong. - array_shift($token_stack); - $span = array_shift($text_stack); - $span = $this->runSpanGamut($span); - $span = "$span"; - $text_stack[0] .= $this->hashPart($span); - $em = ''; - $strong = ''; - } else { - # Other closing marker: close one em or strong and - # change current token state to match the other - $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"; - $text_stack[0] = $this->hashPart($span); - $$tag = ''; # $$tag stands for $em or $strong - } - $tree_char_em = false; - } else if ($token_len == 3) { - if ($em) { - # Reached closing marker for both em and strong. - # Closing strong marker: - 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"; - $text_stack[0] .= $this->hashPart($span); - $$tag = ''; # $$tag stands for $em or $strong - } - } else { - # Reached opening three-char emphasis marker. Push on token - # stack; will be handled by the special condition above. - $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) { - # Unwind any dangling emphasis marker: - if (strlen($token_stack[0]) == 1) { - $text_stack[1] .= array_shift($token_stack); - $text_stack[0] .= array_shift($text_stack); - } - # Closing strong marker: - array_shift($token_stack); - $span = array_shift($text_stack); - $span = $this->runSpanGamut($span); - $span = "$span"; - $text_stack[0] .= $this->hashPart($span); - $strong = ''; - } else { - array_unshift($token_stack, $token); - array_unshift($text_stack, ''); - $strong = $token; - } - } else { - # Here $token_len == 1 - if ($em) { - if (strlen($token_stack[0]) == 1) { - # Closing emphasis marker: - array_shift($token_stack); - $span = array_shift($text_stack); - $span = $this->runSpanGamut($span); - $span = "$span"; - $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]; - # trim one level of quoting - trim whitespace-only lines - $bq = preg_replace('/^[ ]*>[ ]?|^[ ]+$/m', '', $bq); - $bq = $this->runBlockGamut($bq); # recurse - - $bq = preg_replace('/^/m', " ", $bq); - # These leading spaces cause problem with
     content, 
    -    # so we need to fix that:
    -    $bq = preg_replace_callback('{(\s*
    .+?
    )}sx', - array(&$this, '_doBlockQuotes_callback2'), $bq); - - return "\n". $this->hashBlock("
    \n$bq\n
    ")."\n\n"; - } - function _doBlockQuotes_callback2($matches) { - $pre = $matches[1]; - $pre = preg_replace('/^ /m', '', $pre); - return $pre; - } - - - function formParagraphs($text) { - # - # Params: - # $text - string to process with html

    tags - # - # Strip leading and trailing lines: - $text = preg_replace('/\A\n+|\n+\z/', '', $text); - - $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY); - - # - # Wrap

    tags and unhashify HTML blocks - # - foreach ($grafs as $key => $value) { - if (!preg_match('/^B\x1A[0-9]+B$/', $value)) { - # Is a paragraph. - $value = $this->runSpanGamut($value); - $value = preg_replace('/^([ ]*)/', "

    ", $value); - $value .= "

    "; - $grafs[$key] = $this->unhash($value); - } - else { - # Is a block. - # Modify elements of @grafs in-place... - $graf = $value; - $block = $this->html_hashes[$graf]; - $graf = $block; -// if (preg_match('{ -// \A -// ( # $1 =
    tag -//
    ]* -// \b -// markdown\s*=\s* ([\'"]) # $2 = attr quote char -// 1 -// \2 -// [^>]* -// > -// ) -// ( # $3 = contents -// .* -// ) -// (
    ) # $4 = closing tag -// \z -// }xs', $block, $matches)) -// { -// list(, $div_open, , $div_content, $div_close) = $matches; -// -// # We can't call Markdown(), because that resets the hash; -// # that initialization code should be pulled into its own sub, though. -// $div_content = $this->hashHTMLBlocks($div_content); -// -// # Run document gamut methods on the content. -// foreach ($this->document_gamut as $method => $priority) { -// $div_content = $this->$method($div_content); -// } -// -// $div_open = preg_replace( -// '{\smarkdown\s*=\s*([\'"]).+?\1}', '', $div_open); -// -// $graf = $div_open . "\n" . $div_content . "\n" . $div_close; -// } - $grafs[$key] = $graf; - } - } - - return implode("\n\n", $grafs); - } - - - function encodeAttribute($text) { - # - # Encode text for a double-quoted HTML attribute. This function - # is *not* suitable for attributes enclosed in single quotes. - # - $text = $this->encodeAmpsAndAngles($text); - $text = str_replace('"', '"', $text); - return $text; - } - - - function encodeAmpsAndAngles($text) { - # - # Smart processing for ampersands and angle brackets that need to - # be encoded. Valid character entities are left alone unless the - # no-entities mode is set. - # - if ($this->no_entities) { - $text = str_replace('&', '&', $text); - } else { - # Ampersand-encoding based entirely on Nat Irons's Amputator - # MT plugin: - $text = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/', - '&', $text);; - } - # Encode remaining <'s - $text = str_replace('<', '<', $text); - - return $text; - } - - - function doAutoLinks($text) { - $text = preg_replace_callback('{<((https?|ftp|dict):[^\'">\s]+)>}i', - array(&$this, '_doAutoLinks_url_callback'), $text); - - # Email addresses: - $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 = "$url"; - return $this->hashPart($link); - } - function _doAutoLinks_email_callback($matches) { - $address = $matches[1]; - $link = $this->encodeEmailAddress($address); - return $this->hashPart($link); - } - - - function encodeEmailAddress($addr) { - # - # Input: an email address, e.g. "foo@example.com" - # - # Output: the email address as a mailto link, with each character - # of the address encoded as either a decimal or hex entity, in - # the hopes of foiling most address harvesting spam bots. E.g.: - # - #

    foo@exampl - # e.com

    - # - # Based by a filter by Matthew Wickline, posted to BBEdit-Talk. - # With some optimizations by Milian Wolff. - # - $addr = "mailto:" . $addr; - $chars = preg_split('/(? $char) { - $ord = ord($char); - # Ignore non-ascii chars. - if ($ord < 128) { - $r = ($seed * (1 + $key)) % 100; # Pseudo-random function. - # roughly 10% raw, 45% hex, 45% dec - # '@' *must* be encoded. I insist. - if ($r > 90 && $char != '@') /* do nothing */; - else if ($r < 45) $chars[$key] = '&#x'.dechex($ord).';'; - else $chars[$key] = '&#'.$ord.';'; - } - } - - $addr = implode('', $chars); - $text = implode('', array_slice($chars, 7)); # text without `mailto:` - $addr = "$text"; - - return $addr; - } - - - function parseSpan($str) { - # - # Take the string $str and parse it into tokens, hashing embeded HTML, - # escaped characters and handling code spans. - # - $output = ''; - - $span_re = '{ - ( - \\\\'.$this->escape_chars_re.' - | - (?no_markup ? '' : ' - | - # comment - | - <\?.*?\?> | <%.*?%> # processing instruction - | - <[/!$]?[-a-zA-Z0-9:_]+ # regular tags - (?> - \s - (?>[^"\'>]+|"[^"]*"|\'[^\']*\')* - )? - > - ').' - ) - }xs'; - - while (1) { - # - # Each loop iteration seach for either the next tag, the next - # openning code span marker, or the next escaped character. - # Each token is then passed to handleSpanToken. - # - $parts = preg_split($span_re, $str, 2, PREG_SPLIT_DELIM_CAPTURE); - - # Create token from text preceding tag. - if ($parts[0] != "") { - $output .= $parts[0]; - } - - # Check if we reach the end. - if (isset($parts[1])) { - $output .= $this->handleSpanToken($parts[1], $parts[2]); - $str = $parts[2]; - } - else { - break; - } - } - - return $output; - } - - - function handleSpanToken($token, &$str) { - # - # Handle $token provided by parseSpan by determining its nature and - # returning the corresponding value that should replace it. - # - switch ($token{0}) { - case "\\": - return $this->hashPart("&#". ord($token{1}). ";"); - case "`": - # Search for end marker in remaining text. - if (preg_match('/^(.*?[^`])'.preg_quote($token).'(?!`)(.*)$/sm', - $str, $matches)) - { - $str = $matches[2]; - $codespan = $this->makeCodeSpan($matches[1]); - return $this->hashPart($codespan); - } - return $token; // return as text since no ending marker found. - default: - return $this->hashPart($token); - } - } - - - function outdent($text) { - # - # Remove one level of line-leading tabs or spaces - # - return preg_replace('/^(\t|[ ]{1,'.$this->tab_width.'})/m', '', $text); - } - - - # String length function for detab. `_initDetab` will create a function to - # hanlde UTF-8 if the default function does not exist. - var $utf8_strlen = 'mb_strlen'; - - function detab($text) { - # - # Replace tabs with the appropriate amount of space. - # - # For each line we separate the line in blocks delemited by - # tab characters. Then we reconstruct every line by adding the - # appropriate number of space between each blocks. - - $text = preg_replace_callback('/^.*\t.*$/m', - array(&$this, '_detab_callback'), $text); - - return $text; - } - function _detab_callback($matches) { - $line = $matches[0]; - $strlen = $this->utf8_strlen; # strlen function for UTF-8. - - # Split in blocks. - $blocks = explode("\t", $line); - # Add each blocks to the line. - $line = $blocks[0]; - unset($blocks[0]); # Do not add first block twice. - foreach ($blocks as $block) { - # Calculate amount of space, insert spaces, insert block. - $amount = $this->tab_width - - $strlen($line, 'UTF-8') % $this->tab_width; - $line .= str_repeat(" ", $amount) . $block; - } - return $line; - } - function _initDetab() { - # - # Check for the availability of the function in the `utf8_strlen` property - # (initially `mb_strlen`). If the function is not available, create a - # function that will loosely count the number of UTF-8 characters with a - # regular expression. - # - 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) { - # - # Swap back in all the tags hashed by _HashHTMLBlocks. - # - return preg_replace_callback('/(.)\x1A[0-9]+\1/', - array(&$this, '_unhash_callback'), $text); - } - function _unhash_callback($matches) { - return $this->html_hashes[$matches[0]]; - } - -} - - -# -# Markdown Extra Parser Class -# - -class MarkdownExtra_Parser extends Markdown_Parser { - - # Prefix for footnote ids. - var $fn_id_prefix = ""; - - # Optional title attribute for footnote links and backlinks. - var $fn_link_title = MARKDOWN_FN_LINK_TITLE; - var $fn_backlink_title = MARKDOWN_FN_BACKLINK_TITLE; - - # Optional class attribute for footnote links and backlinks. - var $fn_link_class = MARKDOWN_FN_LINK_CLASS; - var $fn_backlink_class = MARKDOWN_FN_BACKLINK_CLASS; - - # Predefined abbreviations. - var $predef_abbr = array(); - - - function MarkdownExtra_Parser() { - # - # Constructor function. Initialize the parser object. - # - # Add extra escapable characters before parent constructor - # initialize the table. - $this->escape_chars .= ':|'; - - # Insert extra document, block, and span transformations. - # Parent constructor will do the sorting. - $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(); - } - - - # Extra variables used during extra transformations. - var $footnotes = array(); - var $footnotes_ordered = array(); - var $abbr_desciptions = array(); - var $abbr_word_re = ''; - - # Give the current footnote number. - var $footnote_counter = 1; - - - function setup() { - # - # Setting up Extra-specific variables. - # - 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() { - # - # Clearing Extra-specific variables. - # - $this->footnotes = array(); - $this->footnotes_ordered = array(); - $this->abbr_desciptions = array(); - $this->abbr_word_re = ''; - - parent::teardown(); - } - - - ### HTML Block Parser ### - - # Tags that are always treated as block tags: - var $block_tags_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend'; - - # Tags treated as block tags only if the opening tag is alone on it's line: - var $context_block_tags_re = 'script|noscript|math|ins|del'; - - # Tags where markdown="1" default to span mode: - var $contain_span_tags_re = 'p|h[1-6]|li|dd|dt|td|th|legend|address'; - - # Tags which must not have their contents modified, no matter where - # they appear: - var $clean_tags_re = 'script|math'; - - # Tags that do not need to be closed. - var $auto_close_tags_re = 'hr|img'; - - - function hashHTMLBlocks($text) { - # - # Hashify HTML Blocks and "clean tags". - # - # We only want to do this for block-level HTML tags, such as headers, - # lists, and tables. That's because we still want to wrap

    s around - # "paragraphs" that are wrapped in non-block-level tags, such as anchors, - # phrase emphasis, and spans. The list of tags we're looking for is - # hard-coded. - # - # This works by calling _HashHTMLBlocks_InMarkdown, which then calls - # _HashHTMLBlocks_InHTML when it encounter block tags. When the markdown="1" - # attribute is found whitin a tag, _HashHTMLBlocks_InHTML calls back - # _HashHTMLBlocks_InMarkdown to handle the Markdown syntax within the tag. - # These two functions are calling each other. It's recursive! - # - # - # Call the HTML-in-Markdown hasher. - # - list($text, ) = $this->_hashHTMLBlocks_inMarkdown($text); - - return $text; - } - function _hashHTMLBlocks_inMarkdown($text, $indent = 0, - $enclosing_tag_re = '', $span = false) - { - # - # Parse markdown text, calling _HashHTMLBlocks_InHTML for block tags. - # - # * $indent is the number of space to be ignored when checking for code - # blocks. This is important because if we don't take the indent into - # account, something like this (which looks right) won't work as expected: - # - #

    - #
    - # Hello World. <-- Is this a Markdown code block or text? - #
    <-- Is this a Markdown code block or a real tag? - #
    - # - # If you don't like this, just don't indent the tag on which - # you apply the markdown="1" attribute. - # - # * If $enclosing_tag_re is not empty, stops at the first unmatched closing - # tag with that name. Nested tags supported. - # - # * If $span is true, text inside must treated as span. So any double - # newline will be replaced by a single newline so that it does not create - # paragraphs. - # - # Returns an array of that form: ( processed text , remaining text ) - # - if ($text === '') return array('', ''); - - # Regex to check for the presense of newlines around a block tag. - $newline_before_re = '/(?:^\n?|\n\n)*$/'; - $newline_after_re = - '{ - ^ # Start of text following the tag. - (?>[ ]*)? # Optional comment. - [ ]*\n # Must be followed by newline. - }xs'; - - # Regex to match any tag. - $block_tag_re = - '{ - ( # $2: Capture hole 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 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 ) - [ ]{0,'.($indent).'}~~~+[ ]*\n - ' : '' ). ' # End (if not is span). - ) - }xs'; - - - $depth = 0; # Current depth inside the tag tree. - $parsed = ""; # Parsed text that will be returned. - - # - # Loop through every tag until we find the closing tag of the parent - # or loop until reaching the end of text if no parent tag specified. - # - do { - # - # Split the text using the first $tag_match pattern found. - # Text before pattern will be first in the array, text after - # pattern will be at the end, and between will be any catches made - # by the pattern. - # - $parts = preg_split($block_tag_re, $text, 2, - PREG_SPLIT_DELIM_CAPTURE); - - # If in Markdown span mode, add a empty-string span-level hash - # after each newline to prevent triggering any block element. - if ($span) { - $void = $this->hashPart("", ':'); - $newline = "$void\n"; - $parts[0] = $void . str_replace("\n", $newline, $parts[0]) . $void; - } - - $parsed .= $parts[0]; # Text before current tag. - - # If end of $text has been reached. Stop loop. - if (count($parts) < 3) { - $text = ""; - break; - } - - $tag = $parts[1]; # Tag to handle. - $text = $parts[2]; # Remaining text after current tag. - $tag_re = preg_quote($tag); # For use in a regular expression. - - # - # Check for: Code span marker - # - if ($tag{0} == "`") { - # Find corresponding end marker. - $tag_re = preg_quote($tag); - if (preg_match('{^(?>.+?|\n(?!\n))*?(?.*\n)+?[ ]{0,'.($indent).'}'.$tag_re.'[ ]*\n}', $text, - $matches)) - { - # End marker found: pass text unchanged until marker. - $parsed .= $tag . $matches[0]; - $text = substr($text, strlen($matches[0])); - } - else { - # No end marker: just skip it. - $parsed .= $tag; - } - } - # - # Check for: Indented code block. - # - else if ($tag{0} == "\n" || $tag{0} == " ") { - # Indented code block: pass it unchanged, will be handled - # later. - $parsed .= $tag; - } - # - # Check for: Opening Block level tag or - # Opening Context Block tag (like ins and del) - # used as a block tag (tag is alone on it's line). - # - 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) ) - ) - { - # Need to parse tag and following text using the HTML parser. - list($block_text, $text) = - $this->_hashHTMLBlocks_inHTML($tag . $text, "hashBlock", true); - - # Make sure it stays outside of any paragraph by adding newlines. - $parsed .= "\n\n$block_text\n\n"; - } - # - # Check for: Clean tag (like script, math) - # HTML Comments, processing instructions. - # - else if (preg_match('{^<(?:'.$this->clean_tags_re.')\b}', $tag) || - $tag{1} == '!' || $tag{1} == '?') - { - # Need to parse tag and following text using the HTML parser. - # (don't check for markdown attribute) - list($block_text, $text) = - $this->_hashHTMLBlocks_inHTML($tag . $text, "hashClean", false); - - $parsed .= $block_text; - } - # - # Check for: Tag with same name as enclosing tag. - # - else if ($enclosing_tag_re !== '' && - # Same name as enclosing tag. - preg_match('{^= 0); - - return array($parsed, $text); - } - function _hashHTMLBlocks_inHTML($text, $hash_method, $md_attr) { - # - # Parse HTML, calling _HashHTMLBlocks_InMarkdown for block tags. - # - # * Calls $hash_method to convert any blocks. - # * Stops when the first opening tag closes. - # * $md_attr indicate if the use of the `markdown="1"` attribute is allowed. - # (it is not inside clean tags) - # - # Returns an array of that form: ( processed text , remaining text ) - # - if ($text === '') return array('', ''); - - # Regex to match `markdown` attribute inside of a tag. - $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'; - - # Regex to match any tag. - $tag_re = '{ - ( # $2: Capture hole tag. - - ".*?" | # Double quotes (can contain `>`) - \'.*?\' | # Single quotes (can contain `>`) - .+? # Anything but quotes and `>`. - )*? - )? - > # End of tag. - | - # HTML Comment - | - <\?.*?\?> | <%.*?%> # Processing instruction - | - # CData Block - ) - }xs'; - - $original_text = $text; # Save original text in case of faliure. - - $depth = 0; # Current depth inside the tag tree. - $block_text = ""; # Temporary text holder for current text. - $parsed = ""; # Parsed text that will be returned. - - # - # Get the name of the starting tag. - # (This pattern makes $base_tag_name_re safe without quoting.) - # - if (preg_match('/^<([\w:$]*)\b/', $text, $matches)) - $base_tag_name_re = $matches[1]; - - # - # Loop through every tag until we find the corresponding closing tag. - # - do { - # - # Split the text using the first $tag_match pattern found. - # Text before pattern will be first in the array, text after - # pattern will be at the end, and between will be any catches made - # by the pattern. - # - $parts = preg_split($tag_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE); - - if (count($parts) < 3) { - # - # End of $text reached with unbalenced tag(s). - # In that case, we return original text unchanged and pass the - # first character as filtered to prevent an infinite loop in the - # parent function. - # - return array($original_text{0}, substr($original_text, 1)); - } - - $block_text .= $parts[0]; # Text before current tag. - $tag = $parts[1]; # Tag to handle. - $text = $parts[2]; # Remaining text after current tag. - - # - # Check for: Auto-close tag (like
    ) - # Comments and Processing Instructions. - # - if (preg_match('{^auto_close_tags_re.')\b}', $tag) || - $tag{1} == '!' || $tag{1} == '?') - { - # Just add the tag to the block as if it was text. - $block_text .= $tag; - } - else { - # - # Increase/decrease nested tag count. Only do so if - # the tag's name match base tag's. - # - if (preg_match('{^mode = $attr_m[2] . $attr_m[3]; - $span_mode = $this->mode == 'span' || $this->mode != 'block' && - preg_match('{^<(?:'.$this->contain_span_tags_re.')\b}', $tag); - - # Calculate indent before tag. - if (preg_match('/(?:^|\n)( *?)(?! ).*?$/', $block_text, $matches)) { - $strlen = $this->utf8_strlen; - $indent = $strlen($matches[1], 'UTF-8'); - } else { - $indent = 0; - } - - # End preceding block with this tag. - $block_text .= $tag; - $parsed .= $this->$hash_method($block_text); - - # Get enclosing tag name for the ParseMarkdown function. - # (This pattern makes $tag_name_re safe without quoting.) - preg_match('/^<([\w:$]*)\b/', $tag, $matches); - $tag_name_re = $matches[1]; - - # Parse the content using the HTML-in-Markdown parser. - list ($block_text, $text) - = $this->_hashHTMLBlocks_inMarkdown($text, $indent, - $tag_name_re, $span_mode); - - # Outdent markdown text. - if ($indent > 0) { - $block_text = preg_replace("/^[ ]{1,$indent}/m", "", - $block_text); - } - - # Append tag content to parsed text. - if (!$span_mode) $parsed .= "\n\n$block_text\n\n"; - else $parsed .= "$block_text"; - - # Start over a new block. - $block_text = ""; - } - else $block_text .= $tag; - } - - } while ($depth > 0); - - # - # Hash last block text that wasn't processed inside the loop. - # - $parsed .= $this->$hash_method($block_text); - - return array($parsed, $text); - } - - - function hashClean($text) { - # - # Called whenever a tag must be hashed when a function insert a "clean" tag - # in $text, it pass through this function and is automaticaly escaped, - # blocking invalid nested overlap. - # - return $this->hashPart($text, 'C'); - } - - - function doHeaders($text) { - # - # Redefined to add id attribute support. - # - # Setext-style headers: - # Header 1 {#header1} - # ======== - # - # Header 2 {#header2} - # -------- - # - $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); - - # atx-style headers: - # # Header 1 {#header1} - # ## Header 2 {#header2} - # ## Header 2 with closing hashes ## {#header3} - # ... - # ###### Header 6 {#header2} - # - $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; - } - - private $_headerTags = array(); - - function _generateHeaderID($title) { - $base = trim(preg_replace('/[^a-z0-9]+/i', '-', strtolower(trim($title))), '-'); - $index = 1; - $id = $base; - while(array_key_exists($id, $this->_headerTags)) { - $id = $base . '-' . $index; - $index++; - } - $this->_headerTags[$id] = $title; - return $id; - } - - function _doHeaders_attr($attr, $title) { - if (empty($attr)) { - $id = $this->_generateHeaderID($title); - return " id=\"$id\""; - } else { - $this->_headerTags[$attr] = $title; - 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], $matches[1]); - $block = "".$this->runSpanGamut($matches[1]).""; - return "\n" . $this->hashBlock($block) . "\n\n"; - } - function _doHeaders_callback_atx($matches) { - $level = strlen($matches[1]); - $attr = $this->_doHeaders_attr($id =& $matches[3], $matches[2]); - $block = "".$this->runSpanGamut($matches[2]).""; - return "\n" . $this->hashBlock($block) . "\n\n"; - } - - - function doTables($text) { - # - # Form HTML tables. - # - $less_than_tab = $this->tab_width - 1; - # - # Find tables with leading pipe. - # - # | Header 1 | Header 2 - # | -------- | -------- - # | Cell 1 | Cell 2 - # | Cell 3 | Cell 4 - # - $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); - - # - # Find tables without leading pipe. - # - # Header 1 | Header 2 - # -------- | -------- - # Cell 1 | Cell 2 - # Cell 3 | Cell 4 - # - $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]; - - # Remove leading pipe for each row. - $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]; - - # Remove any tailing pipes for each line. - $head = preg_replace('/[|] *$/m', '', $head); - $underline = preg_replace('/[|] *$/m', '', $underline); - $content = preg_replace('/[|] *$/m', '', $content); - - # Reading alignement from header underline. - $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] = ''; - } - - # Parsing span elements, including code spans, character escapes, - # and inline HTML tags, so that pipes inside those gets ignored. - $head = $this->parseSpan($head); - $headers = preg_split('/ *[|] */', $head); - $col_count = count($headers); - - # Write column headers. - $text = "\n"; - $text .= "\n"; - $text .= "\n"; - foreach ($headers as $n => $header) - $text .= " ".$this->runSpanGamut(trim($header))."\n"; - $text .= "\n"; - $text .= "\n"; - - # Split content by row. - $rows = explode("\n", trim($content, "\n")); - - $text .= "\n"; - foreach ($rows as $row) { - # Parsing span elements, including code spans, character escapes, - # and inline HTML tags, so that pipes inside those gets ignored. - $row = $this->parseSpan($row); - - # Split row by cell. - $row_cells = preg_split('/ *[|] */', $row, $col_count); - $row_cells = array_pad($row_cells, $col_count, ''); - - $text .= "\n"; - foreach ($row_cells as $n => $cell) - $text .= " ".$this->runSpanGamut(trim($cell))."\n"; - $text .= "\n"; - } - $text .= "\n"; - $text .= "
    "; - - return $this->hashBlock($text) . "\n"; - } - - - function doDefLists($text) { - # - # Form HTML definition lists. - # - $less_than_tab = $this->tab_width - 1; - - # Re-usable pattern to match any entire dl list: - $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 - ) - ) - ) - )'; // mx - - $text = preg_replace_callback('{ - (?>\A\n?|(?<=\n\n)) - '.$whole_list_re.' - }mx', - array(&$this, '_doDefLists_callback'), $text); - - return $text; - } - function _doDefLists_callback($matches) { - # Re-usable patterns to match list item bullets and number markers: - $list = $matches[1]; - - # Turn double returns into triple returns, so that we can make a - # paragraph for the last item in a list, if necessary: - $result = trim($this->processDefListItems($list)); - $result = "
    \n" . $result . "\n
    "; - return $this->hashBlock($result) . "\n\n"; - } - - - function processDefListItems($list_str) { - # - # Process the contents of a single definition list, splitting it - # into individual term and definition list items. - # - $less_than_tab = $this->tab_width - 1; - - # trim trailing blank lines: - $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str); - - # Process definition terms. - $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); - - # Process actual definitions. - $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.'} [:][ ] | -
    | \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
    " . $term . "
    "; - } - 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)) { - # Replace marker with the appropriate whitespace indentation - $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
    " . $def . "
    \n"; - } - - - function doFencedCodeBlocks($text) { - # - # Adding the fenced code block syntax to regular Markdown: - # - # ~~~ - # Code block - # ~~~ - # - $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 = "
    $codeblock
    "; - return "\n\n".$this->hashBlock($codeblock)."\n\n"; - } - function _doFencedCodeBlocks_newlines($matches) { - return str_repeat("empty_element_suffix", - strlen($matches[0])); - } - - - # - # Redefining emphasis markers so that emphasis by underscore does not - # work in the middle of a word. - # - var $em_relist = array( - '' => '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? tags - # - # Strip leading and trailing lines: - $text = preg_replace('/\A\n+|\n+\z/', '', $text); - - $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY); - - # - # Wrap

    tags and unhashify HTML blocks - # - foreach ($grafs as $key => $value) { - $value = trim($this->runSpanGamut($value)); - - # Check if this should be enclosed in a paragraph. - # Clean tag hashes & block tag hashes are left alone. - $is_p = !preg_match('/^B\x1A[0-9]+B|^C\x1A[0-9]+C$/', $value); - - if ($is_p) { - $value = "

    $value

    "; - } - $grafs[$key] = $value; - } - - # Join grafs in one text, then unhash HTML tags. - $text = implode("\n\n", $grafs); - - # Finish by removing any tag hashes still present in $text. - $text = $this->unhash($text); - - return $text; - } - - - ### Footnotes - - function stripFootnotes($text) { - # - # Strips link definitions from text, stores the URLs and titles in - # hash references. - # - $less_than_tab = $this->tab_width - 1; - - # Link defs are in the form: [^id]: url "optional title" - $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 ''; # String that will replace the block - } - - - function doFootnotes($text) { - # - # Replace footnote references in $text [^id] with a special text-token - # which will be replaced by the actual footnote marker in appendFootnotes. - # - if (!$this->in_anchor) { - $text = preg_replace('{\[\^(.+?)\]}', "F\x1Afn:\\1\x1A:", $text); - } - return $text; - } - - - function appendFootnotes($text) { - # - # Append footnote list to text. - # - $text = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}', - array(&$this, '_appendFootnotes_callback'), $text); - - if (!empty($this->footnotes_ordered)) { - $text .= "\n\n"; - $text .= "
    \n"; - $text .= "empty_element_suffix ."\n"; - $text .= "
      \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"; # Need to append newline before parsing. - $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); - - # Add backlink to last paragraph; create new paragraph if needed. - $backlink = ""; - if (preg_match('{

      $}', $footnote)) { - $footnote = substr($footnote, 0, -4) . " $backlink

      "; - } else { - $footnote .= "\n\n

      $backlink

      "; - } - - $text .= "
    1. \n"; - $text .= $footnote . "\n"; - $text .= "
    2. \n\n"; - } - - $text .= "
    \n"; - $text .= "
    "; - } - return $text; - } - function _appendFootnotes_callback($matches) { - $node_id = $this->fn_id_prefix . $matches[1]; - - # Create footnote marker only if it has a corresponding footnote *and* - # the footnote hasn't been used by another marker. - if (isset($this->footnotes[$node_id])) { - # Transfert footnote content to the ordered list. - $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 - "". - "$num". - ""; - } - - return "[^".$matches[1]."]"; - } - - - ### Abbreviations ### - - function stripAbbreviations($text) { - # - # Strips abbreviations from text, stores titles in hash references. - # - $less_than_tab = $this->tab_width - 1; - - # Link defs are in the form: [id]*: url "optional title" - $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 ''; # String that will replace the block - } - - - function doAbbreviations($text) { - # - # Find defined abbreviations in text and wrap them in elements. - # - if ($this->abbr_word_re) { - // cannot use the /x modifier because abbr_word_re may - // contain significant spaces: - $text = preg_replace_callback('{'. - '(?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"); - } else { - $desc = $this->encodeAttribute($desc); - return $this->hashPart("$abbr"); - } - } else { - return $matches[0]; - } - } - -} - - -/* - -PHP Markdown Extra -================== - -Description ------------ - -This is a PHP port of the original Markdown formatter written in Perl -by John Gruber. This special "Extra" version of PHP Markdown features -further enhancements to the syntax for making additional constructs -such as tables and definition list. - -Markdown is a text-to-HTML filter; it translates an easy-to-read / -easy-to-write structured text format into HTML. Markdown's text format -is most similar to that of plain text email, and supports features such -as headers, *emphasis*, code blocks, blockquotes, and links. - -Markdown's syntax is designed not as a generic markup language, but -specifically to serve as a front-end to (X)HTML. You can use span-level -HTML tags anywhere in a Markdown document, and you can use block level -HTML tags (like
    and as well). - -For more information about Markdown's syntax, see: - - - - -Bugs ----- - -To file bug reports please send email to: - - - -Please include with your report: (1) the example input; (2) the output you -expected; (3) the output Markdown actually produced. - - -Version History ---------------- - -See the readme file for detailed release notes for this version. - - -Copyright and License ---------------------- - -PHP Markdown & Extra -Copyright (c) 2004-2009 Michel Fortin - -All rights reserved. - -Based on Markdown -Copyright (c) 2003-2006 John Gruber - -All rights reserved. - -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. - -* Neither the name "Markdown" nor the names of its contributors may - be used to endorse or promote products derived from this software - without specific prior written permission. - -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. - -*/ -?> \ No newline at end of file diff --git a/public/images/teacup-screenshot.png b/public/images/teacup-screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..7497f70d259987a551163159b31e49f3f29aab5a GIT binary patch literal 88606 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 - - - -## Creating a Micropub Endpoint - -After a client has obtained an access token and discovered the user's Micropub endpoint -it is ready to make requests to create posts. - -### The Request - -This is not intended to be a comprehensive guide to Micropub, and only includes the -fields that this client sends. - -The request to create a post will be sent with as a standard HTTP form-encoded request -The example code here is written in PHP but the idea is applicable in any language. - -The request will contain the following POST parameters: - -* `h=entry` - Indicates the type of object being created, in this case an h-entry. -* `content` - The text content the user entered, in this case the caption on the Instagram photo. -* `category` - A comma-separated list of tags that you entered -* `location` - A "geo" URI including the latitude and longitude of the photo if included. (Will look like `geo:37.786971,-122.399677;u=50`, where u=50 indicates the "uncertainty" of the location in meters) -* `in-reply-to` - If set, this is a URL that the post is in reply to - -The request will also contain an access token in the HTTP `Authorization` header: - -
    -Authorization: Bearer XXXXXXXX
    -
    - - -### Verifying Access Tokens - -Before you can begin processing the photo, you must first verify the access token is valid -and contains at least the "post" scope. - -How exactly you do this is dependent on your architecture. You can query the token endpoint -to check if an access token is still valid. See [https://tokens.indieauth.com/#verify tokens.indieauth.com] -for more information. - -Once you have looked up the token info, you need to make a determination -about whether that access token is still valid. You'll have the following information -at hand that can be used to check: - -* `me` - The user who this access token corresponds to. -* `client_id` - The app that generated the token. -* `scope` - The list of scopes that were authorized by the user. -* `issued_at` - The date the token was issued. - -Keep in mind that it may be possible for another user besides yourself to have created -an access token at your token endpoint, so the first thing you'll do when verifying -is making sure the "me" parameter matches your own domain. This way you are the only -one that can create posts on your website. - - -### Validating the Request Parameters - -A valid request to create a post will contain the parameters listed above. For now, -you can verify the presence of everything in the list, or you can try to genericize your -micropub endpoint so that it can also create [http://ownyourgram.com/creating-a-micropub-endpoint photo posts]. - -At a bare minimum, a Micropub request will contain the following: - -* `h=entry` -* `content` - -The access token must also contain at least the "post" scope. - - -### The Response - -Once you've validated the access token and checked for the presence of all required parameters, -you can create a post in your website with the information provided. - -If a post was successfully created, the endpoint must return an `HTTP 201` response with a -`Location` header that points to the URL of the post. No body is required for the response. - -
    -HTTP/1.1 201 Created
    -Location: http://example.com/post/100
    -
    - -If there was an error, the response should include an HTTP error code as appropriate, -and optionally an HTML or other body with more information. Below is a list of possible errors. - -* `HTTP 401 Unauthorized` - No access token was provided in the request. -* `HTTP 403 Forbidden` - An access token was provided, but the authenticated user does not have permission to complete the request. -* `HTTP 400 Bad Request` - Something was wrong with the request, such as a missing "h" parameter, or other missing data. The response body may contain more human-readable information about the error. - - - - - \ No newline at end of file diff --git a/views/docs.php b/views/docs.php index 92998bd..f4e93d2 100644 --- a/views/docs.php +++ b/views/docs.php @@ -4,32 +4,12 @@

    Introduction

    - +
    -

    This is a simple Micropub client for - creating text posts on your own website. To use it, you will need to turn your website - into an OAuth provider, and implement a Micropub endpoint that this app will send - requests to.

    - -

    Once you've signed in, you'll see an interface like the one shown which you can use to - write a post. Clicking "post" will make a Micropub request to your endpoint.

    - - - -

    Configuring Endpoints

    - -

    Authorization Endpoint

    -

    The authorization endpoint tells this app where to direct your browser to sign you in.

    - - -

    Token Endpoint

    -

    The token endpoint is where this app will make a request to get an access token after obtaining authorization.

    - - -

    Micropub Endpoint

    -

    The Micropub endpoint is the URL this app will use to post new photos.

    - +

    Teacup is a simple Micropub client for + posting food and drink to your website. Once you've set up Micropub on your site, + you can use this app to post the experimental food and drink posts to your site.

    The Creating a Micropub Endpoint tutorial will walk you through how to handle incoming POST requests from apps like this.