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

/parser/wp-mediawiki.php

https://bitbucket.org/sdvpartnership/questpc-framework
PHP | 719 lines | 489 code | 94 blank | 136 comment | 56 complexity | a4501046d86c20aab423bb350f47d095 MD5 | raw file
  1. <?php
  2. namespace QuestPC;
  3. /*
  4. Plugin Name: MediaWiki Markup for WordPress
  5. Plugin URI: http://zechs.dyndns.org/wordpress/?page_id=126
  6. Description: Add a subset of MediaWiki markups to WordPress
  7. Version: 0.0.8
  8. Author: Ming-Hsien Tsai
  9. Author URI: http://zechs.dyndns.org/wordpress/
  10. */
  11. /* Copyright 2006 Ming-Hsien Tsai (email : mhtsai208@gmail.com)
  12. This program is free software; you can redistribute it and/or modify
  13. it under the terms of the GNU General Public License as published by
  14. the Free Software Foundation; either version 2 of the License, or
  15. (at your option) any later version.
  16. This program is distributed in the hope that it will be useful,
  17. but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. GNU General Public License for more details.
  20. You should have received a copy of the GNU General Public License
  21. along with this program; if not, write to the Free Software
  22. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  23. */
  24. class WikiText {
  25. const LIST_TYPE_OL = "#";
  26. const LIST_TYPE_UL = "*";
  27. const LIST_TYPE_INDENT = ":";
  28. const LIST_TYPE_DEFINITION = ";";
  29. private $headings = array(); // all headings in the post
  30. private $id_suffix = array(); // the suffix to be appended after IDs of duplicate headings
  31. private $options = array(
  32. 'wpwiki_wiki_site' => null,
  33. 'wpwiki_page_title' => null,
  34. 'wpwiki_add_prefix_for_id' => true,
  35. 'wpwiki_toc_mode' => 'tag',
  36. 'wpwiki_toc_title' => 'Table of Content',
  37. # next setting enables <a> and <img> in <nowiki> and <pre>
  38. 'wpwiki_url_backward' => true,
  39. # use string value to override default "<hr />"
  40. 'wpwiki_hr_tag' => false
  41. ); // options
  42. private $auto_number = 0; // the counter used in anonymous external links
  43. private $table_level = 0;
  44. private $pagenum = 1; // the page number of a multipaged post
  45. private $list_stack = array();
  46. private $rmap = array();
  47. private $regex = array ( // the regular expressions of all rules
  48. 'newline' => array ('/(\r\n|\r)/', 'newline_callback'),
  49. 'encode' => array ('/.*/sm', 'encode'),
  50. 'horizontal' => array ('/^----$/m', 'horizontal_callback'),
  51. 'headings' => array ('/^(={1,6})(.*?)\1(?:\s|$)$/m', 'heading_callback'),
  52. 'lists' => array ('/\n((?:\*|#|\;|\:)+.*?\n(?!(?:\*|#|\;|\:)+))/s', 'list_callback'),
  53. 'preformatted' => array ('/((\n .*)+)/', 'preformatted_callback'),
  54. 'tables' => array ('#^\{\|(.*?)(?:^\|\+(.*?))?(^(?:((?R))|.)*?)^\|}#msi', 'table_callback'),
  55. 'external_links' => array ("/(\[)?((http\:\/\/|https\:\/\/|ftp\:\/\/|gopher\:\/\/|news\:\/\/)[\w|\d|\.|_|\-]+[A-Za-z0-9\/?=&%~_\-\.\:#;',]*)(?(1)([ ]+[^\]]+)?\])/i", 'url_callback'),
  56. 'email' => array ('/(\[)?mailto:([\w|\d|\.|_|\-]+@[\w|\d|\.|_|-]+)(?(1)\])/i', 'email_callback'),
  57. 'wikilinks' => array ('/\[{2}([^\||^\]|^\[]+)(?:\|([^\||^\[|^\]]+))?\]{2}/', 'wikilink_callback'),
  58. 'emphasis' => array ("/(?<!')'('{1,4})(.*?)\\1'(?!')/", 'emphasis_callback'),
  59. // 'paragraph' => array ("/^(.*)\n\n+/Ums", 'paragraph_callback'),
  60. 'decode' => array ('/.*/sm', 'decode'),
  61. 'raw' => array ('/<pre>(.*)<\/pre>/Ums', 'raw_callback'),
  62. 'nowiki' => array ('/<nowiki>|<\/nowiki>/i', 'nowiki_callback')
  63. );
  64. /**
  65. * The constructor
  66. */
  67. public function WikiText( array $options ) {
  68. $this->options = array_merge( $this->options, $options );
  69. if ( !is_string( $this->options['wpwiki_page_title'] ) ) {
  70. SdvException::throwError( '\'wpwiki_page_title\' option must be initialized as current page name', __METHOD__, $this->options );
  71. }
  72. if ( !is_string( $this->options['wpwiki_wiki_site'] ) ) {
  73. SdvException::throwError( '\'wpwiki_wiki_site\' option must be initialized to SERVER_NAME', __METHOD__, $this->options );
  74. }
  75. // calculate rmap
  76. $this->rmap = array (
  77. '[' => md5('['), ']' => md5(']'),
  78. '<' => md5('<'), '>' => md5('>'),
  79. ':' => md5(':'), '/' => md5('/'),
  80. '=' => md5('='), '*' => md5('*'),
  81. '#' => md5('#'), ';' => md5(';'),
  82. "'" => md5("'"), '|' => md5('|'),
  83. '!' => md5('!'), '-' => md5('-'),
  84. "\n" => md5("\n"), ' ' => md5(' ')
  85. );
  86. }
  87. protected function get_permalink( $pagename ) {
  88. return '//' . $this->options['wpwiki_wiki_site'] . '/blog/' . Gl::hsc( $pagename );
  89. }
  90. /**
  91. * Translate MediaWiki markups
  92. */
  93. public function transform($ret) {
  94. // process rules
  95. foreach ( $this->regex as $rule ) {
  96. list( $pattern, $callback ) = $rule;
  97. $ret = preg_replace_callback($pattern, array($this, $callback), $ret);
  98. Dbg\log(__METHOD__.':callback',$callback);
  99. Dbg\log(__METHOD__.':ret',$ret);
  100. }
  101. $ret = $this->generate_toc().$ret;
  102. return $ret;
  103. }
  104. /**
  105. * Remove the leading char of a string
  106. */
  107. private function remove_leading_char($str) {
  108. return substr($str, 1);
  109. }
  110. /**
  111. * Generate table of content
  112. */
  113. private function generate_toc() {
  114. if (empty($this->headings))
  115. return;
  116. $ret = "<div>
  117. <table id='_toc' class='toc' summary='toc'>
  118. <tr><td id='_tochead'><span id='_toctitle'>".$this->options['wpwiki_toc_title']."</span> [<a href='javascript:toggle_toc()'><span id='_toctoggle'>Hide</span></a>]</td></tr>
  119. <tr><td><div id='_toclist' class='toclist'>";
  120. $min = $this->headings[0]['level'];
  121. $level = array();
  122. $prev = 0;
  123. foreach ($this->headings as $k => $h) {
  124. $depth = $h['level'] - $min + 1;
  125. $depth = $depth < 1 ? 1 : $depth;
  126. if ($depth > $prev) { // add one level
  127. $toclevel = count($level) + 1;
  128. $ret .= "<ul>\n<li class='toclevel-$toclevel'>";
  129. $open = true;
  130. array_push($level, 1);
  131. } else if ($depth == $prev || $depth >= count($level)) { // no change
  132. $toclevel = count($level);
  133. $ret .= "</li>\n<li class='toclevel-$toclevel'>";
  134. $level[count($level) - 1] = ++$level[count($level) - 1];
  135. } else {
  136. $toclevel = $depth;
  137. while(count($level) > $depth) {
  138. $ret .= "</li>\n</ul>";
  139. array_pop($level);
  140. }
  141. $level[count($level) - 1] = ++$level[count($level) - 1];
  142. $ret .= "</li>\n<li class='toclevel-$toclevel'>";
  143. }
  144. $prev = $depth;
  145. $ret .= "<a href='".$h['link']."'><span class='tocnumber'>".implode('.', $level)."</span> <span class='toctext'>".$h['text']."</span></a>";
  146. }
  147. // close left
  148. while(count($level) > 0) {
  149. $ret .= "</li></ul>";
  150. array_pop($level);
  151. }
  152. $ret .= "</div></td></tr></table></div>\n";
  153. return $ret;
  154. }
  155. /**
  156. * Convert illegal chars in an ID
  157. */
  158. private function sanitize_id($id) {
  159. $ret = str_replace(' ', '_', $id);
  160. $ret = str_replace('%', '.', rawurlencode($ret));
  161. $ret = $this->options['wpwiki_add_prefix_for_id'] ? '_'.$ret : $ret;
  162. return $ret;
  163. }
  164. /**
  165. * Return the open tag of a list
  166. */
  167. private function open_list($type) {
  168. $ret = "";
  169. switch ($type) {
  170. case self::LIST_TYPE_UL:
  171. $ret = "<ul>";
  172. break;
  173. case self::LIST_TYPE_OL:
  174. $ret = "<ol>";
  175. break;
  176. case self::LIST_TYPE_DEFINITION:
  177. case self::LIST_TYPE_INDENT:
  178. $ret = "<dl>";
  179. break;
  180. }
  181. return $ret;
  182. }
  183. /**
  184. * Return the closing tag of a list
  185. */
  186. private function close_list($type) {
  187. $ret = "";
  188. switch ($type) {
  189. case self::LIST_TYPE_UL:
  190. $ret = "</ul>";
  191. break;
  192. case self::LIST_TYPE_OL:
  193. $ret = "</ol>";
  194. break;
  195. case self::LIST_TYPE_DEFINITION:
  196. case self::LIST_TYPE_INDENT:
  197. $ret = "</dl>";
  198. break;
  199. }
  200. return $ret;
  201. }
  202. /**
  203. * Return the open tag for list item
  204. */
  205. private function open_list_item($type) {
  206. $ret = "";
  207. switch ($type) {
  208. case self::LIST_TYPE_UL:
  209. case self::LIST_TYPE_OL:
  210. $ret = "<li>";
  211. break;
  212. case self::LIST_TYPE_DEFINITION:
  213. $ret = "<dt>";
  214. break;
  215. case self::LIST_TYPE_INDENT:
  216. $ret = "<dd>";
  217. break;
  218. }
  219. return $ret;
  220. }
  221. /**
  222. * Return the closing tag for list item
  223. */
  224. private function close_list_item($type) {
  225. $ret = "";
  226. switch ($type) {
  227. case self::LIST_TYPE_UL:
  228. case self::LIST_TYPE_OL:
  229. $ret = "\n</li>";
  230. break;
  231. case self::LIST_TYPE_DEFINITION:
  232. $ret = "\n</dt>";
  233. break;
  234. case self::LIST_TYPE_INDENT:
  235. $ret = "\n</dd>";
  236. break;
  237. }
  238. return $ret;
  239. }
  240. /**
  241. * Check whether the type of two lists are the same
  242. */
  243. private function list_type_eq($t1, $t2) {
  244. $ret = false;
  245. switch ($t1.$t2) {
  246. case self::LIST_TYPE_UL.self::LIST_TYPE_UL:
  247. case self::LIST_TYPE_OL.self::LIST_TYPE_OL:
  248. case self::LIST_TYPE_DEFINITION.self::LIST_TYPE_DEFINITION:
  249. case self::LIST_TYPE_DEFINITION.self::LIST_TYPE_INDENT:
  250. case self::LIST_TYPE_INDENT.self::LIST_TYPE_INDENT:
  251. case self::LIST_TYPE_INDENT.self::LIST_TYPE_DEFINITION:
  252. $ret = true;
  253. break;
  254. }
  255. return $ret;
  256. }
  257. /**
  258. * Encode special chars in <pre>, <nowiki>, <a> and <img>
  259. */
  260. private function encode($matches) {
  261. if ($this->options['wpwiki_url_backward'])
  262. $pattern = '/<(pre|nowiki)>.*<\/\1>|<a\s+[^>]*>(.*)<\/a>|<img\s+[^>]*\/>/Ums';
  263. else
  264. $pattern = '/<(pre|nowiki)>.*<\/\1>/Ums';
  265. return preg_replace_callback($pattern, array($this, 'encode_callback'), $matches[0]);
  266. }
  267. /**
  268. * Decode special chars in <pre>, <nowiki>, <a> and <img>
  269. */
  270. private function decode($matches) {
  271. $pattern = '/'.md5('[').'(.*)'.md5(']').'/Ums';
  272. return preg_replace_callback($pattern, array($this, 'decode_callback'), $matches[0]);
  273. }
  274. /**
  275. * The callback function for encode
  276. */
  277. private function encode_callback($matches) {
  278. $ret = str_replace(array('%', '-'), array(md5('%'), md5('-')), $matches[0]);
  279. $ret = rawurlencode($ret);
  280. $ret = md5('[').$ret.md5(']');
  281. if ($matches[1] == 'pre')
  282. $ret = "\n".$ret;
  283. return $ret;
  284. }
  285. /**
  286. * The callback function for decode
  287. */
  288. private function decode_callback($matches) {
  289. $ret = rawurldecode($matches[1]);
  290. $ret = str_replace(array(md5('%'), md5('-')), array('%', '-'), $ret);
  291. return $ret;
  292. }
  293. /**
  294. * Replace all continuous newlines to one "\n"
  295. */
  296. private function newline_callback($matches) {
  297. return "\n";
  298. }
  299. /**
  300. * Convert HTML special cahrs in <pre>
  301. */
  302. function raw_callback($matches) {
  303. $text = htmlspecialchars(htmlspecialchars_decode($matches[1]));
  304. $ret = "<pre>$text</pre>";
  305. return $ret;
  306. }
  307. /**
  308. * The callback function for horizontal line
  309. */
  310. private function horizontal_callback($matches) {
  311. $ret = $this->options['wpwiki_hr_tag'];
  312. $ret || $ret = '<hr/>';
  313. return $ret;
  314. }
  315. protected function is_single() {
  316. return true;
  317. }
  318. protected function is_page() {
  319. return true;
  320. }
  321. /**
  322. * The callback function for headings
  323. */
  324. private function heading_callback($matches) {
  325. $level = strlen($matches[1]);
  326. $text = trim($matches[2]);
  327. if ($this->is_single() || $this->is_page()) {
  328. $suffix = '';
  329. if (array_key_exists($text, $this->id_suffix)) {
  330. $this->id_suffix[$text]++;
  331. $suffix = '_'.$this->id_suffix[$text];
  332. } else {
  333. $this->id_suffix[$text] = 1;
  334. }
  335. $id = $this->sanitize_id($text).$suffix;
  336. $ret = "<h{$level} id=\"$id\">{$text}</h{$level}>";
  337. $link = $this->append_pagenum_anchor($this->get_permalink( $this->options['wpwiki_page_title'] ), $this->pagenum, $id);
  338. $this->headings[] = array (
  339. 'level' => $level,
  340. 'link' => $link,
  341. 'text' => $text
  342. );
  343. }
  344. else
  345. $ret = "<h{$level}>{$text}</h{$level}>";
  346. return $ret;
  347. }
  348. /**
  349. * The callback function for lists
  350. */
  351. private function list_callback($matches) {
  352. $list = array();
  353. $prev = "";
  354. $ret = "";
  355. preg_match_all('/^((\*|#|\;|\:|\^)+)(.*?)$/ms', $matches[1], $list, PREG_SET_ORDER);
  356. foreach ($list as $val) {
  357. $whole = $val[0];
  358. $type = $val[1];
  359. $last_type = $val[2];
  360. $text = $val[3];
  361. if (substr($text, 0, 1) == " ")
  362. $text = substr($text, 1);
  363. $size = strlen($type);
  364. // same list level
  365. if ($type == $prev) {
  366. $ret .= $this->close_list_item($last_type).$this->open_list_item($last_type).$text;
  367. } else if ($type == $prev."^") {
  368. // a continuation of previous list item
  369. $ret .= "\n".$text;
  370. } else {
  371. // different list level
  372. $prev_size = strlen($prev);
  373. $min_size = min($size, $prev_size);
  374. // max common prefix
  375. $index = 0;
  376. while ($this->list_type_eq($type[$index], $prev[$index]) && $index < $min_size)
  377. $index++;
  378. // close previous non-common suffix
  379. while(count($this->list_stack) > $index) {
  380. $close_tag = array_pop($this->list_stack);
  381. $ret .= $this->close_list_item($close_tag).$this->close_list($close_tag);
  382. }
  383. // open current non-common suffix
  384. $open = false;
  385. for ($i = $index; $i < $size; $i++) {
  386. $open_tag = $type[$i];
  387. array_push($this->list_stack, $open_tag);
  388. $open = true;
  389. $ret .= $this->open_list($open_tag).$this->open_list_item($open_tag);
  390. }
  391. if (!$open) {
  392. $close_tag = $prev[$index - 1];
  393. $open_tag = $type[$index - 1];
  394. // exchange stack item
  395. array_pop($this->list_stack);
  396. array_push($this->list_stack, $open_tag);
  397. $ret .= $this->close_list_item($close_tag).$this->open_list_item($open_tag);
  398. }
  399. $ret .= $text;
  400. $prev = $type;
  401. }
  402. }
  403. // close remainder
  404. while (count($this->list_stack) > 0) {
  405. $close_tag = array_pop($this->list_stack);
  406. $ret .= $this->close_list_item($close_tag).$this->close_list($close_tag);
  407. }
  408. return "\n".$ret."\n";
  409. }
  410. /**
  411. * The callback function for tables
  412. */
  413. private function table_callback($matches) {
  414. $whole = $matches[0];
  415. $attrs = trim($matches[1]);
  416. $rows = $matches[3];
  417. if (array_key_exists(4, $matches)) {
  418. $this->table_level += 3;
  419. $rows = preg_replace_callback($this->regex['table'][0], array($this, $this->regex['table'][1]), $rows);
  420. $this->table_level -= 3;
  421. }
  422. $rregex = '#(?:^(\||!)-|\G)(.*?)^(.*?)(?=(?:\|-|!-|\z))#msi';
  423. $rows = preg_replace_callback($rregex, array($this, 'rows_callback'), $rows);
  424. $start = $attrs == "" ? "<table>" : "<table {$attrs}>";
  425. $end = "</table>";
  426. // $ret =
  427. // str_repeat("\t", $this->table_level).$start."\n".
  428. // $rows.
  429. // str_repeat("\t", $this->table_level).$end."\n";
  430. $ret = $start."\n".$rows.$end."\n";
  431. return $ret;
  432. }
  433. /**
  434. * The callback function for rows in tables
  435. */
  436. private function rows_callback($matches) {
  437. $whole = $matches[0];
  438. $attrs = trim($matches[2]);
  439. $cells = $matches[3];
  440. if ($whole == "")
  441. return $whole;
  442. $cregex = '#((?:\||!|\|\||!!|\G))(?:([^|\n]*?)\|(?!\|))?(.+?)(?=\||!|\|\||!!|\z)#msi';
  443. $cells = preg_replace_callback($cregex, array(&$this, 'cells_callback'), $cells);
  444. $start = $attrs == "" ? "<tr>" : "<tr {$attrs}>";
  445. $end = "</tr>";
  446. // $ret =
  447. // str_repeat("\t", $this->table_level + 1).$start."\n".
  448. // $cells.
  449. // str_repeat("\t", $this->table_level + 1).$end."\n";
  450. $ret = $start."\n".$cells.$end."\n";
  451. return $ret;
  452. }
  453. /**
  454. * The callback function for cols in rows
  455. */
  456. private function cells_callback($matches) {
  457. $whole = $matches[0];
  458. $type = $matches[1];
  459. $attrs = trim($matches[2]);
  460. $cell = trim($matches[3]);
  461. if($whole == "")
  462. return $whole;
  463. if ($type == '!') {
  464. $start = $attrs == "" ? "<th>" : "<th {$attrs}>";
  465. $end = "</th>";
  466. } else {
  467. $start = $attrs == "" ? "<td>" : "<td {$attrs}>";
  468. $end = "</td>";
  469. }
  470. // $ret =
  471. // str_repeat("\t", $this->table_level + 2).$start."\n".
  472. // str_repeat("\t", $this->table_level + 3).$cell."\n".
  473. // str_repeat("\t", $this->table_level + 2).$end."\n";
  474. $ret = $start."\n".$cell."\n".$end."\n";
  475. return $ret;
  476. }
  477. /**
  478. * The callback function for external links
  479. */
  480. private function url_callback($matches) {
  481. $whole = $matches[0];
  482. $explicit = $matches[1]; // a left "["
  483. $url = $matches[2]; // url
  484. $protocol = $matches[3]; // protocol, eq: http://
  485. $desc = $matches[4]; // url description
  486. if (!isset($desc)) {
  487. if($explicit)
  488. $desc = "[".$this->auto_number++."]";
  489. else
  490. $desc = $url;
  491. }
  492. $desc = trim($desc);
  493. $ret = "<a href=\"{$url}\">{$desc}</a>";
  494. return $ret;
  495. }
  496. /**
  497. * The callback function for email links
  498. */
  499. private function email_callback($matches) {
  500. $whole = $matches[0];
  501. $addr = $matches[2];
  502. $ret = "<a href=\"mailto:{$addr}\">$addr</a>";
  503. return $ret;
  504. }
  505. /**
  506. * Split anchor into the anchor text and the index of the anchor
  507. */
  508. private function split_anchor($anchor) {
  509. $index = strrpos($anchor, '_');
  510. // get the anchor text and anchor index
  511. if ($index != false && $index < strlen($anchor) && is_numeric(substr($anchor, $index + 1))) {
  512. $anchor_txt = substr($anchor, 0, $index);
  513. $anchor_num = substr($anchor, $index + 1);
  514. } else {
  515. $anchor_txt = $anchor;
  516. $anchor_num = 1;
  517. }
  518. return array ($anchor_txt, $anchor_num);
  519. }
  520. /**
  521. * Append page number and anchor to an url to form a valid url
  522. */
  523. private function append_pagenum_anchor($url, $pagenum = 0, $anchor = '') {
  524. $anchor = $anchor ? '#'.$anchor : $anchor;
  525. if ($pagenum > 1)
  526. $url .= (strpos($url, '?') ? '&' : '?').'page='.$pagenum;
  527. $url .= $anchor;
  528. return $url;
  529. }
  530. private function is_toc_id($id) {
  531. switch ($id) {
  532. case "_toc":
  533. case "_tochead":
  534. case "_toctoggle":
  535. case "_toclist":
  536. $ret = true; break;
  537. default:
  538. $ret = false; break;
  539. }
  540. return $ret;
  541. }
  542. /**
  543. * The callback function for wikilinks
  544. */
  545. private function wikilink_callback($matches) {
  546. // $link: $title#$anchor
  547. // $title: the post title
  548. // $anchor: the user defined anchor or some heading
  549. // $desc: the displayed text
  550. $while = $matches[0];
  551. $link = $matches[1];
  552. $desc = $matches[2];
  553. list($title, $anchor) = split('#', $link);
  554. // set desc if desc is empty
  555. $desc = $desc ? $desc : $link;
  556. // compute url
  557. if ($title) {
  558. $url = $this->options['wpwiki_wiki_site'].$title.($anchor ? '#'.$anchor : $anchor);
  559. }
  560. $ret = "<a href=\"{$url}\">{$desc}</a>";
  561. return $ret;
  562. }
  563. /**
  564. * The callback function for bold and italic
  565. */
  566. private function emphasis_callback($matches) {
  567. $type = $matches[1];
  568. $text = $matches[2];
  569. switch (strlen($type)) {
  570. case 1:
  571. $ret = "<em>{$text}</em>";
  572. break;
  573. case 2:
  574. $ret = "<strong>{$text}</strong>";
  575. break;
  576. case 4:
  577. $ret = "<em><strong>{$text}</strong></em>";
  578. break;
  579. case 3:
  580. $delim = "'";
  581. $ret = "<strong>{$delim}{$text}{$delim}</strong>";
  582. break;
  583. }
  584. return $ret;
  585. }
  586. private function paragraph_callback($matches) {
  587. $text = trim($matches[1]);
  588. if ($text == "")
  589. return "";
  590. // skip <pre></pre>
  591. $regex = '/'.$this->rmap['<'].'(pre|nowiki)'.$this->rmap['>'].'(.*)'.$this->rmap['<'].$this->rmap['/'].'(\1)'.$this->rmap['>'].'/s';
  592. if (preg_match($regex, $text))
  593. return "$text\n";
  594. return "<p>\n$text\n</p>\n";;
  595. }
  596. /**
  597. * The callback function for nowiki. Simply return nothing
  598. */
  599. private function nowiki_callback($matches) {
  600. return "";
  601. }
  602. /**
  603. * The callback function for preformatted text
  604. */
  605. private function preformatted_callback($matches) {
  606. $ret = explode("\n", $matches[0]);
  607. $ret = array_map(array($this, 'remove_leading_char'), $ret);
  608. $ret = implode("\n", $ret);
  609. return $this->encode(array("\n<pre>$ret\n</pre>"));
  610. }
  611. }