Main Page | Class Hierarchy | Class List | File List | Class Members

Textile.php

Go to the documentation of this file.
00001 <?php 00002 // @(#) $Id: Textile_8php-source.html,v 1.4 2005/03/21 14:46:22 jhriggs Exp $ 00003 00004 /* This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation; either version 2 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License 00015 * along with this program; if not, write to the Free Software 00016 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00017 */ 00018 00030 class Textile { 00037 var $options = array(); 00038 00046 var $urlre; 00047 00055 var $punct; 00056 00064 var $valignre; 00065 00073 var $tblalignre; 00074 00082 var $halignre; 00083 00091 var $alignre; 00092 00100 var $imgalignre; 00101 00109 var $clstypadre; 00110 00118 var $clstyre; 00119 00127 var $clstyfiltre; 00128 00136 var $codere; 00137 00145 var $blocktags; 00146 00152 var $links = array(); 00153 00165 var $repl = array(); 00166 00173 var $tmp = array(); 00174 00186 function Textile($options = array()) { 00187 $this->options = $options; 00188 $this->options['filters'] = ($this->options['filters'] ? $this->options['filters'] : array()); 00189 $this->options['charset'] = ($this->options['charset'] ? $this->options['charset'] : 'iso-8859-1'); 00190 $this->options['char_encoding'] = (isset($this->options['char_encoding']) ? $this->options['char_encoding'] : 1); 00191 $this->options['do_quotes'] = (isset($this->options['do_quotes']) ? $this->options['do_quotes'] : 1); 00192 $this->options['trim_spaces'] = (isset($this->options['trim_spaces']) ? $this->options['trim_spaces'] : 0); 00193 $this->options['smarty_mode'] = (isset($this->options['smarty_mode']) ? $this->options['smarty_mode'] : 1); 00194 $this->options['preserve_spaces'] = (iset($this->options['preserve_spaces']) ? $this->options['preserve_spaaces'] : 0); 00195 $this->options['head_offset'] = (isset($this->options['head_offset']) ? $this->options['head_offset'] : 0); 00196 00197 if (is_array($this->options['css'])) { 00198 $this->css($this->options['css']); 00199 } 00200 $this->options['macros'] = ($this->options['macros'] ? $this->options['macros'] : $this->default_macros()); 00201 if (isset($this->options['flavor'])) { 00202 $this->flavor($this->options['flavor']); 00203 } else { 00204 $this->flavor('xhtml1/css'); 00205 } 00206 $this->_create_re(); 00207 } // function Textile 00208 00209 // getter/setter methods... 00210 00221 function set($opt, $value = NULL) { 00222 if (is_array($opt)) { 00223 foreach ($opt as $opt => $value) { 00224 $this->set($opt, $value); 00225 } 00226 } else { 00227 // the following options have special set methods 00228 // that activate upon setting: 00229 if ($opt == 'charset') { 00230 $this->charset($value); 00231 } elseif ($opt == 'css') { 00232 $this->css($value); 00233 } elseif ($opt == 'flavor') { 00234 $this->flavor($value); 00235 } else { 00236 $this->options[$opt] = $value; 00237 } 00238 } 00239 } // function set 00240 00251 function get($opt) { 00252 return $this->options[$opt]; 00253 } // function get 00254 00269 function disable_html($disable_html = NULL) { 00270 if ($disable_html != NULL) { 00271 $this->options['disable_html'] = $disable_html; 00272 } 00273 return ($this->options['disable_html'] ? $this->options['disable_html'] : 0); 00274 } // function disable_html 00275 00290 function head_offset($head_offset = NULL) { 00291 if ($head_offset != NULL) { 00292 $this->options['head_offset'] = $head_offset; 00293 } 00294 return ($this->options['head_offset'] ? $this->options['head_offset'] : 0); 00295 } // function head_offset 00296 00313 function flavor($flavor = NULL) { 00314 if ($flavor != NULL) { 00315 $this->options['flavor'] = $flavor; 00316 if (preg_match('/^xhtml(\d)?(\D|$)/', $flavor, $matches)) { 00317 if ($matches[1] == '2') { 00318 $this->options['_line_open'] = '<l>'; 00319 $this->options['_line_close'] = '</l>'; 00320 $this->options['_blockcode_open'] = '<blockcode>'; 00321 $this->options['_blockcode_close'] = '</blockcode>'; 00322 $this->options['css_mode'] = 1; 00323 } else { 00324 // xhtml 1.x 00325 $this->options['_line_open'] = ''; 00326 $this->options['_line_close'] = '<br />'; 00327 $this->options['_blockcode_open'] = '<pre><code>'; 00328 $this->options['_blockcode_close'] = '</code></pre>'; 00329 $this->options['css_mode'] = 1; 00330 } 00331 } elseif (preg_match('/^html/', $flavor)) { 00332 $this->options['_line_open'] = ''; 00333 $this->options['_line_close'] = '<br>'; 00334 $this->options['_blockcode_open'] = '<pre><code>'; 00335 $this->options['_blockcode_close'] = '</code></pre>'; 00336 $this->options['css_mode'] = preg_match('/\/css/', $flavor); 00337 } 00338 if ($this->options['css_mode'] && !isset($this->options['css'])) { $this->_css_defaults(); } 00339 } 00340 return $this->options['flavor']; 00341 } // function flavor 00342 00404 function css($css = NULL) { 00405 if ($css != NULL) { 00406 if (is_array($css)) { 00407 $this->options['css'] = $css; 00408 $this->options['css_mode'] = 1; 00409 } else { 00410 $this->options['css_mode'] = $css; 00411 if ($this->options['css_mode'] && !isset($this->options['css'])) { $this->_css_defaults(); } 00412 } 00413 } 00414 return ($this->options['css_mode'] ? $this->options['css'] : 0); 00415 } // function css 00416 00433 function charset($charset = NULL) { 00434 if ($charset != NULL) { 00435 $this->options['charset'] = $charset; 00436 if (preg_match('/^utf-?8$/i', $this->options['charset'])) { 00437 $this->char_encoding(0); 00438 } else { 00439 $this->char_encoding(1); 00440 } 00441 } 00442 return $this->options['charset']; 00443 } // function charset 00444 00458 function docroot($docroot = NULL) { 00459 if ($docroot != NULL) { 00460 $this->options['docroot'] = $docroot; 00461 } 00462 return $this->options['docroot']; 00463 } // function docroot 00464 00478 function trim_spaces($trim_spaces = NULL) { 00479 if ($trim_spaces != NULL) { 00480 $this->options['trim_spaces'] = $trim_spaces; 00481 } 00482 return $this->options['trim_spaces']; 00483 } // function trim_spaces 00484 00495 function filter_param($filter_param = NULL) { 00496 if ($filter_param != NULL) { 00497 $this->options['filter_param'] = $filter_param; 00498 } 00499 return $this->options['filter_param']; 00500 } // function filter_param 00501 00518 function preserve_spaces($preserve_spaces = NULL) { 00519 if ($preserve_spaces != NULL) { 00520 $this->options['preserve_spaces'] = $preserve_spaces; 00521 } 00522 return $this->options['preserve_spaces']; 00523 } // function preserve_spaces 00524 00537 function filters($filters = NULL) { 00538 if ($filters != NULL) { 00539 $this->options['filters'] = $filters; 00540 } 00541 return $this->options['filters']; 00542 } // function filters 00543 00558 function char_encoding($char_encoding = NULL) { 00559 if ($char_encoding != NULL) { 00560 $this->options['char_encoding'] = $char_encoding; 00561 } 00562 return $this->options['char_encoding']; 00563 } // function char_encoding 00564 00577 function handle_quotes($do_quotes = NULL) { 00578 if ($do_quotes != NULL) { 00579 $this->options['do_quotes'] = $do_quotes; 00580 } 00581 return $this->options['do_quotes']; 00582 } // function handle_quotes 00583 00584 // end of getter/setter methods 00585 00599 function _create_re() { 00600 // a URL discovery regex. This is from Mastering Regex from O'Reilly. 00601 // Some modifications by Brad Choate <brad at bradchoate dot com> 00602 $this->urlre = '(?: 00603 # Must start out right... 00604 (?=[a-zA-Z0-9./#]) 00605 # Match the leading part (proto://hostname, or just hostname) 00606 (?: 00607 # ftp://, http://, or https:// leading part 00608 (?:ftp|https?|telnet|nntp)://(?:\w+(?::\w+)?@)?[-\w]+(?:\.\w[-\w]*)+ 00609 | 00610 (?:mailto:)?[-\+\w]+@[-\w]+(?:\.\w[-\w]*)+ 00611 | 00612 # or, try to find a hostname with our more specific sub-expression 00613 (?i: [a-z0-9] (?:[-a-z0-9]*[a-z0-9])? \. )+ # sub domains 00614 # Now ending .com, etc. For these, require lowercase 00615 (?-i: com\b 00616 | edu\b 00617 | biz\b 00618 | gov\b 00619 | in(?:t|fo)\b # .int or .info 00620 | mil\b 00621 | net\b 00622 | org\b 00623 | museum\b 00624 | aero\b 00625 | coop\b 00626 | name\b 00627 | pro\b 00628 | [a-z][a-z]\b # two-letter country codes 00629 ) 00630 )? 00631 00632 # Allow an optional port number 00633 (?: : \d+ )? 00634 00635 # The rest of the URL is optional, and begins with / . . . 00636 (?: 00637 /? 00638 # The rest are heuristics for what seems to work well 00639 [^.!,?;:"\'<>()\[\]{}\s\x7F-\xFF]* 00640 (?: 00641 [.!,?;:]+ [^.!,?;:"\'<>()\[\]{}\s\x7F-\xFF]+ #\'" 00642 )* 00643 )? 00644 )'; 00645 00646 $this->punct = '[\!"\#\$%&\'()\*\+,\-\./:;<=>\?@\[\\\\\]\^_`{\|}\~]'; 00647 $this->valignre = '[\-^~]'; 00648 $this->tblalignre = '[<>=]'; 00649 $this->halignre = '(?:<>|[<>=])'; 00650 $this->alignre = '(?:(?:' . $this->valignre . '|<>' . $this->valignre . '?|' . $this->valignre . '?<>|' . $this->valignre . '?' . $this->halignre . '?|' . $this->halignre . '?' . $this->valignre . '?)(?!\w))'; 00651 $this->imgalignre = '(?:(?:[<>]|' . $this->valignre . '){1,2})'; 00652 00653 $this->clstypadre = '(?: 00654 (?:\([A-Za-z0-9_\- \#]+\)) 00655 | 00656 (?:{ 00657 (?: \( [^)]+ \) | [^\}] )+ 00658 }) 00659 | 00660 (?:\(+? (?![A-Za-z0-9_\-\#]) ) 00661 | 00662 (?:\)+?) 00663 | 00664 (?: \[ [a-zA-Z\-]+? \] ) 00665 )'; 00666 00667 $this->clstyre = '(?: 00668 (?:\([A-Za-z0-9_\- \#]+\)) 00669 | 00670 (?:{ 00671 [A-Za-z0-9_\-](?: \( [^)]+ \) | [^\}] )+ 00672 }) 00673 | 00674 (?: \[ [a-zA-Z\-]+? \] ) 00675 )'; 00676 00677 $this->clstyfiltre = '(?: 00678 (?:\([A-Za-z0-9_\- \#]+\)) 00679 | 00680 (?:{ 00681 [A-Za-z0-9_\-](?: \( [^)]+ \) | [^\}] )+ 00682 }) 00683 | 00684 (?:\|[^\|]+\|) 00685 | 00686 (?:\(+?(?![A-Za-z0-9_\-\#])) 00687 | 00688 (?:\)+) 00689 | 00690 (?: \[ [a-zA-Z]+? \] ) 00691 )'; 00692 00693 $this->codere = '(?: 00694 (?: 00695 [\[{] 00696 @ # opening 00697 (?:\[([A-Za-z0-9]+)\])? # $1: language id 00698 (.+?) # $2: code 00699 @ # closing 00700 [\]}] 00701 ) 00702 | 00703 (?: 00704 (?:^|(?<=[\s\(])) 00705 @ # opening 00706 (?:\[([A-Za-z0-9]+)\])? # $3: language id 00707 ([^\s].+?[^\s]) # $4: code itself 00708 @ # closing 00709 (?:$|(?=' . $this->punct . '{1,2}|\s)) 00710 ) 00711 )'; 00712 00713 $this->blocktags = ' 00714 < 00715 (( /? ( h[1-6] 00716 | p 00717 | pre 00718 | div 00719 | table 00720 | t[rdh] 00721 | [ou]l 00722 | li 00723 | block(?:quote|code) 00724 | form 00725 | input 00726 | select 00727 | option 00728 | textarea 00729 ) 00730 [ >] 00731 ) 00732 | !-- 00733 ) 00734 '; 00735 } // function _create_re 00736 00746 function process($str) { 00747 /* 00748 * Function names in PHP are case insensitive, so function 00749 * textile() cannot be redefined. Thus, this PHP implementation 00750 * will only use process(). 00751 * 00752 * return $this->textile($str); 00753 * } // function process 00754 * 00755 * function textile($str) { 00756 */ 00757 00758 // quick translator for abbreviated block names 00759 // to their tag 00760 $macros = array('bq' => 'blockquote'); 00761 00762 // an array to hold any portions of the text to be preserved 00763 // without further processing by Textile 00764 array_unshift($this->repl, array()); 00765 00766 // strip out extra newline characters. we're only matching for \n herein 00767 //$str = preg_replace('!(?:\r?\n|\r)!', "\n", $str); 00768 $str = preg_replace('!(?:\015?\012|\015)!', "\n", $str); 00769 00770 // optionally remove trailing spaces 00771 if ($this->options['trim_spaces']) { $str = preg_replace('/ +$/m', '', $str); } 00772 00773 // preserve contents of the '==', 'pre', 'blockcode' sections 00774 $str = preg_replace_callback('{(^|\n\n)==(.+?)==($|\n\n)}s', 00775 $this->_cb('"$m[1]\n\n" . $me->_repl($me->repl[0], $me->format_block(array("text" => $m[2]))) . "\n\n$m[3]"'), $str); 00776 00777 if (!$this->disable_html()) { 00778 // preserve style, script tag contents 00779 $str = preg_replace_callback('!(<(style|script)(?:>| .+?>).*?</\2>)!s', $this->_cb('$me->_repl($me->repl[0], $m[1])'), $str); 00780 00781 // preserve HTML comments 00782 $str = preg_replace_callback('|(<!--.+?-->)|s', $this->_cb('$me->_repl($me->repl[0], $m[1])'), $str); 00783 00784 // preserve pre block contents, encode contents by default 00785 $pre_start = count($this->repl[0]); 00786 $str = preg_replace_callback('{(<pre(?: [^>]*)?>)(.+?)(</pre>)}s', 00787 $this->_cb('"\n\n" . $me->_repl($me->repl[0], $m[1] . $me->encode_html($m[2], 1) . $m[3]) . "\n\n"'), $str); 00788 // fix code tags within pre blocks we just saved. 00789 for ($i = $pre_start; $i < count($this->repl[0]); $i++) { 00790 $this->repl[0][$i] = preg_replace('|&lt;(/?)code(.*?)&gt;|s', '<$1code$2>', $this->repl[0][$i]); 00791 } 00792 00793 // preserve code blocks by default, encode contents 00794 $str = preg_replace_callback('{(<code(?: [^>]+)?>)(.+?)(</code>)}s', 00795 $this->_cb('$me->_repl($me->repl[0], $m[1] . $me->encode_html($m[2], 1) . $m[3])'), $str); 00796 00797 // encode blockcode tag (an XHTML 2 tag) and encode it's 00798 // content by default 00799 $str = preg_replace_callback('{(<blockcode(?: [^>]+)?>)(.+?)(</blockcode>)}s', 00800 $this->_cb('"\n\n" . $me->_repl($me->repl[0], $m[1] . $me->encode_html($m[2], 1) . $m[3]) . "\n\n"'), $str); 00801 00802 // preserve PHPish, ASPish code 00803 $str = preg_replace_callback('!(<([\?%]).*?(\2)>)!s', $this->_cb('$me->_repl($me->repl[0], $m[1])'), $str); 00804 } 00805 00806 // pass through and remove links that follow this format 00807 // [id_without_spaces (optional title text)]url 00808 // lines like this are stripped from the content, and can be 00809 // referred to using the "link text":id_without_spaces syntax 00810 //$links = array(); 00811 $str = preg_replace_callback('{(?:\n|^) [ ]* \[ ([^ ]+?) [ ]*? (?:\( (.+?) \) )? \] ((?:(?:ftp|https?|telnet|nntp)://|/)[^ ]+?) [ ]* (\n|$)}mx', 00812 $this->_cb('($me->links[$m[1]] = array("url" => $m[3], "title" => $m[2])) ? $m[4] : $m[4]'), $str); 00813 //$this->links = $links; 00814 00815 // eliminate starting/ending blank lines 00816 $str = preg_replace('/^\n+/s', '', $str, 1); 00817 $str = preg_replace('/\n+$/s', '', $str, 1); 00818 00819 // split up text into paragraph blocks, capturing newlines too 00820 $para = preg_split('/(\n{2,})/', $str, -1, PREG_SPLIT_DELIM_CAPTURE); 00821 unset($block, $bqlang, $filter, $class, $sticky, $lines, 00822 $style, $stickybuff, $lang, $clear); 00823 00824 $out = ''; 00825 00826 foreach ($para as $para) { 00827 if (preg_match('/^\n+$/s', $para)) { 00828 if ($sticky && $stickybuff) { 00829 $stickybuff .= $para; 00830 } else { 00831 $out .= $para; 00832 } 00833 continue; 00834 } 00835 00836 if ($sticky) { 00837 $sticky++; 00838 } else { 00839 unset($block); 00840 unset($class); 00841 $style = ''; 00842 unset($lang); 00843 } 00844 00845 unset($id, $cite, $align, $padleft, $padright, $lines, $buffer); 00846 if (preg_match('{^(h[1-6]|p|bq|bc|fn\d+) 00847 ((?:' . $this->clstyfiltre . '*|' . $this->halignre . ')*) 00848 (\.\.?) 00849 (?::(\d+|' . $this->urlre . '))?\ (.*)$}sx', $para, $matches)) { 00850 if ($sticky) { 00851 if ($block == 'bc') { 00852 // close our blockcode section 00853 $out = preg_replace('/\n\n$/', '', $out, 1); 00854 $out .= $this->options['_blockcode_close'] . "\n\n"; 00855 } elseif ($block == 'bq') { 00856 $out = preg_replace('/\n\n$/', '', $out, 1); 00857 $out .= '</blockquote>' . "\n\n"; 00858 } elseif ($block == 'table') { 00859 $table_out = $this->format_table(array('text' => $stickybuff)); 00860 if (!$table_out) { $table_out = ''; } 00861 $out .= $table_out; 00862 unset($stickybuff); 00863 } elseif ($block == 'dl') { 00864 $dl_out = $this->format_deflist(array('text' => $stickybuff)); 00865 if (!$dl_out) { $dl_out = ''; } 00866 $out .= $dl_out; 00867 unset($stickybuff); 00868 } 00869 $sticky = 0; 00870 } 00871 // block macros: h[1-6](class)., bq(class)., bc(class)., p(class). 00872 //warn "paragraph: [[$para]]\n\tblock: $1\n\tparams: $2\n\tcite: $4"; 00873 $block = $matches[1]; 00874 $params = $matches[2]; 00875 $cite = $matches[4]; 00876 if ($matches[3] == '..') { 00877 $sticky = 1; 00878 } else { 00879 $sticky = 0; 00880 unset($class); 00881 unset($bqlang); 00882 unset($lang); 00883 $style = ''; 00884 unset($filter); 00885 } 00886 if (preg_match('/^h([1-6])$/', $block, $matches2)) { 00887 if ($this->options['head_offset']) { 00888 $block = 'h' . ($matches2[1] + $this->options['head_offset']); 00889 } 00890 } 00891 if (preg_match('{(' . $this->halignre . '+)}', $params, $matches2)) { 00892 $align = $matches2[1]; 00893 $params = preg_replace('{' . $this->halignre . '+}', '', $params, 1); 00894 } 00895 if ($params) { 00896 if (preg_match('/\|(.+)\|/', $params, $matches2)) { 00897 $filter = $matches2[1]; 00898 $params = preg_replace('/\|.+?\|/', '', $params, 1); 00899 } 00900 if (preg_match('/{([^}]+)}/', $params, $matches2)) { 00901 $style = $matches2[1]; 00902 $style = preg_replace('/\n/', ' ', $style); 00903 $params = preg_replace('/{[^}]+}/', '', $params); 00904 } 00905 if (preg_match('/\(([A-Za-z0-9_\-\ ]+?)(?:\#(.+?))?\)/', $params, $matches2) || 00906 preg_match('/\(([A-Za-z0-9_\-\ ]+?)?(?:\#(.+?))\)/', $params, $matches2)) { 00907 if ($matches2[1] || $matches2[2]) { 00908 $class = $matches2[1]; 00909 $id = $matches2[2]; 00910 if ($class) { 00911 $params = preg_replace('/\([A-Za-z0-9_\-\ ]+?(#.*?)?\)/', '', $params); 00912 } elseif ($id) { 00913 $params = preg_replace('/\(#.+?\)/', '', $params); 00914 } 00915 } 00916 } 00917 if (preg_match('/(\(+)/', $params, $matches2)) { 00918 $padleft = strlen($matches2[1]); 00919 $params = preg_replace('/\(+/', '', $params, 1); 00920 } 00921 if (preg_match('/(\)+)/', $params, $matches2)) { 00922 $padright = strlen($matches2[1]); 00923 $params = preg_replace('/\)+/', '', $params, 1); 00924 } 00925 if (preg_match('/\[(.+?)\]/', $params, $matches2)) { 00926 $lang = $matches2[1]; 00927 if ($block == 'bc') { 00928 $bqlang = $lang; 00929 unset($lang); 00930 } 00931 $params = preg_replace('/\[.+?\]/', '', $params, 1); 00932 } 00933 } 00934 // warn "settings:\n\tblock: $block\n\tpadleft: $padleft\n\tpadright: $padright\n\tclass: $class\n\tstyle: $style\n\tid: $id\n\tfilter: $filter\n\talign: $align\n\tlang: $lang\n\tsticky: $sticky"; 00935 $para = $matches[5]; 00936 } elseif (preg_match('|^<textile#(\d+)>$|', $para, $matches)) { 00937 $buffer = $this->repl[0][$matches[1] - 1]; 00938 } elseif (preg_match('/^clear([<>]+)?\.$/', $para, $matches)) { 00939 if ($matches[1] == '<') { 00940 $clear = 'left'; 00941 } elseif ($matches[1] == '>') { 00942 $clear = 'right'; 00943 } else { 00944 $clear = 'both'; 00945 } 00946 continue; 00947 } elseif ($sticky && $stickybuff && 00948 ($block == 'table' || $block == 'dl')) { 00949 $stickybuff .= $para; 00950 continue; 00951 } elseif (preg_match('{^(?:' . $this->halignre . '|' . $this->clstypadre . '*)* 00952 [\*\#] 00953 (?:' . $this->halignre . '|' . $this->clstypadre . '*)* 00954 \ }x', $para)) { 00955 // '*', '#' prefix means a list 00956 $buffer = $this->format_list(array('text' => $para)); 00957 } elseif (preg_match('{^(?:table(?:' . $this->tblalignre . '|' . $this->clstypadre . '*)* 00958 (\.\.?)\s+)? 00959 (?:_|' . $this->alignre . '|' . $this->clstypadre . '*)*\|}x', $para, $matches)) { 00960 // handle wiki-style tables 00961 if ($matches[1] && ($matches[1] == '..')) { 00962 $block = 'table'; 00963 $stickybuff = $para; 00964 $sticky = 1; 00965 continue; 00966 } else { 00967 $buffer = $this->format_table(array('text' => $para)); 00968 } 00969 } elseif (preg_match('{^(?:dl(?:' . $this->clstyre . ')*(\.\.?)\s+)}x', $para, $matches)) { 00970 // handle definition lists 00971 if ($matches[1] && ($matches[1] == '..')) { 00972 $block = 'dl'; 00973 $stickybuff = $para; 00974 $sticky = 1; 00975 continue; 00976 } else { 00977 $buffer = $this->format_deflist(array('text' => $para)); 00978 } 00979 } 00980 if ($buffer) { 00981 $out .= $buffer; 00982 continue; 00983 } 00984 $lines = preg_split('/\n/', $para); 00985 if ((count($lines) == 1) && ($lines[0] == '')) { 00986 continue; 00987 } 00988 00989 $block = ($block ? $block : 'p'); 00990 00991 $buffer = ''; 00992 $pre = ''; 00993 $post = ''; 00994 00995 if ($block == 'bc') { 00996 if ($sticky <= 1) { 00997 $pre .= $this->options['_blockcode_open']; 00998 $pre = preg_replace('/>$/s', '', $pre, 1); 00999 if ($bqlang) { $pre .= " language=\"$bqlang\""; } 01000 if ($align) { 01001 $alignment = $this->_halign($align); 01002 if ($this->options['css_mode']) { 01003 if (($padleft || $padright) && 01004 (($alignment == 'left') || ($alignment == 'right'))) { 01005 $style .= ';float:' . $alignment; 01006 } else { 01007 $style .= ';text-align:' . $alignment; 01008 } 01009 $class .= ' ' . ($this->options['css']["class_align_$alignment"] ? $this->options['css']["class_align_$alignment"] : $alignment); 01010 } else { 01011 if ($alignment) { $pre .= " align=\"$alignment\""; } 01012 } 01013 } 01014 if ($padleft) { $style .= ";padding-left:${padleft}em"; } 01015 if ($padright) { $style .= ";padding-right:${padright}em"; } 01016 if ($clear) { $style .= ";clear:${clear}"; } 01017 if ($class) { $class = preg_replace('/^ /', '', $class, 1); } 01018 if ($class) { $pre .= " class=\"$class\""; } 01019 if ($id) { $pre .= " id=\"$id\""; } 01020 if ($style) { $style = preg_replace('/^;/', '', $style, 1); } 01021 if ($style) { $pre .= " style=\"$style\""; } 01022 if ($lang) { $pre .= " lang=\"$lang\""; } 01023 $pre .= '>'; 01024 unset($lang); 01025 unset($bqlang); 01026 unset($clear); 01027 } 01028 $para = preg_replace_callback('{(?:^|(?<=[\s>])|([{[])) 01029 ==(.+?)== 01030 (?:$|([\]}])|(?=' . $this->punct . '{1,2}|\s))}sx', 01031 $this->_cb('$me->_repl($me->repl[0], $me->format_block(array("text" => $m[2], "inline" => 1, "pre" => $m[1], "post" => $m[3])))'), $para); 01032 $buffer .= $this->encode_html_basic($para, 1); 01033 $buffer = preg_replace('/&lt;textile#(\d+)&gt;/', '<textile#$1>', $buffer); 01034 if ($sticky == 0) { 01035 $post .= $this->options['_blockcode_close']; 01036 } 01037 $out .= $pre . $buffer . $post; 01038 continue; 01039 } elseif ($block == 'bq') { 01040 if ($sticky <= 1) { 01041 $pre .= '<blockquote'; 01042 if ($align) { 01043 $alignment = $this->_halign($align); 01044 if ($this->options['css_mode']) { 01045 if (($padleft || $padright) && 01046 (($alignment == 'left') || ($alignment == 'right'))) { 01047 $style .= ';float:' . $alignment; 01048 } else { 01049 $style .= ';text-align:' . $alignment; 01050 } 01051 $class .= ' ' . ($this->options['css']["class_align_$alignment"] ? $this->options['css']["class_align_$alignment"] : $alignment); 01052 } else { 01053 if ($alignment) { $pre .= " align=\"$alignment\""; } 01054 } 01055 } 01056 if ($padleft) { $style .= ";padding-left:${padleft}em"; } 01057 if ($padright) { $style .= ";padding-right:${padright}em"; } 01058 if ($clear) { $style .= ";clear:${clear}"; } 01059 if ($class) { $class = preg_replace('/^ /', '', $class, 1); } 01060 if ($class) { $pre .= " class=\"$class\""; } 01061 if ($id) { $pre .= " id=\"$id\""; } 01062 if ($style) { $style = preg_replace('/^;/', '', $style, 1); } 01063 if ($style) { $pre .= " style=\"$style\""; } 01064 if ($lang) { $pre .= " lang=\"$lang\""; } 01065 if ($cite) { $pre .= ' cite="' . $this->format_url(array('url' => $cite)) . '"'; } 01066 $pre .= '>'; 01067 unset($clear); 01068 } 01069 $pre .= '<p>'; 01070 } elseif (preg_match('/fn(\d+)/', $block, $matches)) { 01071 $fnum = $matches[1]; 01072 $pre .= '<p'; 01073 if ($this->options['css']['class_footnote']) { $class .= ' ' . $this->options['css']['class_footnote']; } 01074 if ($align) { 01075 $alignment = $this->_halign($align); 01076 if ($this->options['css_mode']) { 01077 if (($padleft || $padright) && 01078 (($alignment == 'left') || ($alignment == 'right'))) { 01079 $style .= ';float:' . $alignment; 01080 } else { 01081 $style .= ';text-align:' . $alignment; 01082 } 01083 $class .= ($this->options['css']["class_align_$alignment"] ? $this->options['css']["class_align_$alignment"] : $alignment); 01084 } else { 01085 $pre .= " align=\"$alignment\""; 01086 } 01087 } 01088 if ($padleft) { $style .= ";padding-left:${padleft}em"; } 01089 if ($padright) { $style .= ";padding-right:${padright}em"; } 01090 if ($clear) { $style .= ";clear:${clear}"; } 01091 if ($class) { $class = preg_replace('/^ /', '', $class, 1); } 01092 if ($class) { $pre .= " class=\"$class\""; } 01093 $pre .= ' id="' . ($this->options['css']['id_footnote_prefix'] ? $this->options['css']['id_footnote_prefix'] : 'fn') . $fnum . '"'; 01094 if ($style) { $style = preg_replace('/^;/', '', $style, 1); } 01095 if ($style) { $pre .= " style=\"$style\""; } 01096 if ($lang) { $pre .= " lang=\"$lang\""; } 01097 $pre .= '>'; 01098 $pre .= '<sup>' . $fnum . '</sup> '; 01099 // we can close like a regular paragraph tag now 01100 $block = 'p'; 01101 unset($clear); 01102 } else { 01103 $pre .= '<' . ($macros[$block] ? $macros[$block] : $block); 01104 if ($align) { 01105 $alignment = $this->_halign($align); 01106 if ($this->options['css_mode']) { 01107 if (($padleft || $padright) && 01108 (($alignment == 'left') || ($alignment == 'right'))) { 01109 $style .= ';float:' . $alignment; 01110 } else { 01111 $style .= ';text-align:' . $alignment; 01112 } 01113 $class .= ' ' . ($this->options['css']["class_align_$alignment"] ? $this->options['css']["class_align_$alignment"] : $alignment); 01114 } else { 01115 $pre .= " align=\"$alignment\""; 01116 } 01117 } 01118 if ($padleft) { $style .= ";padding-left:${padleft}em"; } 01119 if ($padright) { $style .= ";padding-right:${padright}em"; } 01120 if ($clear) { $style .= ";clear:${clear}"; } 01121 if ($class) { $class = preg_replace('/^ /', '', $class, 1); } 01122 if ($class) { $pre .= " class=\"$class\""; } 01123 if ($id) { $pre .= " id=\"$id\""; } 01124 if ($style) { $style = preg_replace('/^;/', '', $style, 1); } 01125 if ($style) { $pre .= " style=\"$style\""; } 01126 if ($lang) { $pre .= " lang=\"$lang\""; } 01127 if ($cite && ($block == 'bq')) { $pre .= ' cite="' . $this->format_url(array('url' => $cite)) . '"'; } 01128 $pre .= '>'; 01129 unset($clear); 01130 } 01131 01132 $buffer = $this->format_paragraph(array('text' => $para)); 01133 01134 if ($block == 'bq') { 01135 if (!preg_match('/<p[ >]/', $buffer)) { $post .= '</p>'; } 01136 if ($sticky == 0) { 01137 $post .= '</blockquote>'; 01138 } 01139 } else { 01140 $post .= '</' . $block . '>'; 01141 } 01142 01143 if (preg_match('{' . $this->blocktags . '}x', $buffer)) { 01144 $buffer = preg_replace('/^\n\n/s', '', $buffer, 1); 01145 $out .= $buffer; 01146 } else { 01147 if ($filter) { $buffer = $this->format_block(array('text' => "|$filter|" . $buffer, 'inline' => 1)); } 01148 $out .= $pre . $buffer . $post; 01149 } 01150 } 01151 01152 if ($sticky) { 01153 if ($block == 'bc') { 01154 // close our blockcode section 01155 $out .= $this->options['_blockcode_close']; // . "\n\n"; 01156 } elseif ($block == 'bq') { 01157 $out .= '</blockquote>'; // . "\n\n"; 01158 } elseif (($block == 'table') && $stickybuff) { 01159 $table_out = $this->format_table(array('text' => $stickybuff)); 01160 if ($table_out) { $out .= $table_out; } 01161 } elseif (($block == 'dl') && $stickybuff) { 01162 $dl_out = $this->format_deflist(array('text' => $stickybuff)); 01163 if ($dl_out) { $out .= $dl_out; } 01164 } 01165 } 01166 01167 // cleanup-- restore preserved blocks 01168 for ($i = count($this->repl[0]); $i > 0; $i--) { 01169 $out = preg_replace('!(?:<|&lt;)textile#' . $i . '(?:>|&gt;)!', str_replace('$', '\\$', $this->repl[0][$i - 1]), $out, 1); 01170 } 01171 array_shift($this->repl); 01172 01173 // scan for br, hr tags that are not closed and close them 01174 // only for xhtml! just the common ones -- don't fret over input 01175 // and the like. 01176 if (preg_match('/^xhtml/i', $this->flavor())) { 01177 $out = preg_replace('/(<(?:img|br|hr)[^>]*?(?<!\/))>/', '$1 />', $out); 01178 } 01179 01180 return $out; 01181 } // function process 01182 01202 function format_paragraph($args) { 01203 $buffer = (isset($args['text']) ? $args['text'] : ''); 01204 01205 array_unshift($this->repl, array()); 01206 $buffer = preg_replace_callback('{(?:^|(?<=[\s>])|([{[])) 01207 ==(.+?)== 01208 (?:$|([\]}])|(?=' . $this->punct . '{1,2}|\s))}sx', 01209 $this->_cb('$me->_repl($me->repl[0], $me->format_block(array("text" => $m[2], "inline" => 1, "pre" => $m[1], "post" => $m[3])))'), $buffer); 01210 01211 unset($tokens); 01212 if (preg_match('/</', $buffer) && (!$this->disable_html())) { // optimization -- no point in tokenizing if we 01213 // have no tags to tokenize 01214 $tokens = $this->_tokenize($buffer); 01215 } else { 01216 $tokens = array(array('text', $buffer)); 01217 } 01218 $result = ''; 01219 foreach ($tokens as $token) { 01220 $text = $token[1]; 01221 if ($token[0] == 'tag') { 01222 $text = preg_replace('/&(?!amp;)/', '&amp;', $text); 01223 $result .= $text; 01224 } else { 01225 $text = $this->format_inline(array('text' => $text)); 01226 $result .= $text; 01227 } 01228 } 01229 01230 // now, add line breaks for lines that contain plaintext 01231 $lines = preg_split('/\n/', $result); 01232 $result = ''; 01233 $needs_closing = 0; 01234 foreach ($lines as $line) { 01235 if (!preg_match('{(' . $this->blocktags . ')}x', $line) 01236 && ((preg_match('/^[^<]/', $line) || preg_match('/>[^<]/', $line)) 01237 || !preg_match('/<img /', $line))) { 01238 if ($this->options['_line_open']) { 01239 if ($result != '') { $result .= "\n"; } 01240 $result .= $this->options['_line_open'] . $line . $this->options['_line_close']; 01241 } else { 01242 if ($needs_closing) { 01243 $result .= $this->options['_line_close'] . "\n"; 01244 } else { 01245 $needs_closing = 1; 01246 if ($result != '') { $result .= "\n"; } 01247 } 01248 $result .= $line; 01249 } 01250 } else { 01251 if ($needs_closing) { 01252 $result .= $this->options['_line_close'] . "\n"; 01253 } else { 01254 if ($result != '') { $result .= "\n"; } 01255 } 01256 $result .= $line; 01257 $needs_closing = 0; 01258 } 01259 } 01260 01261 // at this point, we will restore the \001's to \n's (reversing 01262 // the step taken in _tokenize). 01263 //$result = preg_replace('/\r/', "\n", $result); 01264 $result = preg_replace('/\001/', "\n", $result); 01265 01266 for ($i = count($this->repl[0]); $i > 0; $i--) { 01267 $result = preg_replace("|<textile#$i>|", str_replace('$', '\\$', $this->repl[0][$i - 1]), $result, 1); 01268 } 01269 array_shift($this->repl); 01270 01271 // quotalize 01272 if ($this->options['do_quotes']) { 01273 $result = $this->process_quotes($result); 01274 } 01275 01276 return $result; 01277 } // function format_paragraph 01278 01298 function format_inline($args) { 01299 $qtags = array(array('**', 'b', '(?<!\*)\*\*(?!\*)', '\*'), 01300 array('__', 'i', '(?<!_)__(?!_)', '_'), 01301 array('??', 'cite', '\?\?(?!\?)', '\?'), 01302 array('*', 'strong', '(?<!\*)\*(?!\*)', '\*'), 01303 array('_', 'em', '(?<!_)_(?!_)', '_'), 01304 array('-', 'del', '(?<!\-)\-(?!\-)', '-'), 01305 array('+', 'ins', '(?<!\+)\+(?!\+)', '\+'), 01306 array('++', 'big', '(?<!\+)\+\+(?!\+)', '\+\+'), 01307 array('--', 'small', '(?<!\-)\-\-(?!\-)', '\-\-'), 01308 array('~', 'sub', '(?<!\~)\~(?![\\\\/~])', '\~')); 01309 $text = (isset($args['text']) ? $args['text'] : ''); 01310 01311 array_unshift($this->repl, array()); 01312 01313 $text = preg_replace_callback('{' . $this->codere . '}mx', $this->_cb('$me->_repl($me->repl[0], $me->format_code(array("text" => $m[2] . $m[4], "lang" => $m[1] . $m[3])))'), $text); 01314 01315 // images must be processed before encoding the text since they might 01316 // have the <, > alignment specifiers... 01317 01318 // !blah (alt)! -> image 01319 $text = preg_replace_callback('{(?:^|(?<=[\s>])|([{[])) # $1: open brace/bracket 01320 ! # opening 01321 (' . $this->imgalignre . '?) # $2: optional alignment 01322 (' . $this->clstypadre . '*) # $3: optional CSS class/id 01323 (' . $this->imgalignre . '?) # $4: optional alignment 01324 (?:\s*) # space between alignment/css stuff 01325 ([^\s\(!]+) # $5: filename 01326 (\s*[^\(!]*(?:\([^\)]+\))?[^!]*) # $6: extras (alt text) 01327 ! # closing 01328 (?::(\d+|' . $this->urlre . '))? # $7: optional URL 01329 (?:$|([\]}])|(?=' . $this->punct . '{1,2}|\s)) # $8: closing brace/bracket 01330 }mx', $this->_cb('$me->_repl($me->repl[0], $me->format_image(array("pre" => $m[1], "src" => $m[5], "align" => ($m[2] ? $m[2] : $m[4]), "extra" => $m[6], "url" => $m[7], "clsty" => $m[3], "post" => $m[8])))'), $text); 01331 01332 $text = preg_replace_callback('{(?:^|(?<=[\s>])|([{[])) # $1: open brace/bracket 01333 % # opening 01334 (' . $this->halignre . '?) # $2: optional alignment 01335 (' . $this->clstyre . '*) # $3: optional CSS class/id 01336 (' . $this->halignre . '?) # $4: optional alignment 01337 (?:\s*) # spacing 01338 ([^%]+?) # $5: text 01339 % # closing 01340 (?::(\d+|' . $this->urlre . '))? # $6: optional URL 01341 (?:$|([]}])|(?=' . $this->punct . '{1,2}|\s)) # $7: closing brace/bracket 01342 }mx', $this->_cb('$me->_repl($me->repl[0], $me->format_span(array("pre" => $m[1], "text" => $m[5], "align" => ($m[2] ? $m[2] : $m[4]), "cite" => $m[6], "clsty" => $m[3], "post" => $m[7])))'), $text); 01343 01344 $text = $this->encode_html($text); 01345 $text = preg_replace('!&lt;textile#(\d+)&gt;!', '<textile#$1>', $text); 01346 $text = preg_replace('!&amp;quot;!', '&#34;', $text); 01347 $text = preg_replace('!&amp;(([a-z]+|#\d+);)!', '&$1', $text); 01348 $text = preg_replace('!&quot;!', '"', $text); 01349 01350 // These create markup with entities. Do first and 'save' result for later: 01351 // "text":url -> hyperlink 01352 // links with brackets surrounding 01353 $parenre = '\( (?: [^()] )* \)'; 01354 $text = preg_replace_callback('{( 01355 [{[] 01356 (?: 01357 (?:" # quote character 01358 (' . $this->clstyre . '*)? # $2: optional CSS class/id 01359 ([^"]+?) # $3: link text 01360 (?:\( ( (?:[^()]|' . $parenre . ')*) \))? # $4: optional link title 01361 " # closing quote 01362 ) 01363 | 01364 (?:\' # open single quote 01365 (' . $this->clstyre . '*)? # $5: optional CSS class/id 01366 ([^\']+?) # $6: link text 01367 (?:\( ( (?:[^()]|' . $parenre . ')*) \))? # $7: optional link title 01368 \' # closing quote 01369 ) 01370 ) 01371 :(.+?) # $8: URL suffix 01372 [\]}] 01373 ) 01374 }mx', $this->_cb('$me->_repl($me->repl[0], $me->format_link(array("text" => $m[1], "linktext" => $m[3] . $m[6], "title" => $me->encode_html_basic($m[4] . $m[7]), "url" => $m[8], "clsty" => $m[2] . $m[5])))'), $text); 01375 01376 $text = preg_replace_callback('{((?:^|(?<=[\s>\(])) # $1: open brace/bracket 01377 (?: (?:" # quote character " 01378 (' . $this->clstyre . '*)? # $2: optional CSS class/id 01379 ([^"]+?) # $3: link text " 01380 (?:\( ( (?:[^()]|' . $parenre . ')*) \))? # $4: optional link title 01381 " # closing quote # " 01382 ) 01383 | 01384 (?:\' # open single quote \' 01385 (' . $this->clstyre . '*)? # $5: optional CSS class/id 01386 ([^\']+?) # $6: link text \' 01387 (?:\( ( (?:[^()]|' . $parenre . ')*) \))? # $7: optional link title 01388 \' # closing quote \' 01389 ) 01390 ) 01391 :(\d+|' . $this->urlre . ') # $8: URL suffix 01392 (?:$|(?=' . $this->punct . '{1,2}|\s))) # $9: closing brace/bracket 01393 }mx', $this->_cb('$me->_repl($me->repl[0], $me->format_link(array("text" => $m[1], "linktext" => $m[3] . $m[6], "title" => $me->encode_html_basic($m[4] . $m[7]), "url" => $m[8], "clsty" => $m[2] . $m[5])))'), $text); 01394 01395 if (preg_match('/^xhtml2/', $this->flavor())) { 01396 // citation with cite link 01397 $text = preg_replace_callback('{(?:^|(?<=[\s>\'"\(])|([{[])) # $1: open brace/bracket \' 01398 \?\? # opening \'??\' 01399 ([^\?]+?) # $2: characters (can\'t contain \'?\') 01400 \?\? # closing \'??\' 01401 :(\d+|' . $this->urlre . ') # $3: optional citation URL 01402 (?:$|([\]}])|(?=' . $this->punct . '{1,2}|\s)) # $4: closing brace/bracket 01403 }mx', $this->_cb('$me->_repl($me->repl[0], $me->format_cite(array("pre" => $m[1], "text" => $m[2], "cite" => $m[3], "post" => $m[4])))'), $text); 01404 } 01405 01406 // footnotes 01407 if (preg_match('/[^ ]\[\d+\]/', $text)) { 01408 $fntag = '<sup'; 01409 if ($this->options['css']['class_footnote']) { $fntag .= ' class="' . $this->options['css']['class_footnote'] . '"'; } 01410 $fntag .= '><a href="#' . ($this->options['css']['id_footnote_prefix'] ? $this->options['css']['id_footnote_prefix'] : 'fn'); 01411 $text = preg_replace('|([^ ])\[(\d+)\]|', '$1' . $fntag . '$2">$2</a></sup>', $text); 01412 } 01413 01414 // translate macros: 01415 $text = preg_replace_callback('{(\{)(.+?)(\})}x', 01416 $this->_cb('$me->format_macro(array("pre" => $m[1], "post" => $m[3], "macro" => $m[2]))'), $text); 01417 01418 // these were present with textile 1 and are common enough 01419 // to not require macro braces... 01420 // (tm) -> &trade; 01421 $text = preg_replace('|[\(\[]TM[\)\]]|i', '&#8482;', $text); 01422 // (c) -> &copy; 01423 $text = preg_replace('|[\(\[]C[\)\]]|i', '&#169;', $text); 01424 // (r) -> &reg; 01425 $text = preg_replace('|[\(\[]R[\)\]]|i', '&#174;', $text); 01426 01427 if ($this->preserve_spaces()) { 01428 // replace two spaces with an em space 01429 $text = preg_replace('/(?<!\s)\ \ (?!=\s)/', '&#8195;', $text); 01430 } 01431 01432 $redo = preg_match('/[\*_\?\-\+\^\~]/', $text); 01433 $last = $text; 01434 while ($redo) { 01435 // simple replacements... 01436 $redo = 0; 01437 foreach ($qtags as $tag) { 01438 list ($this->tmp['f'][], $this->tmp['r'][], $qf, $cls) = $tag; 01439 if ($last != ($text = preg_replace_callback('{(?:^|(?<=[\s>\'"])|([{[])) # "\' $1 - pre 01440 ' . $qf . ' # 01441 (?:(' . $this->clstyre . '*))? # $2 - attributes 01442 ([^' . $cls . '\s].*?) # $3 - content 01443 (?<=\S)' . $qf . ' # 01444 (?:$|([\]}])|(?=' . $this->punct . '{1,2}|\s)) # $4 - post 01445 }mx', $this->_cb('$me->format_tag(array("tag" => end($me->tmp["r"]), "marker" => end($me->tmp["f"]), "pre" => $m[1], "text" => $m[3], "clsty" => $m[2], "post" => $m[4]))'), $text))) { 01446 $redo = ($redo || ($last != $text)); 01447 $last = $text; 01448 } 01449 array_pop($this->tmp['f']); array_pop($this->tmp['r']); 01450 } 01451 } 01452 01453 // superscript is an even simpler replacement... 01454 $text = preg_replace('/(?<!\^)\^(?!\^)(.+?)(?<!\^)\^(?!\^)/', '<sup>$1</sup>', $text); 01455 01456 // ABC(Aye Bee Cee) -> acronym 01457 $text = preg_replace_callback('{\b([A-Z][A-Za-z0-9]*?[A-Z0-9]+?)\b(?:[(]([^)]*)[)])}', 01458 $this->_cb('$me->_repl($me->repl[0],"<acronym title=\"" . $me->encode_html_basic($m[2]) . "\">$m[1]</acronym>")'), $text); 01459 01460 // ABC -> 'capped' span 01461 if ($this->tmp['caps'][] = $this->options['css']['class_caps']) { 01462 $text = preg_replace_callback('/(^|[^"][>\s]) # " 01463 ((?:[A-Z](?:[A-Z0-9\.,\']|\&amp;){2,}\ *)+?) # \' 01464 (?=[^A-Z\.0-9]|$) 01465 /mx', $this->_cb('$m[1] . $me->_repl($me->repl[0], "<span class=\"" . end($me->tmp["caps"]) . "\">$m[2]</span>")'), $text); 01466 } 01467 array_pop($this->tmp['caps']); 01468 01469 // nxn -> n&times;n 01470 $text = preg_replace('!((?:[0-9\.]0|[1-9]|\d[\'"])\ ?)x(\ ?\d)!', '$1&#215;$2', $text); 01471 01472 // translate these entities to the Unicode equivalents: 01473 $text = preg_replace('/&#133;/', '&#8230;', $text); 01474 $text = preg_replace('/&#145;/', '&#8216;', $text); 01475 $text = preg_replace('/&#146;/', '&#8217;', $text); 01476 $text = preg_replace('/&#147;/', '&#8220;', $text); 01477 $text = preg_replace('/&#148;/', '&#8221;', $text); 01478 $text = preg_replace('/&#150;/', '&#8211;', $text); 01479 $text = preg_replace('/&#151;/', '&#8212;', $text); 01480 01481 // Restore replacements done earlier: 01482 for ($i = count($this->repl[0]); $i > 0; $i--) { 01483 $text = preg_replace("|<textile#$i>|", str_replace('$', '\\$', $this->repl[0][$i - 1]), $text); 01484 } 01485 array_shift($this->repl); 01486 01487 // translate entities to characters for highbit stuff since 01488 // we're using utf8 01489 // removed for backward compatability with older versions of Perl 01490 //if (preg_match('/^utf-?8$/i', $this->options['charset'])) { 01491 // // translate any unicode entities to native UTF-8 01492 // $text = preg_replace('/\&\#(\d+);/e', '($1 > 127) ? pack('U', $1) : chr($1)', $text); 01493 //} 01494 01495 return $text; 01496 } // function format_inline 01497 01530 function format_macro($attrs) { 01531 $macro = $attrs['macro']; 01532 if ($this->options['macros'][$macro]) { 01533 return $this->options['macros'][$macro]; 01534 } 01535 01536 return $attrs['pre'] . $macro . $attrs['post']; 01537 } // function format_macro 01538 01570 function format_cite($args) { 01571 $pre = (isset($args['pre']) ? $args['pre'] : ''); 01572 $text = (isset($args['text']) ? $args['text'] : ''); 01573 $cite = $args['cite']; 01574 $post = (isset($args['post']) ? $args['post'] : ''); 01575 $this->_strip_borders($pre, $post); 01576 $tag = $pre . '<cite'; 01577 if (preg_match('/^xhtml2/', $this->flavor()) && $cite) { 01578 $cite = $this->format_url(array('url' => $cite)); 01579 $tag .= " cite=\"$cite\""; 01580 } else { 01581 $post .= ':'; 01582 } 01583 $tag .= '>'; 01584 return $tag . $this->format_inline(array('text' => $text)) . '</cite>' . $post; 01585 } // function format_cite 01586 01610 function format_code($args) { 01611 $code = (isset($args['text']) ? $args['text'] : ''); 01612 $lang = $args['lang']; 01613 $code = $this->encode_html($code, 1); 01614 $code = preg_replace('/&lt;textile#(\d+)&gt;/', '<textile#$1>', $code); 01615 $tag = '<code'; 01616 if ($lang) { $tag .= " language=\"$lang\""; } 01617 return $tag . '>' . $code . '</code>'; 01618 } // function format_code 01619 01662 function format_classstyle($clsty = NULL, $class = NULL, $style = NULL) { 01663 $class = preg_replace('/^ /', '', $class, 1); 01664 01665 unset($lang, $padleft, $padright, $id); 01666 if ($clsty && preg_match('/{([^}]+)}/', $clsty, $matches)) { 01667 $_style = $matches[1]; 01668 $_style = preg_replace('/\n/', ' ', $_style); 01669 $style .= ';' . $_style; 01670 $clsty = preg_replace('/{[^}]+}/', '', $clsty); 01671 } 01672 if ($clsty && (preg_match('/\(([A-Za-z0-9_\- ]+?)(?:#(.+?))?\)/', $clsty, $matches) || 01673 preg_match('/\(([A-Za-z0-9_\- ]+?)?(?:#(.+?))\)/', $clsty, $matches))) { 01674 if ($matches[1] || $matches[2]) { 01675 if ($class) { 01676 $class = $matches[1] . ' ' . $class; 01677 } else { 01678 $class = $matches[1]; 01679 } 01680 $id = $matches[2]; 01681 if ($class) { 01682 $clsty = preg_replace('/\([A-Za-z0-9_\- ]+?(#.*?)?\)/', '', $clsty); 01683 } 01684 if ($id) { 01685 $clsty = preg_replace('/\(#.+?\)/', '', $clsty); 01686 } 01687 } 01688 } 01689 if ($clsty && preg_match('/(\(+)/', $clsty, $matches)) { 01690 $padleft = strlen($matches[1]); 01691 $clsty = preg_replace('/\(+/', '', $clsty, 1); 01692 } 01693 if ($clsty && preg_match('/(\)+)/', $clsty, $matches)) { 01694 $padright = strlen($matches[1]); 01695 $clsty = preg_replace('/\)+/', '', $clsty, 1); 01696 } 01697 if ($clsty && preg_match('/\[(.+?)\]/', $clsty, $matches)) { 01698 $lang = $matches[1]; 01699 $clsty = preg_replace('/\[.+?\]/', '', $clsty); 01700 } 01701 $attrs = ''; 01702 if ($padleft) { $style .= ";padding-left:${padleft}em"; } 01703 if ($padright) { $style .= ";padding-right:${padright}em"; } 01704 $style = preg_replace('/^;/', '', $style, 1); 01705 $class = preg_replace('/^ /', '', $class, 1); 01706 $class = preg_replace('/ $/', '', $class, 1); 01707 if ($class) { $attrs .= " class=\"$class\""; } 01708 if ($id) { $attrs .= " id=\"$id\""; } 01709 if ($style) { $attrs .= " style=\"$style\""; } 01710 if ($lang) { $attrs .= " lang=\"$lang\""; } 01711 $attrs = preg_replace('/^ /', '', $attrs, 1); 01712 return $attrs; 01713 } // function format_classstyle 01714 01749 function format_tag($args) { 01750 $tagname = $args['tag']; 01751 $text = (isset($args['text']) ? $args['text'] : ''); 01752 $pre = (isset($args['pre']) ? $args['pre'] : ''); 01753 $post = (isset($args['post']) ? $args['post'] : ''); 01754 $clsty = (isset($args['clsty']) ? $args['clsty'] : ''); 01755 $this->_strip_borders($pre, $post); 01756 $tag = "<$tagname"; 01757 $attr = $this->format_classstyle($clsty); 01758 if ($attr) { $tag .= " $attr"; } 01759 $tag .= ">$text</$tagname>"; 01760 return $pre . $tag . $post; 01761 } // function format_tag 01762 01782 function format_deflist($args) { 01783 $str = (isset($args['text']) ? $args['text'] : ''); 01784 unset($clsty); 01785 $lines = preg_split('/\n/', $str); 01786 if (preg_match('{^(dl(' . $this->clstyre . '*?)\.\.?(?:\ +|$))}x', $lines[0], $matches)) { 01787 $clsty = $matches[2]; 01788 $lines[0] = substr($lines[0], strlen($matches[1])); 01789 } 01790 01791 unset($dt, $dd); 01792 $out = ''; 01793 foreach ($lines as $line) { 01794 if (preg_match('{^((?:' . $this->clstyre . '*)(?:[^\ ].*?)(?<!["\'\ ])):([^\ \/].*)$}x', $line, $matches)) { 01795 if ($dt && $dd) { $out .= $this->add_term($dt, $dd); } 01796 $dt = $matches[1]; 01797 $dd = $matches[2]; 01798 } else { 01799 $dd .= "\n" . $line; 01800 } 01801 } 01802 if ($dt && $dd) { $out .= $this->add_term($dt, $dd); } 01803 01804 $tag = '<dl'; 01805 if ($clsty) { $attr = $this->format_classstyle($clsty); } 01806 if ($attr) { $tag .= " $attr"; } 01807 $tag .= '>' . "\n"; 01808 01809 return $tag . $out . "</dl>\n"; 01810 } // function format_deflist 01811 01824 function add_term($dt, $dd) { 01825 unset($dtattr, $ddattr); 01826 unset($dtlang); 01827 if (preg_match('{^(' . $this->clstyre . '*)}x', $dt, $matches)) { 01828 $param = $matches[1]; 01829 $dtattr = $this->format_classstyle($param); 01830 if (preg_match('/\[([A-Za-z]+?)\]/', $param, $matches)) { 01831 $dtlang = $matches[1]; 01832 } 01833 $dt = substr($dt, strlen($param)); 01834 } 01835 if (preg_match('{^(' . $this->clstyre . '*)}x', $dd, $matches)) { 01836 $param = $matches[1]; 01837 // if the language was specified for the term, 01838 // then apply it to the definition as well (unless 01839 // already specified of course) 01840 if ($dtlang && preg_match('/\[([A-Za-z]+?)\]/', $param)) { 01841 unset($dtlang); 01842 } 01843 $ddattr = $this->format_classstyle(($dtlang ? "[$dtlang]" : '') . $param); 01844 $dd = substr($dd, strlen($param)); 01845 } 01846 $out = '<dt'; 01847 if ($dtattr) { $out .= " $dtattr"; } 01848 $out .= '>' . $this->format_inline(array('text' => $dt)) . '</dt>' . "\n"; 01849 if (preg_match('/\n\n/', $dd)) { 01850 if (preg_match('/\n\n/', $dd)) { $dd = $this->process($dd); } 01851 } else { 01852 $dd = $this->format_paragraph(array('text' => $dd)); 01853 } 01854 $out .= '<dd'; 01855 if ($ddattr) { $out .= " $ddattr"; } 01856 $out .= '>' . $dd . '</dd>' . "\n"; 01857 return $out; 01858 } // function add_term 01859 01883 function format_list($args) { 01884 $str = (isset($args['text']) ? $args['text'] : ''); 01885 01886 $list_tags = array('*' => 'ul', '#' => 'ol'); 01887 01888 $lines = preg_split('/\n/', $str); 01889 01890 unset($stack); 01891 $last_depth = 0; 01892 $item = ''; 01893 $out = ''; 01894 foreach ($lines as $line) { 01895 if (preg_match('{^((?:' . $this->clstypadre . '*|' . $this->halignre . ')*) 01896 ([\#\*]+) 01897 ((?:' . $this->halignre . '|' . $this->clstypadre . '*)*) 01898 \ (.+)$}x', $line, $matches)) { 01899 if ($item != '') { 01900 if (preg_match('/\n/', $item)) { 01901 if ($this->options['_line_open']) { 01902 $item = preg_replace('/(<li[^>]*>|^)/m', '$1' . $this->options['_line_open'], $item); 01903 $item = preg_replace('/(\n|$)/s', $this->options['_line_close'] . '$1', $item); 01904 } else { 01905 $item = preg_replace('/(\n)/s', $this->options['_line_close'] . '$1', $item); 01906 } 01907 } 01908 $out .= $item; 01909 $item = ''; 01910 } 01911 $type = substr($matches[2], 0, 1); 01912 $depth = strlen($matches[2]); 01913 $blockparam = $matches[1]; 01914 $itemparam = $matches[3]; 01915 $line = $matches[4]; 01916 unset ($blockclsty, $blockalign, $blockattr, $itemattr, $itemclsty, 01917 $itemalign); 01918 if (preg_match('{(' . $this->clstypadre . '+)}x', $blockparam, $matches)) { 01919 $blockclsty = $matches[1]; 01920 } 01921 if (preg_match('{(' . $this->halignre . '+)}', $blockparam, $matches)) { 01922 $blockalign = $matches[1]; 01923 } 01924 if (preg_match('{(' . $this->clstypadre . '+)}x', $itemparam, $matches)) { 01925 $itemclsty = $matches[1]; 01926 } 01927 if (preg_match('{(' . $this->halignre . '+)}', $itemparam, $matches)) { 01928 $itemalign = $matches[1]; 01929 } 01930 if ($itemclsty) { $itemattr = $this->format_classstyle($itemclsty); } 01931 if ($depth > $last_depth) { 01932 for ($j = $last_depth; $j < $depth; $j++) { 01933 $out .= "\n<$list_tags[$type]"; 01934 $stack[] = $type; 01935 if ($blockclsty) { 01936 $blockattr = $this->format_classstyle($blockclsty); 01937 if ($blockattr) { $out .= ' ' . $blockattr; } 01938 } 01939 $out .= ">\n<li"; 01940 if ($itemattr) { $out .= " $itemattr"; } 01941 $out .= ">"; 01942 } 01943 } elseif ($depth < $last_depth) { 01944 for ($j = $depth; $j < $last_depth; $j++) { 01945 if ($j == $depth) { $out .= "</li>\n"; } 01946 $type = array_pop($stack); 01947 $out .= "</$list_tags[$type]>\n</li>\n"; 01948 } 01949 if ($depth) { 01950 $out .= '<li'; 01951 if ($itemattr) { $out .= " $itemattr"; } 01952 $out .= '>'; 01953 } 01954 } else { 01955 $out .= "</li>\n<li"; 01956 if ($itemattr) { $out .= " $itemattr"; } 01957 $out .= '>'; 01958 } 01959 $last_depth = $depth; 01960 } 01961 if ($item != '') { $item .= "\n"; } 01962 $item .= $this->format_paragraph(array('text' => $line)); 01963 } 01964 01965 if (preg_match('/\n/', $item, $matches)) { 01966 if ($this->options['_line_open']) { 01967 $item = preg_replace('/(<li[^>]*>|^)/m', '$1' . $this->options['_line_open'], $item); 01968 $item = preg_replace('/(\n|$)/s', $this->options['_line_close'] . '$1', $item); 01969 } else { 01970 $item = preg_replace('/(\n)/s', $this->options['_line_close'] . '$1', $item); 01971 } 01972 } 01973 $out .= $item; 01974 01975 for ($j = 1; $j <= $last_depth; $j++) { 01976 if ($j == 1) { $out .= '</li>'; } 01977 $type = array_pop($stack); 01978 $out .= "\n" . '</' . $list_tags[$type] . '>' . "\n"; 01979 if ($j != $last_depth) { $out .= '</li>'; } 01980 } 01981 01982 return $out . "\n"; 01983 } // function format_list 01984 02003 function format_block($args) { 02004 $str = (isset($args['text']) ? $args['text'] : ''); 02005 $inline = $args['inline']; 02006 $pre = (isset($args['pre']) ? $args['pre'] : ''); 02007 $post = (isset($args['post']) ? $args['post'] : ''); 02008 $this->_strip_borders($pre, $post); 02009 $filters = (preg_match('/^(\|(?:(?:[a-z0-9_\-]+)\|)+)/', $str, $matches) ? $matches[1] : ''); 02010 if ($filters) { 02011 $filtreg = preg_replace('/[^A-Za-z0-9]/', '\\\\$1', $filters); 02012 $str = preg_replace('/^' . $filtreg . '/', '', $str, 1); 02013 $filters = preg_replace('/^\|/', '', $filters, 1); 02014 $filters = preg_replace('/\|$/', '', $filter, 1); 02015 $filters = preg_split('/\|/', $filters); 02016 $str = $this->apply_filters(array('text' => $str, 'filters' => $filters)); 02017 $count = count($filters); 02018 if ($str = preg_replace('!(<p>){' . $count . '}!se', '(++$i ? "$1" : "$1")', $str) && $i) { 02019 $str = preg_replace('!(</p>){' . $count . '}!s', '$1', $str); 02020 $str = preg_replace('!(<br( /)?>){' . $count . '}!s', '$1', $str); 02021 } 02022 } 02023 if ($inline) { 02024 // strip off opening para, closing para, since we're 02025 // operating within an inline block 02026 $str = preg_replace('/^\s*<p[^>]*>/', '', $str, 1); 02027 $str = preg_replace('/<\/p>\s*$/', '', $str, 1); 02028 } 02029 return $pre . $str . $post; 02030 } // function format_block 02031 02043 function format_link($args) { 02044 $text = (isset($args['text']) ? $args['text'] : ''); 02045 $linktext = (isset($args['linktext']) ? $args['linktext'] : ''); 02046 $title = $args['title']; 02047 $url = $args['url']; 02048 $clsty = $args['clsty']; 02049 02050 if (!$url || ($url == '')) { 02051 return $text; 02052 } 02053 if (isset($this->links) && isset($this->links[$url])) { 02054 $title = ($title ? $title : $this->links[$url]['title']); 02055 $url = $this->links[$url]['url']; 02056 } 02057 $linktext = preg_replace('/ +$/', '', $linktext, 1); 02058 $linktext = $this->format_paragraph(array('text' => $linktext)); 02059 $url = $this->format_url(array('linktext' => $linktext, 'url' => $url)); 02060 $tag = "<a href=\"$url\""; 02061 $attr = $this->format_classstyle($clsty); 02062 if ($attr) { $tag .= " $attr"; } 02063 if ($title) { 02064 $title = preg_replace('/^\s+/', '', $title, 1); 02065 if (strlen($title)) { $tag .= " title=\"$title\""; } 02066 } 02067 $tag .= ">$linktext</a>"; 02068 return $tag; 02069 } // function format_link 02070 02081 function format_url($args) { 02082 $url = ($args['url'] ? $args['url'] : ''); 02083 if (preg_match('/^(mailto:)?([-\+\w]+@[-\w]+(\.\w[-\w]*)+)$/', $url, $matches)) { 02084 $url = 'mailto:' . $this->mail_encode($matches[2]); 02085 } 02086 if (!preg_match('!^(/|\./|\.\./|#)!', $url)) { 02087 if (!preg_match('!^(https?|ftp|mailto|nntp|telnet)!', $url)) { $url = "http://$url"; } 02088 } 02089 $url = preg_replace('/&(?!amp;)/', '&amp;', $url); 02090 $url = preg_replace('/\ /', '+', $url); 02091 $url = preg_replace_callback('/^((?:.+?)\?)(.+)$/', $this->_cb('$m[1] . $me->encode_url($m[2])'), $url); 02092 return $url; 02093 } // function format_url 02094 02102 function format_span($args) { 02103 $text = (isset($args['text']) ? $args['text'] : ''); 02104 $pre = (isset($args['pre']) ? $args['pre'] : ''); 02105 $post = (isset($args['post']) ? $args['post'] : ''); 02106 $align = $args['align']; 02107 $cite = (isset($args['cite']) ? $args['cite'] : ''); 02108 $clsty = $args['clsty']; 02109 $this->_strip_borders($pre, $post); 02110 unset($class, $style); 02111 $tag = "<span"; 02112 $style = ''; 02113 if ($align) { 02114 if ($self->options['css_mode']) { 02115 $alignment = $this->_halign($align); 02116 if ($alignment) { $style .= ";float:$alignment"; } 02117 if ($alignment) { $class .= ' ' . $this->options['css']["class_align_$alignment"]; } 02118 } else { 02119 $alignment = ($this->_halign($align) ? $this->_halign($align) : $this->_valign($align)); 02120 if ($alignment) { $tag .= " align=\"$alignment\""; } 02121 } 02122 } 02123 $attr = $this->format_classstyle($clsty, $class, $style); 02124 if ($attr) { $tag .= " $attr"; } 02125 if ($cite) { 02126 $cite = preg_replace('/^:/', '', $cite, 1); 02127 $cite = $this->format_url(array('url' => $cite)); 02128 $tag .= " cite=\"$cite\""; 02129 } 02130 return $pre . $tag . '>' . $this->format_paragraph(array('text' => $text)) . '</span>' . $post; 02131 } // function format_span 02132 02186 function format_image($args) { 02187 $src = (isset($args['src']) ? $args['src'] : ''); 02188 $extra = $args['extra']; 02189 $align = $args['align']; 02190 $pre = (isset($args['pre']) ? $args['pre'] : ''); 02191 $post = (isset($args['post']) ? $args['post'] : ''); 02192 $link = $args['url']; 02193 $clsty = $args['clsty']; 02194 $this->_strip_borders($pre, $post); 02195 if (strlen($src) == 0) { return $pre . '!!' . $post; } 02196 unset($tag); 02197 if (preg_match('/^xhtml2/', $this->options['flavor'])) { 02198 unset($type); // poor man's mime typing. need to extend this externally 02199 if (preg_match('/(?:\.jpeg|\.jpg)$/i', $src)) { 02200 $type = 'image/jpeg'; 02201 } elseif (preg_match('/\.gif$/i', $src)) { 02202 $type = 'image/gif'; 02203 } elseif (preg_match('/\.png$/i', $src)) { 02204 $type = 'image/png'; 02205 } elseif (preg_match('/\.tiff$/i', $src)) { 02206 $type = 'image/tiff'; 02207 } 02208 $tag = "<object"; 02209 if ($type) { $tag .= " type=\"$type\""; } 02210 $tag .= " data=\"$src\""; 02211 } else { 02212 $tag = "<img src=\"$src\""; 02213 } 02214 unset($class, $style); 02215 if ($align) { 02216 if ($this->options['css_mode']) { 02217 $alignment = $this->_halign($align); 02218 if ($alignment) { $style .= ";float:$alignment"; } 02219 if ($alignment) { $class .= ' ' . $alignment; } 02220 $alignment = $this->_valign($align); 02221 if ($alignment) { 02222 $imgvalign = (preg_match('/(top|bottom)/', $alignment) ? 'text-' . $alignment : $alignment); 02223 if ($imgvalign) { $style .= ";vertical-align:$imgvalign"; } 02224 if ($alignment) { $class .= ' ' . $this->options['css']["class_align_$alignment"]; } 02225 } 02226 } else { 02227 $alignment = ($this->_halign($align) ? $this->_halign($align) : $this->_valign($align)); 02228 if ($alignment) { $tag .= " align=\"$alignment\""; } 02229 } 02230 } 02231 unset($pctw, $pcth, $w, $h, $alt); 02232 if ($extra) { 02233 $alt = (preg_match('/\(([^\)]+)\)/', $extra, $matches) ? $matches[1] : ''); 02234 $extra = preg_replace('/\([^\)]+\)/', '', $extra, 1); 02235 $pct = (preg_match('/(^|\s)(\d+)%(\s|$)/', $extra, $matches) ? $matches[2] : ''); 02236 if (!$pct) { 02237 list($pctw, $pcth) = (preg_match('/(^|\s)(\d+)%x(\d+)%(\s|$)/', $extra, $matches) ? array($matches[2], $matches[3]) : NULL); 02238 } else { 02239 $pctw = $pcth = $pct; 02240 } 02241 if (!$pctw && !$pcth) { 02242 list($w,$h) = (preg_match('/(^|\s)(\d+|\*)x(\d+|\*)(\s|$)/', $extra, $matches) ? array($matches[2], $matches[3]) : NULL); 02243 if ($w == '*') { $w = ''; } 02244 if ($h == '*') { $h = ''; } 02245 if (!$w) { 02246 $w = (preg_match('/(^|[,\s])(\d+)w([\s,]|$)/', $extra, $matches) ? $matches[2] : ''); 02247 } 02248 if (!$h) { 02249 $h = (preg_match('/(^|[,\s])(\d+)h([\s,]|$)/', $extra, $matches) ? $matches[2] : ''); 02250 } 02251 } 02252 } 02253 $alt = ($alt ? $alt : ''); 02254 if (!preg_match('/^xhtml2/', $this->options['flavor'])) { 02255 $tag .= ' alt="' . $this->encode_html_basic($alt) . '"'; 02256 } 02257 if ($w && $h) { 02258 if (!preg_match('/^xhtml2/', $this->options['flavor'])) { 02259 $tag .= " height=\"$h\" width=\"$w\""; 02260 } else { 02261 $style .= ";height:${h}px;width:${w}px"; 02262 } 02263 } else { 02264 list($image_w, $image_h) = $this->image_size($src); 02265 if (($image_w && $image_h) && ($w || $h)) { 02266 // image size determined, but only width or height specified 02267 if ($w && !$h) { 02268 // width defined, scale down height proportionately 02269 $h = intval($image_h * ($w / $image_w)); 02270 } elseif ($h && !$w) { 02271 $w = intval($image_w * ($h / $image_h)); 02272 } 02273 } else { 02274 $w = $image_w; 02275 $h = $image_h; 02276 } 02277 if ($w && $h) { 02278 if ($pctw || $pcth) { 02279 $w = intval($w * $pctw / 100); 02280 $h = intval($h * $pcth / 100); 02281 } 02282 if (!preg_match('/^xhtml2/', $this->options['flavor'])) { 02283 $tag .= " height=\"$h\" width=\"$w\""; 02284 } else { 02285 $style .= ";height:{$h}px;width:{$w}px"; 02286 } 02287 } 02288 } 02289 $attr = $this->format_classstyle($clsty, $class, $style); 02290 if ($attr) { $tag .= " $attr"; } 02291 if (preg_match('/^xhtml2/', $this->options['flavor'])) { 02292 $tag .= '><p>' . $this->encode_html_basic($alt) . '</p></object>'; 02293 } elseif (preg_match('/^xhtml/', $this->options['flavor'])) { 02294 $tag .= ' />'; 02295 } else { 02296 $tag .= '>'; 02297 } 02298 if ($link) { 02299 $link = preg_replace('/^:/', '', $link, 1); 02300 $link = $this->format_url(array('url' => $link)); 02301 $tag = '<a href="' . $link . '">' . $tag . '</a>'; 02302 } 02303 return $pre . $tag . $post; 02304 } // function format_image 02305 02317 function format_table($args) { 02318 $str = (isset($args['text']) ? $args['text'] : ''); 02319 02320 $lines = preg_split('/\n/', $str); 02321 unset($rows); 02322 $line_count = count($lines); 02323 for ($i = 0; $i < $line_count; $i++) { 02324 if (!preg_match('/\|\s*$/', $lines[$i])) { 02325 if ($i + 1 < $line_count) { 02326 if ($i + 1 <= count($lines) - 1) { $lines[$i + 1] = $lines[$i] . "\n" . $lines[$i + 1]; } 02327 } else { 02328 $rows[] = $lines[$i]; 02329 } 02330 } else { 02331 $rows[] = $lines[$i]; 02332 } 02333 } 02334 unset($tid, $tpadl, $tpadr, $tlang); 02335 $tclass = ''; 02336 $tstyle = ''; 02337 $talign = ''; 02338 if (preg_match('/^table[^\.]/', $rows[0])) { 02339 $row = $rows[0]; 02340 $row = preg_replace('/^table/', '', $row, 1); 02341 $params = 1; 02342 // process row parameters until none are left 02343 while ($params) { 02344 if (preg_match('{^(' . $this->tblalignre . ')}', $row, $matches)) { 02345 // found row alignment 02346 $talign .= $matches[1]; 02347 if ($matches[1]) { $row = substr($row, strlen($matches[1])); } 02348 if ($matches[1]) { continue; } 02349 } 02350 if (preg_match('{^(' . $this->clstypadre . ')}x', $row, $matches)) { 02351 // found a class/id/style/padding indicator 02352 $clsty = $matches[1]; 02353 if ($clsty) { $row = substr($row, strlen($clsty)); } 02354 if (preg_match('/{([^}]+)}/', $clsty, $matches)) { 02355 $tstyle = $matches[1]; 02356 $clsty = preg_replace('/{([^}]+)}/', '', $clsty, 1); 02357 if ($tstyle) { continue; } 02358 } 02359 if (preg_match('/\(([A-Za-z0-9_\- ]+?)(?:#(.+?))?\)/', $clsty, $matches) || 02360 preg_match('/\(([A-Za-z0-9_\- ]+?)?(?:#(.+?))\)/', $clsty, $matches)) { 02361 if ($matches[1] || $matches[2]) { 02362 $tclass = $matches[1]; 02363 $tid = $matches[2]; 02364 continue; 02365 } 02366 } 02367 if (preg_match('/(\(+)/', $clsty, $matches)) { $tpadl = strlen($matches[1]); } 02368 if (preg_match('/(\)+)/', $clsty, $matches)) { $tpadr = strlen($matches[1]); } 02369 if (preg_match('/\[(.+?)\]/', $clsty, $matches)) { $tlang = $matches[1]; } 02370 if ($clsty) { continue; } 02371 } 02372 $params = 0; 02373 } 02374 $row = preg_replace('/\.\s+/', '', $row, 1); 02375 $rows[0] = $row; 02376 } 02377 $out = ''; 02378 $cols = preg_split('/\|/', $rows[0] . ' '); 02379 unset($colaligns, $rowspans); 02380 foreach ($rows as $row) { 02381 $cols = preg_split('/\|/', $row . ' '); 02382 $colcount = count($cols) - 1; 02383 array_pop($cols); 02384 $colspan = 0; 02385 $row_out = ''; 02386 unset($rowclass, $rowid, $rowalign, $rowstyle, $rowheader); 02387 if (!$cols[0]) { $cols[0] = ''; } 02388 if (preg_match('/_/', $cols[0])) { 02389 $cols[0] = preg_replace('/_/', '', $cols[0]); 02390 $rowheader = 1; 02391 } 02392 if (preg_match('/{([^}]+)}/', $cols[0], $matches)) { 02393 $rowstyle = $matches[1]; 02394 $cols[0] = preg_replace('/{[^}]+}/', '', $cols[0]); 02395 } 02396 if (preg_match('/\(([^\#]+?)?(#(.+))?\)/', $cols[0], $matches)) { 02397 $rowclass = $matches[1]; 02398 $rowid = $matches[3]; 02399 $cols[0] = preg_replace('/\([^\)]+\)/', '', $cols[0]); 02400 } 02401 if (preg_match('{(' . $this->alignre . ')}', $cols[0], $matches)) { $rowalign = $matches[1]; } 02402 for ($c = $colcount - 1; $c > 0; $c--) { 02403 if ($rowspans[$c]) { 02404 $rowspans[$c]--; 02405 if ($rowspans[$c] > 1) { continue; } 02406 } 02407 unset($colclass, $colid, $header, $colparams, $colpadl, $colpadr, $collang); 02408 $colstyle = ''; 02409 $colalign = $colaligns[$c]; 02410 $col = array_pop($cols); 02411 $col = ($col ? $col : ''); 02412 $attrs = ''; 02413 if (preg_match('{^(((_|[/\\\\]\d+|' . $this->alignre . '|' . $this->clstypadre . ')+)\.\ )}x', $col, $matches)) { 02414 $colparams = $matches[2]; 02415 $col = substr($col, strlen($matches[1])); 02416 $params = 1; 02417 // keep processing column parameters until there 02418 // are none left... 02419 while ($params) { 02420 if (preg_match('{^(_|' . $this->alignre . ')(.*)$}', $colparams, $matches)) { 02421 // found alignment or heading indicator 02422 $attrs .= $matches[1]; 02423 if ($matches[1]) { $colparams = $matches[2]; } 02424 if ($matches[1]) { continue; } 02425 } 02426 if (preg_match('{^(' . $this->clstypadre . ')(.*)$}x', $colparams, $matches)) { 02427 // found a class/id/style/padding marker 02428 $clsty = $matches[1]; 02429 if ($clsty) { $colparams = $matches[2]; } 02430 if (preg_match('/{([^}]+)}/', $clsty, $matches)) { 02431 $colstyle = $matches[1]; 02432 $clsty = preg_replace('/{([^}]+)}/', '', $clsty, 1); 02433 } 02434 if (preg_match('/\(([A-Za-z0-9_\- ]+?)(?:#(.+?))?\)/', $clsty, $matches) || 02435 preg_match('/\(([A-Za-z0-9_\- ]+?)?(?:#(.+?))\)/', $clsty, $matches)) { 02436 if ($matches[1] || $matches[2]) { 02437 $colclass = $matches[1]; 02438 $colid = $matches[2]; 02439 if ($colclass) { 02440 $clsty = preg_replace('/\([A-Za-z0-9_\- ]+?(#.*?)?\)/', '', $clsty); 02441 } elseif ($colid) { 02442 $clsty = preg_replace('/\(#.+?\)/', '', $clsty); 02443 } 02444 } 02445 } 02446 if (preg_match('/(\(+)/', $clsty, $matches)) { 02447 $colpadl = strlen($matches[1]); 02448 $clsty = preg_replace('/\(+/', '', $clsty, 1); 02449 } 02450 if (preg_match('/(\)+)/', $clsty, $matches)) { 02451 $colpadr = strlen($matches[1]); 02452 $clsty = preg_replace('/\)+/', '', $clsty, 1); 02453 } 02454 if (preg_match('/\[(.+?)\]/', $clsty, $matches)) { 02455 $collang = $matches[1]; 02456 $clsty = preg_replace('/\[.+?\]/', '', $clsty, 1); 02457 } 02458 if ($clsty) { continue; } 02459 } 02460 if (preg_match('/^\\\\(\d+)/', $colparams, $matches)) { 02461 $colspan = $matches[1]; 02462 $colparams = substr($colparams, strlen($matches[1]) + 1); 02463 if ($matches[1]) { continue; } 02464 } 02465 if (preg_match('/\/(\d+)/', $colparams, $matches)) { 02466 if ($matches[1]) { $rowspans[$c] = $matches[1]; } 02467 $colparams = substr($colparams, strlen($matches[1]) + 1); 02468 if ($matches[1]) { continue; } 02469 } 02470 $params = 0; 02471 } 02472 } 02473 if (strlen($attrs)) { 02474 if (preg_match('/_/', $attrs)) { $header = 1; } 02475 if (preg_match('{(' . $this->alignre . ')}', $attrs, $matches) && strlen($matches[1])) { $colalign = ''; } 02476 // determine column alignment 02477 if (preg_match('/<>/', $attrs)) { 02478 $colalign .= '<>'; 02479 } elseif (preg_match('/</', $attrs)) { 02480 $colalign .= '<'; 02481 } elseif (preg_match('/=/', $attrs)) { 02482 $colalign = '='; 02483 } elseif (preg_match('/>/', $attrs)) { 02484 $colalign = '>'; 02485 } 02486 if (preg_match('/\^/', $attrs)) { 02487 $colalign .= '^'; 02488 } elseif (preg_match('/~/', $attrs)) { 02489 $colalign .= '~'; 02490 } elseif (preg_match('/-/', $attrs)) { 02491 $colalign .= '-'; 02492 } 02493 } 02494 if ($rowheader) { $header = 1; } 02495 if ($header) { $colaligns[$c] = $colalign; } 02496 $col = preg_replace('/^ +/', '', $col, 1); $col = preg_replace('/ +$/', '', $col, 1); 02497 if (strlen($col)) { 02498 // create one cell tag 02499 $rowspan = ($rowspans[$c] ? $rowspans[$c] : 0); 02500 $col_out = '<' . ($header ? 'th' : 'td'); 02501 if ($colalign) { 02502 // horizontal, vertical alignment 02503 $halign = $this->_halign($colalign); 02504 if ($halign) { $col_out .= " align=\"$halign\""; } 02505 $valign = $this->_valign($colalign); 02506 if ($valign) { $col_out .= " valign=\"$valign\""; } 02507 } 02508 // apply css attributes, row, column spans 02509 if ($colpadl) { $colstyle .= ";padding-left:${colpadl}em"; } 02510 if ($colpadr) { $colstyle .= ";padding-right:${colpadr}em"; } 02511 if ($colclass) { $col_out .= " class=\"$colclass\""; } 02512 if ($colid) { $col_out .= " id=\"$colid\""; } 02513 if ($colstyle) { $colstyle = preg_replace('/^;/', '', $colstyle, 1); } 02514 if ($colstyle) { $col_out .= " style=\"$colstyle\""; } 02515 if ($collang) { $col_out .= " lang=\"$collang\""; } 02516 if ($colspan > 1) { $col_out .= " colspan=\"$colspan\""; } 02517 if ($rowspan > 1) { $col_out .= " rowspan=\"$rowspan\""; } 02518 $col_out .= '>'; 02519 // if the content of this cell has newlines OR matches 02520 // our paragraph block signature, process it as a full-blown 02521 // textile document 02522 if (preg_match('/\n\n/', $col) || 02523 preg_match('{^(?:' . $this->halignre . '|' . $this->clstypadre . '*)* 02524 [\*\#] 02525 (?:' . $this->clstypadre . '*|' . $this->halignre . ')*\ }x', $col)) { 02526 $col_out .= $this->process($col); 02527 } else { 02528 $col_out .= $this->format_paragraph(array('text' => $col)); 02529 } 02530 $col_out .= '</' . ($header ? 'th' : 'td') . '>'; 02531 $row_out = $col_out . $row_out; 02532 if ($colspan) { $colspan = 0; } 02533 } else { 02534 if ($colspan == 0) { $colspan = 1; } 02535 $colspan++; 02536 } 02537 } 02538 if ($colspan > 1) { 02539 // handle the spanned column if we came up short 02540 $colspan--; 02541 $row_out = "<td" 02542 . ($colspan > 1 ? " colspan=\"$colspan\"" : '') 02543 . "></td>$row_out"; 02544 } 02545 02546 // build one table row 02547 $out .= "<tr"; 02548 if ($rowalign) { 02549 $valign = $this->_valign($rowalign); 02550 if ($valign) { $out .= " valign=\"$valign\""; } 02551 } 02552 if ($rowclass) { $out .= " class=\"$rowclass\""; } 02553 if ($rowid) { $out .= " id=\"$rowid\""; } 02554 if ($rowstyle) { $out .= " style=\"$rowstyle\""; } 02555 $out .= ">$row_out</tr>"; 02556 } 02557 02558 // now, form the table tag itself 02559 $table = ''; 02560 $table .= "<table"; 02561 if ($talign) { 02562 if ($this->options['css_mode']) { 02563 // horizontal alignment 02564 $alignment = $this->_halign($talign); 02565 if ($talign == '=') { 02566 $tstyle .= ';margin-left:auto;margin-right:auto'; 02567 } else { 02568 if ($alignment) { $tstyle .= ';float:' . $alignment; } 02569 } 02570 if ($alignment) { $tclass .= ' ' . $alignment; } 02571 } else { 02572 $alignment = $this->_halign($talign); 02573 if ($alignment) { $table .= " align=\"$alignment\""; } 02574 } 02575 } 02576 if ($tpadl) { $tstyle .= ";padding-left:${tpadl}em"; } 02577 if ($tpadr) { $tstyle .= ";padding-right:${tpadr}em"; } 02578 if ($tclass) { $tclass = preg_replace('/^ /', '', $tclass, 1); } 02579 if ($tclass) { $table .= " class=\"$tclass\""; } 02580 if ($tid) { $table .= " id=\"$tid\""; } 02581 if ($tstyle) { $tstyle = preg_replace('/^;/', '', $tstyle, 1); } 02582 if ($tstyle) { $table .= " style=\"$tstyle\""; } 02583 if ($tlang) { $table .= " lang=\"$tlang\""; } 02584 if ($tclass || $tid || $tstyle) { $table .= " cellspacing=\"0\""; } 02585 $table .= ">$out</table>"; 02586 02587 if (preg_match('|<tr></tr>|', $table)) { 02588 // exception -- something isn't right so return fail case 02589 return NULL; 02590 } 02591 02592 return $table; 02593 } // function format_table 02594 02617 function apply_filters($args) { 02618 $text = $args['text']; 02619 if (!$text) { return ''; } 02620 $list = $args['filters']; 02621 $filters = $this->options['filters']; 02622 if (!is_array($filters)) { return $text; } 02623 02624 $param = $this->filter_param(); 02625 foreach ($list as $filter) { 02626 if (!isset($filters[$filter])) { continue; } 02627 if (is_string($filters[$filter])) { 02628 $text = (($f = create_function('$text, $param', $filters[$filter])) ? $f($text, $param) : $text); 02629 } 02630 } 02631 return $text; 02632 } // function apply_filters 02633 02634 // minor utility / formatting routines 02635 02636 var $Have_Entities = 1; 02637 02654 function encode_html($html, $can_double_encode = FALSE) { 02655 if (!$html) { return ''; } 02656 if ($this->Have_Entities && $this->options['char_encoding']) { 02657 $html = htmlentities($html); 02658 } else { 02659 $html = $this->encode_html_basic($html, $can_double_encode); 02660 } 02661 return $html; 02662 } // function encode_html 02663 02674 function decode_html($html) { 02675 $html = preg_replace('!&quot;!', '"', $html); 02676 $html = preg_replace('!&amp;!', '&', $html); 02677 $html = preg_replace('!&lt;!', '<', $html); 02678 $html = preg_replace('!&gt;!', '>', $html); 02679 return $html; 02680 } // function decode_html 02681 02698 function encode_html_basic($html, $can_double_encode = FALSE) { 02699 if (!$html) { return ''; } 02700 if (!preg_match('/[^\w\s]/', $html)) { return $html; } 02701 if ($can_double_encode) { 02702 $html = preg_replace('!&!', '&amp;', $html); 02703 } else { 02704 // Encode any & not followed by something that looks like 02705 // an entity, numeric or otherwise. 02706 $html = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w{1,8});)/', '&amp;', $html); 02707 } 02708 $html = preg_replace('!"!', '&quot;', $html); 02709 $html = preg_replace('!<!', '&lt;', $html); 02710 $html = preg_replace('!>!', '&gt;', $html); 02711 return $html; 02712 } // function encode_html_basic 02713 02729 function image_size($file) { 02730 $Have_ImageSize = function_exists('getimagesize'); 02731 02732 if ($Have_ImageSize) { 02733 if (file_exists($file)) { 02734 return @getimagesize($file); 02735 } else { 02736 if ($docroot = ($this->docroot() ? $this->docroot() : $_SERVER['DOCUMENT_ROOT'])) { 02737 $fullpath = $docroot . preg_replace('|^/*(.*)$|', '/$1', $file); 02738 if (file_exists($fullpath)) { 02739 return @getimagesize($fullpath); 02740 } 02741 } 02742 } 02743 } 02744 return @getimagesize($file); 02745 } // function image_size 02746 02757 function encode_url($str) { 02758 $str = preg_replace_callback('!([^A-Za-z0-9_\.\-\+\&=%;])!x', 02759 $this->_cb('ord($m[1]) > 255 ? \'%u\' . sprintf("%04X", ord($m[1])) 02760 : \'%\' . sprintf("%02X", ord($m[1]))'), $str); 02761 return $str; 02762 } // function encode_url 02763 02773 function mail_encode($addr) { 02774 // granted, this is simple, but it gives off warm fuzzies 02775 $addr = preg_replace_callback('!([^\$])!x', 02776 $this->_cb('ord($m[1]) > 255 ? \'%u\' . sprintf("%04X", ord($m[1])) 02777 : \'%\' . sprintf("%02X", ord($m[1]))'), $addr); 02778 return $addr; 02779 } // function mail_encode 02780 02790 function process_quotes($str) { 02791 // stub routine for now. subclass and implement. 02792 return $str; 02793 } // function process_quotes 02794 02795 // a default set of macros for the {...} macro syntax 02796 // just a handy way to write a lot of the international characters 02797 // and some commonly used symbols 02798 02807 function default_macros() { 02808 // <, >, " must be html entities in the macro text since 02809 // those values are escaped by the time they are processed 02810 // for macros. 02811 return array( 02812 'c|' => '&#162;', // CENT SIGN 02813 '|c' => '&#162;', // CENT SIGN 02814 'L-' => '&#163;', // POUND SIGN 02815 '-L' => '&#163;', // POUND SIGN 02816 'Y=' => '&#165;', // YEN SIGN 02817 '=Y' => '&#165;', // YEN SIGN 02818 '(c)' => '&#169;', // COPYRIGHT SIGN 02819 '&lt;&lt;' => '&#171;', // LEFT-POINTING DOUBLE ANGLE QUOTATION 02820 '(r)' => '&#174;', // REGISTERED SIGN 02821 '+_' => '&#177;', // PLUS-MINUS SIGN 02822 '_+' => '&#177;', // PLUS-MINUS SIGN 02823 '&gt;&gt;' => '&#187;', // RIGHT-POINTING DOUBLE ANGLE QUOTATION 02824 '1/4' => '&#188;', // VULGAR FRACTION ONE QUARTER 02825 '1/2' => '&#189;', // VULGAR FRACTION ONE HALF 02826 '3/4' => '&#190;', // VULGAR FRACTION THREE QUARTERS 02827 'A`' => '&#192;', // LATIN CAPITAL LETTER A WITH GRAVE 02828 '`A' => '&#192;', // LATIN CAPITAL LETTER A WITH GRAVE 02829 'A\'' => '&#193;', // LATIN CAPITAL LETTER A WITH ACUTE 02830 '\'A' => '&#193;', // LATIN CAPITAL LETTER A WITH ACUTE 02831 'A^' => '&#194;', // LATIN CAPITAL LETTER A WITH CIRCUMFLEX 02832 '^A' => '&#194;', // LATIN CAPITAL LETTER A WITH CIRCUMFLEX 02833 'A~' => '&#195;', // LATIN CAPITAL LETTER A WITH TILDE 02834 '~A' => '&#195;', // LATIN CAPITAL LETTER A WITH TILDE 02835 'A"' => '&#196;', // LATIN CAPITAL LETTER A WITH DIAERESIS 02836 '"A' => '&#196;', // LATIN CAPITAL LETTER A WITH DIAERESIS 02837 'Ao' => '&#197;', // LATIN CAPITAL LETTER A WITH RING ABOVE 02838 'oA' => '&#197;', // LATIN CAPITAL LETTER A WITH RING ABOVE 02839 'AE' => '&#198;', // LATIN CAPITAL LETTER AE 02840 'C,' => '&#199;', // LATIN CAPITAL LETTER C WITH CEDILLA 02841 ',C' => '&#199;', // LATIN CAPITAL LETTER C WITH CEDILLA 02842 'E`' => '&#200;', // LATIN CAPITAL LETTER E WITH GRAVE 02843 '`E' => '&#200;', // LATIN CAPITAL LETTER E WITH GRAVE 02844 'E\'' => '&#201;', // LATIN CAPITAL LETTER E WITH ACUTE 02845 '\'E' => '&#201;', // LATIN CAPITAL LETTER E WITH ACUTE 02846 'E^' => '&#202;', // LATIN CAPITAL LETTER E WITH CIRCUMFLEX 02847 '^E' => '&#202;', // LATIN CAPITAL LETTER E WITH CIRCUMFLEX 02848 'E"' => '&#203;', // LATIN CAPITAL LETTER E WITH DIAERESIS 02849 '"E' => '&#203;', // LATIN CAPITAL LETTER E WITH DIAERESIS 02850 'I`' => '&#204;', // LATIN CAPITAL LETTER I WITH GRAVE 02851 '`I' => '&#204;', // LATIN CAPITAL LETTER I WITH GRAVE 02852 'I\'' => '&#205;', // LATIN CAPITAL LETTER I WITH ACUTE 02853 '\'I' => '&#205;', // LATIN CAPITAL LETTER I WITH ACUTE 02854 'I^' => '&#206;', // LATIN CAPITAL LETTER I WITH CIRCUMFLEX 02855 '^I' => '&#206;', // LATIN CAPITAL LETTER I WITH CIRCUMFLEX 02856 'I"' => '&#207;', // LATIN CAPITAL LETTER I WITH DIAERESIS 02857 '"I' => '&#207;', // LATIN CAPITAL LETTER I WITH DIAERESIS 02858 'D-' => '&#208;', // LATIN CAPITAL LETTER ETH 02859 '-D' => '&#208;', // LATIN CAPITAL LETTER ETH 02860 'N~' => '&#209;', // LATIN CAPITAL LETTER N WITH TILDE 02861 '~N' => '&#209;', // LATIN CAPITAL LETTER N WITH TILDE 02862 'O`' => '&#210;', // LATIN CAPITAL LETTER O WITH GRAVE 02863 '`O' => '&#210;', // LATIN CAPITAL LETTER O WITH GRAVE 02864 'O\'' => '&#211;', // LATIN CAPITAL LETTER O WITH ACUTE 02865 '\'O' => '&#211;', // LATIN CAPITAL LETTER O WITH ACUTE 02866 'O^' => '&#212;', // LATIN CAPITAL LETTER O WITH CIRCUMFLEX 02867 '^O' => '&#212;', // LATIN CAPITAL LETTER O WITH CIRCUMFLEX 02868 'O~' => '&#213;', // LATIN CAPITAL LETTER O WITH TILDE 02869 '~O' => '&#213;', // LATIN CAPITAL LETTER O WITH TILDE 02870 'O"' => '&#214;', // LATIN CAPITAL LETTER O WITH DIAERESIS 02871 '"O' => '&#214;', // LATIN CAPITAL LETTER O WITH DIAERESIS 02872 'O/' => '&#216;', // LATIN CAPITAL LETTER O WITH STROKE 02873 '/O' => '&#216;', // LATIN CAPITAL LETTER O WITH STROKE 02874 'U`' => '&#217;', // LATIN CAPITAL LETTER U WITH GRAVE 02875 '`U' => '&#217;', // LATIN CAPITAL LETTER U WITH GRAVE 02876 'U\'' => '&#218;', // LATIN CAPITAL LETTER U WITH ACUTE 02877 '\'U' => '&#218;', // LATIN CAPITAL LETTER U WITH ACUTE 02878 'U^' => '&#219;', // LATIN CAPITAL LETTER U WITH CIRCUMFLEX 02879 '^U' => '&#219;', // LATIN CAPITAL LETTER U WITH CIRCUMFLEX 02880 'U"' => '&#220;', // LATIN CAPITAL LETTER U WITH DIAERESIS 02881 '"U' => '&#220;', // LATIN CAPITAL LETTER U WITH DIAERESIS 02882 'Y\'' => '&#221;', // LATIN CAPITAL LETTER Y WITH ACUTE 02883 '\'Y' => '&#221;', // LATIN CAPITAL LETTER Y WITH ACUTE 02884 'a`' => '&#224;', // LATIN SMALL LETTER A WITH GRAVE 02885 '`a' => '&#224;', // LATIN SMALL LETTER A WITH GRAVE 02886 'a\'' => '&#225;', // LATIN SMALL LETTER A WITH ACUTE 02887 '\'a' => '&#225;', // LATIN SMALL LETTER A WITH ACUTE 02888 'a^' => '&#226;', // LATIN SMALL LETTER A WITH CIRCUMFLEX 02889 '^a' => '&#226;', // LATIN SMALL LETTER A WITH CIRCUMFLEX 02890 'a~' => '&#227;', // LATIN SMALL LETTER A WITH TILDE 02891 '~a' => '&#227;', // LATIN SMALL LETTER A WITH TILDE 02892 'a"' => '&#228;', // LATIN SMALL LETTER A WITH DIAERESIS 02893 '"a' => '&#228;', // LATIN SMALL LETTER A WITH DIAERESIS 02894 'ao' => '&#229;', // LATIN SMALL LETTER A WITH RING ABOVE 02895 'oa' => '&#229;', // LATIN SMALL LETTER A WITH RING ABOVE 02896 'ae' => '&#230;', // LATIN SMALL LETTER AE 02897 'c,' => '&#231;', // LATIN SMALL LETTER C WITH CEDILLA 02898 ',c' => '&#231;', // LATIN SMALL LETTER C WITH CEDILLA 02899 'e`' => '&#232;', // LATIN SMALL LETTER E WITH GRAVE 02900 '`e' => '&#232;', // LATIN SMALL LETTER E WITH GRAVE 02901 'e\'' => '&#233;', // LATIN SMALL LETTER E WITH ACUTE 02902 '\'e' => '&#233;', // LATIN SMALL LETTER E WITH ACUTE 02903 'e^' => '&#234;', // LATIN SMALL LETTER E WITH CIRCUMFLEX 02904 '^e' => '&#234;', // LATIN SMALL LETTER E WITH CIRCUMFLEX 02905 'e"' => '&#235;', // LATIN SMALL LETTER E WITH DIAERESIS 02906 '"e' => '&#235;', // LATIN SMALL LETTER E WITH DIAERESIS 02907 'i`' => '&#236;', // LATIN SMALL LETTER I WITH GRAVE 02908 '`i' => '&#236;', // LATIN SMALL LETTER I WITH GRAVE 02909 'i\'' => '&#237;', // LATIN SMALL LETTER I WITH ACUTE 02910 '\'i' => '&#237;', // LATIN SMALL LETTER I WITH ACUTE 02911 'i^' => '&#238;', // LATIN SMALL LETTER I WITH CIRCUMFLEX 02912 '^i' => '&#238;', // LATIN SMALL LETTER I WITH CIRCUMFLEX 02913 'i"' => '&#239;', // LATIN SMALL LETTER I WITH DIAERESIS 02914 '"i' => '&#239;', // LATIN SMALL LETTER I WITH DIAERESIS 02915 'n~' => '&#241;', // LATIN SMALL LETTER N WITH TILDE 02916 '~n' => '&#241;', // LATIN SMALL LETTER N WITH TILDE 02917 'o`' => '&#242;', // LATIN SMALL LETTER O WITH GRAVE 02918 '`o' => '&#242;', // LATIN SMALL LETTER O WITH GRAVE 02919 'o\'' => '&#243;', // LATIN SMALL LETTER O WITH ACUTE 02920 '\'o' => '&#243;', // LATIN SMALL LETTER O WITH ACUTE 02921 'o^' => '&#244;', // LATIN SMALL LETTER O WITH CIRCUMFLEX 02922 '^o' => '&#244;', // LATIN SMALL LETTER O WITH CIRCUMFLEX 02923 'o~' => '&#245;', // LATIN SMALL LETTER O WITH TILDE 02924 '~o' => '&#245;', // LATIN SMALL LETTER O WITH TILDE 02925 'o"' => '&#246;', // LATIN SMALL LETTER O WITH DIAERESIS 02926 '"o' => '&#246;', // LATIN SMALL LETTER O WITH DIAERESIS 02927 ':-' => '&#247;', // DIVISION SIGN 02928 '-:' => '&#247;', // DIVISION SIGN 02929 'o/' => '&#248;', // LATIN SMALL LETTER O WITH STROKE 02930 '/o' => '&#248;', // LATIN SMALL LETTER O WITH STROKE 02931 'u`' => '&#249;', // LATIN SMALL LETTER U WITH GRAVE 02932 '`u' => '&#249;', // LATIN SMALL LETTER U WITH GRAVE 02933 'u\'' => '&#250;', // LATIN SMALL LETTER U WITH ACUTE 02934 '\'u' => '&#250;', // LATIN SMALL LETTER U WITH ACUTE 02935 'u^' => '&#251;', // LATIN SMALL LETTER U WITH CIRCUMFLEX 02936 '^u' => '&#251;', // LATIN SMALL LETTER U WITH CIRCUMFLEX 02937 'u"' => '&#252;', // LATIN SMALL LETTER U WITH DIAERESIS 02938 '"u' => '&#252;', // LATIN SMALL LETTER U WITH DIAERESIS 02939 'y\'' => '&#253;', // LATIN SMALL LETTER Y WITH ACUTE 02940 '\'y' => '&#253;', // LATIN SMALL LETTER Y WITH ACUTE 02941 'y"' => '&#255;', // LATIN SMALL LETTER Y WITH DIAERESIS 02942 '"y' => '&#255;', // LATIN SMALL LETTER Y WITH DIAERESIS 02943 'OE' => '&#338;', // LATIN CAPITAL LIGATURE OE 02944 'oe' => '&#339;', // LATIN SMALL LIGATURE OE 02945 '*' => '&#8226;', // BULLET 02946 'Fr' => '&#8355;', // FRENCH FRANC SIGN 02947 'L=' => '&#8356;', // LIRA SIGN 02948 '=L' => '&#8356;', // LIRA SIGN 02949 'Rs' => '&#8360;', // RUPEE SIGN 02950 'C=' => '&#8364;', // EURO SIGN 02951 '=C' => '&#8364;', // EURO SIGN 02952 'tm' => '&#8482;', // TRADE MARK SIGN 02953 '&lt;-' => '&#8592;', // LEFTWARDS ARROW 02954 '-&gt;' => '&#8594;', // RIGHTWARDS ARROW 02955 '&lt;=' => '&#8656;', // LEFTWARDS DOUBLE ARROW 02956 '=&gt;' => '&#8658;', // RIGHTWARDS DOUBLE ARROW 02957 '=/' => '&#8800;', // NOT EQUAL TO 02958 '/=' => '&#8800;', // NOT EQUAL TO 02959 '&lt;_' => '&#8804;', // LESS-THAN OR EQUAL TO 02960 '_&lt;' => '&#8804;', // LESS-THAN OR EQUAL TO 02961 '&gt;_' => '&#8805;', // GREATER-THAN OR EQUAL TO 02962 '_&gt;' => '&#8805;', // GREATER-THAN OR EQUAL TO 02963 ':(' => '&#9785;', // WHITE FROWNING FACE 02964 ':)' => '&#9786;', // WHITE SMILING FACE 02965 'spade' => '&#9824;', // BLACK SPADE SUIT 02966 'club' => '&#9827;', // BLACK CLUB SUIT 02967 'heart' => '&#9829;', // BLACK HEART SUIT 02968 'diamond' => '&#9830;', // BLACK DIAMOND SUIT 02969 ); 02970 } // function default_macros 02971 02972 // "private", internal routines 02973 02980 function _css_defaults() { 02981 $css_defaults = array( 02982 'class_align_right' => 'right', 02983 'class_align_left' => 'left', 02984 'class_align_center' => 'center', 02985 'class_align_top' => 'top', 02986 'class_align_bottom' => 'bottom', 02987 'class_align_middle' => 'middle', 02988 'class_align_justify' => 'justify', 02989 'class_caps' => 'caps', 02990 'class_footnote' => 'footnote', 02991 'id_footnote_prefix' => 'fn', 02992 ); 02993 $this->css($css_defaults); 02994 } // function _css_defaults 02995 03025 function _halign($align) { 03026 if (preg_match('/<>/', $align)) { 03027 return 'justify'; 03028 } elseif (preg_match('/</', $align)) { 03029 return 'left'; 03030 } elseif (preg_match('/>/', $align)) { 03031 return 'right'; 03032 } elseif (preg_match('/=/', $align)) { 03033 return 'center'; 03034 } 03035 return ''; 03036 } // function _halign 03037 03063 function _valign($align) { 03064 if (preg_match('/\^/', $align)) { 03065 return 'top'; 03066 } elseif (preg_match('/~/', $align)) { 03067 return 'bottom'; 03068 } elseif (preg_match('/-/', $align)) { 03069 return 'middle'; 03070 } 03071 return ''; 03072 } // function _valign 03073 03109 function _imgalign($align) { 03110 $align = preg_replace('/(<>|=)/', '', $align); 03111 return ($this->_valign($align) ? $this->_valign($align) : $this->_halign($align)); 03112 } // function _imgalign 03113 03130 function _strip_borders(&$pre, &$post) { 03131 if ($post && $pre && preg_match('/[{[]/', ($open = substr($pre, 0, 1)))) { 03132 $close = substr($post, 0, 1); 03133 if ((($open == '{') && ($close == '}')) || 03134 (($open == '[') && ($close == ']'))) { 03135 $pre = substr($pre, 1); 03136 $post = substr($post, 1); 03137 } else { 03138 if (!preg_match('/[}\]]/', $close)) { $close = substr($post, -1, 1); } 03139 if ((($open == '{') && ($close == '}')) || 03140 (($open == '[') && ($close == ']'))) { 03141 $pre = substr($pre, 1); 03142 $post = substr($post, 0, strlen($post) - 1); 03143 } 03144 } 03145 } 03146 } // function _strip_borders 03147 03162 function _repl(&$array, $str) { 03163 $array[] = $str; 03164 return '<textile#' . count($array) . '>'; 03165 } // function _repl 03166 03177 function _tokenize($str) { 03178 $pos = 0; 03179 $len = strlen($str); 03180 unset($tokens); 03181 03182 $depth = 6; 03183 $nested_tags = substr(str_repeat('(?:</?[A-Za-z0-9:]+ \s? (?:[^<>]|', $depth), 0, -1) 03184 . str_repeat(')*>)', $depth); 03185 $match = '(?s: <! ( -- .*? -- \s* )+ > )| # comment 03186 (?s: <\? .*? \?> )| # processing instruction 03187 (?s: <% .*? %> )| # ASP-like 03188 (?:' . $nested_tags . ')| 03189 (?:' . $this->codere . ')'; // nested tags 03190 03191 while (preg_match('{(' . $match . ')}x', substr($str, $pos), $matches, PREG_OFFSET_CAPTURE)) { 03192 $whole_tag = $matches[1][0]; 03193 $sec_start = $pos + $matches[1][1] + strlen($whole_tag); 03194 $tag_start = $sec_start - strlen($whole_tag); 03195 if ($pos < $tag_start) { 03196 $tokens[] = array('text', substr($str, $pos, $tag_start - $pos)); 03197 } 03198 if (preg_match('/^[[{]?@/', $whole_tag)) { 03199 $tokens[] = array('text', $whole_tag); 03200 } else { 03201 // this clever hack allows us to preserve \n within tags. 03202 // this is restored at the end of the format_paragraph method 03203 //$whole_tag = preg_replace('/\n/', "\r", $whole_tag); 03204 $whole_tag = preg_replace('/\n/', "\001", $whole_tag); 03205 $tokens[] = array('tag', $whole_tag); 03206 } 03207 $pos = $sec_start; 03208 } 03209 if ($pos < $len) { $tokens[] = array('text', substr($str, $pos, $len - $pos)); } 03210 return $tokens; 03211 } // function _tokenize 03212 03221 function version() { 03222 /* Why text and an ID? Well, the text is easier for the user to 03223 * read and understand while the build ID, being a number (a date 03224 * with a serial, specifically), is easier for the developer to 03225 * use to determine newer/older versions for upgrade and 03226 * installation purposes. 03227 */ 03228 return array("text" => "2.0.8", "build" => 2005032100); 03229 } // function version 03230 03243 function _cb($function) { 03244 $current =& Textile::_current_store($this); 03245 return create_function('$m', '$me =& Textile::_current(); return ' . $function . ';'); 03246 } // function _cb 03247 03263 /* static */ function &_current_store(&$new) { 03264 static $current = array(); 03265 03266 if ($new != NULL) { 03267 $current = array(&$new); 03268 } 03269 03270 return $current; 03271 } // function _current_store 03272 03283 /* static */ function &_current() { 03284 $current =& Textile::_current_store($null = NULL); 03285 return $current[0]; 03286 } // function _current 03287 } // class Textile 03288 03337 class MTLikeTextile extends Textile { 03349 function MTLikeTextile($options = array()) { 03350 parent::Textile($options); 03351 } // function MTLikeTextile 03352 03356 function process_quotes($str) { 03357 if (!$this->options['do_quotes'] || !function_exists('SmartyPants')) { 03358 return $str; 03359 } 03360 03361 return SmartyPants($str, $this->options['smarty_mode']); 03362 } // function process_quotes 03363 03367 function format_url($args) { 03368 $url = ($args['url'] ? $args['url'] : ''); 03369 03370 if (preg_match('/^(imdb|google|dict|amazon)(:(.+))?$/x', $url, $matches)) { 03371 $term = $matches[3]; 03372 $term = ($term ? $term : strip_tags($args['linktext'])); 03373 03374 switch ($matches[1]) { 03375 case 'imdb': 03376 $args['url'] = 'http://www.imdb.com/Find?for=' . $term; 03377 break; 03378 03379 case 'google': 03380 $args['url'] = 'http://www.google.com/search?q=' . $term; 03381 break; 03382 03383 case 'dict': 03384 $args['url'] = 'http://www.dictionary.com/search?q=' . $term; 03385 break; 03386 03387 case 'amazon': 03388 $args['url'] = 'http://www.amazon.com/exec/obidos/external-search?index=blended&keyword=' . $term; 03389 break; 03390 } 03391 } 03392 03393 return parent::format_url($args); 03394 } // function format_url 03395 } // class MTLikeTextile 03396 04080 ?>

Generated on Mon Mar 21 08:37:29 2005 for TextilePHP by doxygen 1.3.7