PageRenderTime 41ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/userguide/classes/kohana/kodoc/markdown.php

https://bitbucket.org/seyar/parshin.local
PHP | 264 lines | 120 code | 44 blank | 100 comment | 13 complexity | 95aa9bc636aed4f72548f2d056e241bd MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. /**
  3. * Custom Markdown parser for Kohana documentation.
  4. *
  5. * @package Kohana/Userguide
  6. * @category Base
  7. * @author Kohana Team
  8. * @copyright (c) 2009 Kohana Team
  9. * @license http://kohanaphp.com/license
  10. */
  11. class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
  12. /**
  13. * @var string base url for links
  14. */
  15. public static $base_url = '';
  16. /**
  17. * @var string base url for images
  18. */
  19. public static $image_url = '';
  20. /**
  21. * Currently defined heading ids.
  22. * Used to prevent creating multiple headings with same id.
  23. * @var array
  24. */
  25. protected $_heading_ids = array();
  26. /**
  27. * @var string the generated table of contents
  28. */
  29. protected static $_toc = "";
  30. /**
  31. * Slightly less terrible way to make it so the TOC only shows up when we
  32. * want it to. set this to true to show the toc.
  33. */
  34. public static $show_toc = false;
  35. public function __construct()
  36. {
  37. // doImage is 10, add image url just before
  38. $this->span_gamut['doImageURL'] = 9;
  39. // doLink is 20, add base url just before
  40. $this->span_gamut['doBaseURL'] = 19;
  41. // Add API links
  42. $this->span_gamut['doAPI'] = 90;
  43. // Add note spans last
  44. $this->span_gamut['doNotes'] = 100;
  45. // Parse Kohana view inclusions at the very end
  46. $this->document_gamut['doIncludeViews'] = 99;
  47. // Show table of contents for userguide pages
  48. $this->document_gamut['doTOC'] = 100;
  49. // PHP4 makes me sad.
  50. parent::MarkdownExtra_Parser();
  51. }
  52. /**
  53. * Callback for the heading setext style
  54. *
  55. * Heading 1
  56. * =========
  57. *
  58. * @param array Matches from regex call
  59. * @return string Generated html
  60. */
  61. function _doHeaders_callback_setext($matches)
  62. {
  63. if ($matches[3] == '-' && preg_match('{^- }', $matches[1]))
  64. return $matches[0];
  65. $level = $matches[3]{0} == '=' ? 1 : 2;
  66. $attr = $this->_doHeaders_attr($id =& $matches[2]);
  67. // Only auto-generate id if one doesn't exist
  68. if(empty($attr))
  69. $attr = ' id="'.$this->make_heading_id($matches[1]).'"';
  70. // Add this header to the page toc
  71. $this->_add_to_toc($level,$matches[1],$this->make_heading_id($matches[1]));
  72. $block = "<h$level$attr>".$this->runSpanGamut($matches[1])."</h$level>";
  73. return "\n" . $this->hashBlock($block) . "\n\n";
  74. }
  75. /**
  76. * Callback for the heading atx style
  77. *
  78. * # Heading 1
  79. *
  80. * @param array Matches from regex call
  81. * @return string Generated html
  82. */
  83. function _doHeaders_callback_atx($matches)
  84. {
  85. $level = strlen($matches[1]);
  86. $attr = $this->_doHeaders_attr($id =& $matches[3]);
  87. // Only auto-generate id if one doesn't exist
  88. if(empty($attr))
  89. $attr = ' id="'.$this->make_heading_id($matches[2]).'"';
  90. // Add this header to the page toc
  91. $this->_add_to_toc($level,$matches[2],$this->make_heading_id($matches[2]));
  92. $block = "<h$level$attr>".$this->runSpanGamut($matches[2])."</h$level>";
  93. return "\n" . $this->hashBlock($block) . "\n\n";
  94. }
  95. /**
  96. * Makes a heading id from the heading text
  97. * If any heading share the same name then subsequent headings will have an integer appended
  98. *
  99. * @param string The heading text
  100. * @return string ID for the heading
  101. */
  102. function make_heading_id($heading)
  103. {
  104. $id = url::title($heading, '-', TRUE);
  105. if(isset($this->_heading_ids[$id]))
  106. {
  107. $id .= '-';
  108. $count = 0;
  109. while (isset($this->_heading_ids[$id]) AND ++$count)
  110. {
  111. $id .= $count;
  112. }
  113. }
  114. return $id;
  115. }
  116. public function doIncludeViews($text)
  117. {
  118. if (preg_match_all('/{{([^\s{}]++)}}/', $text, $matches, PREG_SET_ORDER))
  119. {
  120. $replace = array();
  121. $replace = array();
  122. foreach ($matches as $set)
  123. {
  124. list($search, $view) = $set;
  125. try
  126. {
  127. $replace[$search] = View::factory($view)->render();
  128. }
  129. catch (Exception $e)
  130. {
  131. ob_start();
  132. // Capture the exception handler output and insert it instead
  133. Kohana::exception_handler($e);
  134. $replace[$search] = ob_get_clean();
  135. }
  136. }
  137. $text = strtr($text, $replace);
  138. }
  139. return $text;
  140. }
  141. /**
  142. * Add the current base url to all local links.
  143. *
  144. * [filesystem](about.filesystem "Optional title")
  145. *
  146. * @param string span text
  147. * @return string
  148. */
  149. public function doBaseURL($text)
  150. {
  151. // URLs containing "://" are left untouched
  152. return preg_replace('~(?<!!)(\[.+?\]\()(?!\w++://)(?!#)(\S*(?:\s*+".+?")?\))~', '$1'.Kodoc_Markdown::$base_url.'$2', $text);
  153. }
  154. /**
  155. * Add the current base url to all local images.
  156. *
  157. * ![Install Page](img/install.png "Optional title")
  158. *
  159. * @param string span text
  160. * @return string
  161. */
  162. public function doImageURL($text)
  163. {
  164. // URLs containing "://" are left untouched
  165. return preg_replace('~(!\[.+?\]\()(?!\w++://)(\S*(?:\s*+".+?")?\))~', '$1'.Kodoc_Markdown::$image_url.'$2', $text);
  166. }
  167. /**
  168. * Parses links to the API browser.
  169. *
  170. * [Class_Name], [Class::method] or [Class::$property]
  171. *
  172. * @param string span text
  173. * @return string
  174. */
  175. public function doAPI($text)
  176. {
  177. return preg_replace_callback('/\['.Kodoc::$regex_class_member.'\]/i', 'Kodoc::link_class_member', $text);
  178. }
  179. /**
  180. * Wrap notes in the applicable markup. Notes can contain single newlines.
  181. *
  182. * [!!] Remember the milk!
  183. *
  184. * @param string span text
  185. * @return string
  186. */
  187. public function doNotes($text)
  188. {
  189. if ( ! preg_match('/^\[!!\]\s*+(.+?)(?=\n{2,}|$)/s', $text, $match))
  190. {
  191. return $text;
  192. }
  193. return $this->hashBlock('<p class="note">'.$match[1].'</p>');
  194. }
  195. protected function _add_to_toc($level, $name, $id)
  196. {
  197. self::$_toc[] = array(
  198. 'level' => $level,
  199. 'name' => $name,
  200. 'id' => $id);
  201. }
  202. public function doTOC($text)
  203. {
  204. // Only add the toc do userguide pages, not api since they already have one
  205. if (self::$show_toc AND Route::name(Request::instance()->route) == "docs/guide")
  206. {
  207. $toc = View::factory('userguide/page-toc')
  208. ->set('array', self::$_toc)
  209. ->render()
  210. ;
  211. if (($offset = strpos($text, '<p>')) !== FALSE)
  212. {
  213. // Insert the page TOC just before the first <p>, which every
  214. // Markdown page should (will?) have.
  215. $text = substr_replace($text, $toc, $offset, 0);
  216. }
  217. }
  218. return $text;
  219. }
  220. } // End Kodoc_Markdown