123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- <?php
- /**
- * Custom Markdown parser for Kohana documentation.
- *
- * @package Kohana/Userguide
- * @category Base
- * @author Kohana Team
- * @copyright (c) Kohana Team
- * @license https://koseven.ga/LICENSE.md
- */
- class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
- /**
- * @var string base url for links
- */
- public static $base_url = '';
- /**
- * @var string base url for images
- */
- public static $image_url = '';
-
- /**
- * Currently defined heading ids.
- * Used to prevent creating multiple headings with same id.
- *
- * @var array
- */
- protected $_heading_ids = [];
-
- /**
- * @var array the generated table of contents
- */
- protected static $_toc = [];
-
- /**
- * Slightly less terrible way to make it so the TOC only shows up when we
- * want it to. set this to true to show the toc.
- */
- public static $show_toc = FALSE;
-
- /**
- * Transform some text using [Kodoc_Markdown]
- *
- * @see Markdown()
- *
- * @param string Text to parse
- * @return string Transformed text
- */
- public static function markdown($text)
- {
- static $instance;
- if ($instance === NULL)
- {
- $instance = new Kodoc_Markdown;
- }
- return $instance->transform($text);
- }
- public function __construct()
- {
- // doImage is 10, add image url just before
- $this->span_gamut['doImageURL'] = 9;
- // doLink is 20, add base url just before
- $this->span_gamut['doBaseURL'] = 19;
- // Add API links
- $this->span_gamut['doAPI'] = 90;
- // Add note spans last
- $this->span_gamut['doNotes'] = 100;
- // Parse Kohana view inclusions at the very end
- $this->document_gamut['doIncludeViews'] = 99;
- // Show table of contents for userguide pages
- $this->document_gamut['doTOC'] = 100;
- // Call parent constructor.
- parent::__construct();
- }
-
- /**
- * Callback for the heading setext style
- *
- * Heading 1
- * =========
- *
- * @param array Matches from regex call
- * @return string Generated html
- */
- function _doHeaders_callback_setext($matches)
- {
- if ($matches[3] == '-' AND preg_match('{^- }', $matches[1]))
- return $matches[0];
- $level = ($matches[3][0] == '=') ? 1 : 2;
- $attr = $this->_doHeaders_attr($id =& $matches[2]);
-
- // Only auto-generate id if one doesn't exist
- if (empty($attr))
- {
- $attr = ' id="'.$this->make_heading_id($matches[1]).'"';
- }
-
- // Add this header to the page toc
- $this->_add_to_toc($level,$matches[1],$this->make_heading_id($matches[1]));
-
- $block = "<h$level$attr>".$this->runSpanGamut($matches[1])."</h$level>";
- return "\n".$this->hashBlock($block)."\n\n";
- }
-
- /**
- * Callback for the heading atx style
- *
- * # Heading 1
- *
- * @param array Matches from regex call
- * @return string Generated html
- */
- function _doHeaders_callback_atx($matches)
- {
- $level = strlen($matches[1]);
- $attr = $this->_doHeaders_attr($id =& $matches[3]);
-
- // Only auto-generate id if one doesn't exist
- if (empty($attr))
- {
- $attr = ' id="'.$this->make_heading_id($matches[2]).'"';
- }
-
- // Add this header to the page toc
- $this->_add_to_toc($level, $matches[2], $this->make_heading_id(empty($matches[3]) ? $matches[2] : $matches[3]));
-
- $block = "<h$level$attr>".$this->runSpanGamut($matches[2])."</h$level>";
- return "\n".$this->hashBlock($block)."\n\n";
- }
-
- /**
- * Makes a heading id from the heading text
- * If any heading share the same name then subsequent headings will have an integer appended
- *
- * @param string The heading text
- * @return string ID for the heading
- */
- function make_heading_id($heading)
- {
- $id = url::title($heading, '-', TRUE);
-
- if (isset($this->_heading_ids[$id]))
- {
- $id .= '-';
-
- $count = 0;
-
- while (isset($this->_heading_ids[$id]) AND ++$count)
- {
- $id .= $count;
- }
- }
- return $id;
- }
- public function doIncludeViews($text)
- {
- if (preg_match_all('/{{([^\s{}]++)}}/', $text, $matches, PREG_SET_ORDER))
- {
- $replace = [];
- foreach ($matches as $set)
- {
- list($search, $view) = $set;
- if (Kohana::find_file('views', $view))
- {
- try
- {
- $replace[$search] = View::factory($view)->render();
- }
- catch (Exception $e)
- {
- /**
- * Capture the exception handler output and insert it instead.
- *
- * NOTE: Is this really the correct way to handle an exception?
- */
- $response = Kohana_exception::_handler($e);
- $replace[$search] = $response->body();
- }
- }
- }
- $text = strtr($text, $replace);
- }
- return $text;
- }
- /**
- * Add the current base url to all local links.
- *
- * [filesystem](about.filesystem "Optional title")
- *
- * @param string Span text
- * @return string
- */
- public function doBaseURL($text)
- {
- // URLs containing "://" are left untouched
- return preg_replace('~(?<!!)(\[.+?\]\()(?!\w++://)(?!#)(\S*(?:\s*+".+?")?\))~', '$1'.Kodoc_Markdown::$base_url.'$2', $text);
- }
- /**
- * Add the current base url to all local images.
- *
- * ![Install Page](img/install.png "Optional title")
- *
- * @param string Span text
- * @return string
- */
- public function doImageURL($text)
- {
- // URLs containing "://" are left untouched
- return preg_replace('~(!\[.+?\]\()(?!\w++://)(\S*(?:\s*+".+?")?\))~', '$1'.Kodoc_Markdown::$image_url.'$2', $text);
- }
- /**
- * Parses links to the API browser.
- *
- * [Class_Name], [Class::method] or [Class::$property]
- *
- * @param string Span text
- * @return string
- */
- public function doAPI($text)
- {
- return preg_replace_callback('/\['.Kodoc::$regex_class_member.'\]/i', 'Kodoc::link_class_member', $text);
- }
- /**
- * Wrap notes in the applicable markup. Notes can contain single newlines.
- *
- * [!!] Remember the milk!
- *
- * @param string Span text
- * @return string
- */
- public function doNotes($text)
- {
- if ( ! preg_match('/^\[!!\]\s*+(.+?)(?=\n{2,}|$)/s', $text, $match))
- {
- return $text;
- }
- return $this->hashBlock('<p class="note">'.$match[1].'</p>');
- }
-
- protected function _add_to_toc($level, $name, $id)
- {
- self::$_toc[] = [
- 'level' => $level,
- 'name' => $name,
- 'id' => $id];
- }
- public function doTOC($text)
- {
- // Only add the toc do userguide pages, not api since they already have one
- if (self::$show_toc AND Route::name(Request::current()->route()) == "docs/guide")
- {
- $toc = View::factory('userguide/page-toc')
- ->set('array', self::$_toc)
- ->render()
- ;
- if (($offset = strpos($text, '<p>')) !== FALSE)
- {
- // Insert the page TOC just before the first <p>, which every
- // Markdown page should (will?) have.
- $text = substr_replace($text, $toc, $offset, 0);
- }
- }
- return $text;
- }
- } // End Kodoc_Markdown
|