4 * Example: get XHTML from a given Textile-markup string ($string)
6 * $textile = new Textile;
7 * echo $textile->TextileThis($string);
21 A Humane Web Text Generator
25 Copyright (c) 2003-2004, Dean Allen <dean@textism.com>
28 Thanks to Carlo Zottmann <carlo@g-blog.net> for refactoring
29 Textile's procedural code into a class framework
34 Redistribution and use in source and binary forms, with or without
35 modification, are permitted provided that the following conditions are met:
37 * Redistributions of source code must retain the above copyright notice,
38 this list of conditions and the following disclaimer.
40 * Redistributions in binary form must reproduce the above copyright notice,
41 this list of conditions and the following disclaimer in the documentation
42 and/or other materials provided with the distribution.
44 * Neither the name Textile nor the names of its contributors may be used to
45 endorse or promote products derived from this software without specific
46 prior written permission.
48 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
49 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
52 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
53 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
54 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
55 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
56 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
57 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
58 POSSIBILITY OF SUCH DAMAGE.
63 Block modifier syntax:
66 Paragraphs beginning with 'hn. ' (where n is 1-6) are wrapped in header tags.
67 Example: h1. Header... -> <h1>Header...</h1>
69 Paragraph: p. (also applied by default)
70 Example: p. Text -> <p>Text</p>
73 Example: bq. Block quotation... -> <blockquote>Block quotation...</blockquote>
75 Blockquote with citation: bq.:http://citation.url
76 Example: bq.:http://textism.com/ Text...
77 -> <blockquote cite="http://textism.com">Text...</blockquote>
80 Example: fn1. Footnote... -> <p id="fn1">Footnote...</p>
83 Consecutive paragraphs beginning with # are wrapped in ordered list tags.
84 Example: <ol><li>ordered list</li></ol>
87 Consecutive paragraphs beginning with * are wrapped in unordered list tags.
88 Example: <ul><li>unordered list</li></ul>
90 Phrase modifier syntax:
92 _emphasis_ -> <em>emphasis</em>
93 __italic__ -> <i>italic</i>
94 *strong* -> <strong>strong</strong>
95 **bold** -> <b>bold</b>
96 ??citation?? -> <cite>citation</cite>
97 -deleted text- -> <del>deleted</del>
98 +inserted text+ -> <ins>inserted</ins>
99 ^superscript^ -> <sup>superscript</sup>
100 ~subscript~ -> <sub>subscript</sub>
101 @code@ -> <code>computer code</code>
102 %(bob)span% -> <span class="bob">span</span>
104 ==notextile== -> leave text alone (do not format)
106 "linktext":url -> <a href="url">linktext</a>
107 "linktext(title)":url -> <a href="url" title="title">linktext</a>
109 !imageurl! -> <img src="imageurl" />
110 !imageurl(alt text)! -> <img src="imageurl" alt="alt text" />
111 !imageurl!:linkurl -> <a href="linkurl"><img src="imageurl" /></a>
113 ABC(Always Be Closing) -> <acronym title="Always Be Closing">ABC</acronym>
121 |And|Another|table|row|
123 |_. A|_. table|_. header|_.row|
126 Tables with attributes:
128 table{border:1px solid black}.
129 {background:#ddd;color:red}. |{}| | | |
134 Most anywhere Textile code is used, attributes such as arbitrary css style,
135 css classes, and ids can be applied. The syntax is fairly consistent.
137 The following characters quickly alter the alignment of block elements:
139 < -> left align ex. p<. left-aligned para
140 > -> right align h3>. right-aligned header 3
141 = -> centred h4=. centred header 4
142 <> -> justified p<>. justified paragraph
144 These will change vertical alignment in table cells:
146 ^ -> top ex. |^. top-aligned table cell|
147 - -> middle |-. middle aligned|
148 ~ -> bottom |~. bottom aligned cell|
150 Plain (parentheses) inserted between block syntax and the closing dot-space
151 indicate classes and ids:
153 p(hector). paragraph -> <p class="hector">paragraph</p>
155 p(#fluid). paragraph -> <p id="fluid">paragraph</p>
157 (classes and ids can be combined)
158 p(hector#fluid). paragraph -> <p class="hector" id="fluid">paragraph</p>
160 Curly {brackets} insert arbitrary css style
162 p{line-height:18px}. paragraph -> <p style="line-height:18px">paragraph</p>
164 h3{color:red}. header 3 -> <h3 style="color:red">header 3</h3>
166 Square [brackets] insert language attributes
168 p[no]. paragraph -> <p lang="no">paragraph</p>
170 %[fr]phrase% -> <span lang="fr">phrase</span>
172 Usually Textile block element syntax requires a dot and space before the block
173 begins, but since lists don't, they can be styled just using braces
175 #{color:blue} one -> <ol style="color:blue">
181 Using the span tag to style a phrase
183 It goes like this, %{color:red}the fourth the fifth%
184 -> It goes like this, <span style="color:red">the fourth the fifth</span>
204 // -------------------------------------------------------------
207 $this->hlgn
= "(?:\<(?!>)|(?<!<)\>|\<\>|\=|[()]+)";
208 $this->vlgn
= "[\-^~]";
209 $this->clas
= "(?:\([^)]+\))";
210 $this->lnge
= "(?:\[[^]]+\])";
211 $this->styl
= "(?:\{[^}]+\})";
212 $this->cspn
= "(?:\\\\\d+)";
213 $this->rspn
= "(?:\/\d+)";
214 $this->a
= "(?:{$this->hlgn}?{$this->vlgn}?|{$this->vlgn}?{$this->hlgn}?)";
215 $this->s
= "(?:{$this->cspn}?{$this->rspn}?|{$this->rspn}?{$this->cspn}?)";
216 $this->c
= "(?:{$this->clas}?{$this->styl}?{$this->lnge}?|{$this->styl}?{$this->lnge}?{$this->clas}?|{$this->lnge}?{$this->styl}?{$this->clas}?)";
217 $this->pnct
= '[\!"#\$%&\'()\*\+,\-\./:;<=>\?@\[\\\]\^_`{\|}\~]';
218 $this->urlch
= '[\w"$\-_.+!*\'(),";\/?:@=&%#{}|\\^~\[\]`]';
221 // -------------------------------------------------------------
222 function TextileThis($text, $lite='', $encode='', $noimage='', $strict='', $rel='')
225 $this->rel
= ' rel="'.$rel.'" ';
227 $text = $this->incomingEntities($text);
230 $text = str_replace("x%x%", "&", $text);
235 $text = $this->fixEntities($text);
236 $text = $this->cleanWhiteSpace($text);
239 $text = $this->getRefs($text);
241 $text = $this->noTextile($text);
242 $text = $this->links($text);
244 $text = $this->image($text);
246 $text = $this->code($text);
247 $text = $this->span($text);
248 $text = $this->footnoteRef($text);
249 $text = $this->glyphs($text);
250 $text = $this->retrieve($text);
253 $text = $this->lists($text);
254 $text = $this->table($text);
255 $text = $this->block($text);
258 // clean up <notextile>
259 $text = preg_replace('/<\/?notextile>/', "", $text);
261 // turn the temp char back to an ampersand entity
262 $text = str_replace("x%x%", "&", $text);
265 $text = str_replace("<br />", "<br />\n", $text);
271 // -------------------------------------------------------------
272 function pba($in, $element = "") // "parse block attributes"
284 if ($element == 'td') {
285 if (preg_match("/\\\\(\d+)/", $matched, $csp)) $colspan = $csp[1];
286 if (preg_match("/\/(\d+)/", $matched, $rsp)) $rowspan = $rsp[1];
288 if (preg_match("/($this->vlgn)/", $matched, $vert))
289 $style[] = "vertical-align:" . $this->vAlign($vert[1]) . ";";
292 if (preg_match("/\{([^}]*)\}/", $matched, $sty)) {
293 $style[] = rtrim($sty[1], ';') . ';';
294 $matched = str_replace($sty[0], '', $matched);
297 if (preg_match("/\[([^)]+)\]/U", $matched, $lng)) {
299 $matched = str_replace($lng[0], '', $matched);
302 if (preg_match("/\(([^()]+)\)/U", $matched, $cls)) {
304 $matched = str_replace($cls[0], '', $matched);
307 if (preg_match("/([(]+)/", $matched, $pl)) {
308 $style[] = "padding-left:" . strlen($pl[1]) . "em;";
309 $matched = str_replace($pl[0], '', $matched);
312 if (preg_match("/([)]+)/", $matched, $pr)) {
314 $style[] = "padding-right:" . strlen($pr[1]) . "em;";
315 $matched = str_replace($pr[0], '', $matched);
318 if (preg_match("/($this->hlgn)/", $matched, $horiz))
319 $style[] = "text-align:" . $this->hAlign($horiz[1]) . ";";
321 if (preg_match("/^(.*)#(.*)$/", $class, $ids)) {
326 return join('',array(
327 ($style) ?
' style="' . join("", $style) .'"':'',
328 ($class) ?
' class="' . $class .'"':'',
329 ($lang) ?
' lang="' . $lang .'"':'',
330 ($id) ?
' id="' . $id .'"':'',
331 ($colspan) ?
' colspan="' . $colspan .'"':'',
332 ($rowspan) ?
' rowspan="' . $rowspan .'"':''
338 // -------------------------------------------------------------
339 function table($text)
341 $text = $text . "\n\n";
342 return preg_replace_callback("/^(?:table(_?{$this->s}{$this->a}{$this->c})\. ?\n)?^({$this->a}{$this->c}\.? ?\|.*\|)\n\n/smU",
343 array(&$this, "fTable"), $text);
346 // -------------------------------------------------------------
347 function hasRawText($text)
349 // checks whether the text has text not already enclosed by a block tag
350 return '' != trim(preg_replace('@<(p|blockquote|div|form|table|ul|ol|pre|code|h\d)[^>]*?>.*</\1>@s', '', $text));
353 // -------------------------------------------------------------
354 function fTable($matches)
356 $tatts = $this->pba($matches[1], 'table');
358 foreach(preg_split("/\|$/m", $matches[2], -1, PREG_SPLIT_NO_EMPTY
) as $row) {
359 if (preg_match("/^($this->a$this->c\. )(.*)/m", $row, $rmtch)) {
360 $ratts = $this->pba($rmtch[1], 'tr');
364 foreach(explode("|", $row) as $cell) {
366 if (preg_match("/^_/", $cell)) $ctyp = "h";
367 if (preg_match("/^(_?$this->s$this->a$this->c\. )(.*)/", $cell, $cmtch)) {
368 $catts = $this->pba($cmtch[1], 'td');
372 $cell = $this->span($cell);
374 if (trim($cell) != '')
375 $cells[] = "\t\t\t<t$ctyp$catts>$cell</t$ctyp>";
377 $rows[] = "\t\t<tr$ratts>\n" . join("\n", $cells) . "\n\t\t</tr>";
378 unset($cells, $catts);
380 return "\t<table$tatts>\n" . join("\n", $rows) . "\n\t</table>\n\n";
383 // -------------------------------------------------------------
384 function lists($text)
386 return preg_replace_callback("/^([#*]+$this->c .*)$(?![^#*])/smU", array(&$this, "fList"), $text);
389 // -------------------------------------------------------------
392 $text = explode("\n", $m[0]);
393 foreach($text as $line) {
394 $nextline = next($text);
395 if (preg_match("/^([#*]+)($this->a$this->c) (.*)$/s", $line, $m)) {
396 list(, $tl, $atts, $content) = $m;
397 $nl = preg_replace("/^([#*]+)\s.*/", "$1", $nextline);
398 if (!isset($lists[$tl])) {
400 $atts = $this->pba($atts);
401 $line = "\t<" . $this->lT($tl) . "l$atts>\n\t\t<li>" . $content;
403 $line = "\t\t<li>" . $content;
406 if(strlen($nl) <= strlen($tl)) $line .= "</li>";
407 foreach(array_reverse($lists) as $k => $v) {
408 if(strlen($k) > strlen($nl)) {
409 $line .= "\n\t</" . $this->lT($k) . "l>";
418 return join("\n", $out);
421 // -------------------------------------------------------------
424 return preg_match("/^#+/", $in) ?
'o' : 'u';
427 // -------------------------------------------------------------
430 return preg_replace_callback('@<(p)([^>]*?)>(.*)(</\1>)@s', array(&$this, 'doBr'), $in);
433 // -------------------------------------------------------------
436 $content = preg_replace("@(.+)(?<!<br>|<br />)\n(?![#*\s|])@", '$1<br />', $m[3]);
437 return '<'.$m[1].$m[2].'>'.$content.$m[4];
440 // -------------------------------------------------------------
441 function block($text)
443 $pre = $php = $txp = false;
444 $find = array('bq', 'h[1-6]', 'fn\d+', 'p');
446 $text = explode("\n\n", $text);
447 array_push($text, " ");
449 foreach($text as $line) {
450 if (preg_match('/<pre>/i', $line)) {
453 elseif (preg_match('/<txp:php>/i', $line)) {
456 elseif (preg_match('/^\s*<txp:/i', $line)) {
460 foreach($find as $tag) {
461 $line = ($pre == false and $php == false and $txp == false)
462 ?
preg_replace_callback("/^($tag)($this->a$this->c)\.(?::(\S+))? (.*)$/s",
463 array(&$this, "fBlock"), $line)
467 $hasraw = $this->hasRawText($line);
468 if (!$php and !$txp and $hasraw)
469 $line = preg_replace('/^(?!\t|<\/?pre|<\/?code|$| )(.*)/s', "\t<p>$1</p>", $line);
471 $line = $this->doPBr($line);
472 $line = preg_replace('/<br>/', '<br />', $line);
474 if (preg_match('/<\/pre>/i', $line)) {
477 elseif (preg_match('/<\/txp:php>/i', $line)) {
480 if ($txp == true) $txp = false;
483 return join("\n", $out);
486 // -------------------------------------------------------------
490 list(, $tag, $atts, $cite, $content) = $m;
492 $atts = $this->pba($atts);
494 if (preg_match("/fn(\d+)/", $tag, $fns)) {
496 $fnid = empty($this->fn
[$fns[1]]) ?
$fns[1] : $this->fn
[$fns[1]];
497 $atts .= ' id="fn' . $fnid . '"';
498 $content = '<sup>' . $fns[1] . '</sup> ' . $content;
505 $cite = $this->checkRefs($cite);
506 $cite = ($cite != '') ?
' cite="' . $cite . '"' : '';
507 $start = "\t<blockquote$cite>\n\t\t<p";
508 $end = "</p>\n\t</blockquote>";
511 return "$start$atts>$content$end";
514 // -------------------------------------------------------------
517 $qtags = array('\*\*','\*','\?\?','-','__','_','%','\+','~','\^');
520 foreach($qtags as $f) {
521 $text = preg_replace_callback("/
522 (?:^|(?<=[\s>$pnct])|([{[]))
526 ([^\s$f]+|\S[^$f]*[^\s$f])
529 (?:$|([\]}])|(?=[[:punct:]]{1,2}|\s))
530 /x", array(&$this, "fSpan"), $text);
535 // -------------------------------------------------------------
551 list(,, $tag, $atts, $cite, $content, $end) = $m;
553 $atts = $this->pba($atts);
554 $atts .= ($cite != '') ?
'cite="' . $cite . '"' : '';
556 $out = "<$tag$atts>$content$end</$tag>";
558 // $this->dump($out);
564 // -------------------------------------------------------------
565 function links($text)
567 return preg_replace_callback('/
568 ([\s[{(]|[[:punct:]])? # $pre
570 (' . $this->c
. ') # $atts
573 (?:\(([^)]+)\)(?="))? # $title
575 ('.$this->urlch
.'+) # $url
579 /Ux', array(&$this, "fLink"), $text);
582 // -------------------------------------------------------------
585 list(, $pre, $atts, $text, $title, $url, $slash, $post) = $m;
587 $url = $this->checkRefs($url);
589 $atts = $this->pba($atts);
590 $atts .= ($title != '') ?
'title="' . htmlspecialchars($title) . '"' : '';
592 $atts = ($atts) ?
$this->shelve($atts) : '';
594 $url = $this->relURL($url);
596 $out = $pre . '<a href="' . $url . $slash . '"' . $atts . $this->rel
. '>' . $text . '</a>' . $post;
598 // $this->dump($out);
603 // -------------------------------------------------------------
604 function getRefs($text)
606 return preg_replace_callback("/(?<=^|\s)\[(.+)\]((?:http:\/\/|\/)\S+)(?=\s|$)/U",
607 array(&$this, "refs"), $text);
609 // -------------------------------------------------------------
613 list(, $flag, $url) = $m;
614 $this->urlrefs
[$flag] = $url;
618 // -------------------------------------------------------------
619 function checkRefs($text)
621 return (isset($this->urlrefs
[$text])) ?
$this->urlrefs
[$text] : $text;
624 // -------------------------------------------------------------
625 function relURL($url)
627 $parts = parse_url($url);
628 if ((empty($parts['scheme']) or @$parts['scheme'] == 'http') and
629 empty($parts['host']) and
630 preg_match('/^\w/', @$parts['path']))
635 // -------------------------------------------------------------
636 function image($text)
638 return preg_replace_callback("/
640 (\<|\=|\>)?? # optional alignment atts
641 ($this->c) # optional style,class atts
642 (?:\. )? # optional dot-space
643 ([^\s(!]+) # presume this is the src
645 (?:\(([^\)]+)\))? # optional title
647 (?::(\S+))? # optional href
648 (?=\s|$|[\]})]) # lookahead: space or end of string
649 /Ux", array(&$this, "fImage"), $text);
652 // -------------------------------------------------------------
655 list(, $algn, $atts, $url) = $m;
656 $atts = $this->pba($atts);
657 $atts .= ($algn != '') ?
' align="' . $this->iAlign($algn) . '"' : '';
658 $atts .= (isset($m[4])) ?
' title="' . $m[4] . '"' : '';
659 $atts .= (isset($m[4])) ?
' alt="' . $m[4] . '"' : ' alt=""';
660 $size = @getimagesize
($url);
661 if ($size) $atts .= " $size[3]";
663 $href = (isset($m[5])) ?
$this->checkRefs($m[5]) : '';
664 $url = $this->checkRefs($url);
666 $url = $this->relURL($url);
669 ($href) ?
'<a href="' . $href . '">' : '',
670 '<img src="' . $url . '"' . $atts . ' />',
671 ($href) ?
'</a>' : ''
674 return join('',$out);
677 // -------------------------------------------------------------
680 return preg_replace_callback("/
681 (?:^|(?<=[\s\(])|([[{])) # before
683 (?:\|(\w+)\|)? # lang
689 /Ux", array(&$this, "fCode"), $text);
692 // -------------------------------------------------------------
695 @list
(, $before, $lang, $code, $after) = $m;
696 $lang = ($lang) ?
' language="' . $lang . '"' : '';
697 return $before . '<code' . $lang . '>' . $code . '</code>' . $after;
700 // -------------------------------------------------------------
701 function shelve($val)
703 $this->shelf
[] = $val;
704 return ' <' . count($this->shelf
) . '>';
707 // -------------------------------------------------------------
708 function retrieve($text)
711 if (isset($this->shelf
) && is_array($this->shelf
)) {
712 foreach($this->shelf
as $r) {
714 $text = str_replace("<$i>", $r, $text);
720 // -------------------------------------------------------------
721 function incomingEntities($text)
723 return preg_replace("/&(?![#a-z0-9]+;)/i", "x%x%", $text);
726 // -------------------------------------------------------------
727 function encodeEntities($text)
729 return (function_exists('mb_encode_numericentity'))
730 ?
$this->encode_high($text)
731 : htmlentities($text, ENT_NOQUOTES
, "utf-8");
734 // -------------------------------------------------------------
735 function fixEntities($text)
737 /* de-entify any remaining angle brackets or ampersands */
738 return str_replace(array(">", "<", "&"),
739 array(">", "<", "&"), $text);
742 // -------------------------------------------------------------
743 function cleanWhiteSpace($text)
745 $out = str_replace(array("\r\n", "\t"), array("\n", ''), $text);
746 $out = preg_replace("/\n{3,}/", "\n\n", $out);
747 $out = preg_replace("/\n *\n/", "\n\n", $out);
748 $out = preg_replace('/"$/', "\" ", $out);
752 // -------------------------------------------------------------
753 function noTextile($text)
755 $text = preg_replace_callback('/(^|\s)<notextile>(.*)<\/notextile>(\s|$)?/msU',
756 array(&$this, "fTextile"), $text);
757 return preg_replace_callback('/(^|\s)==(.*)==(\s|$)?/msU',
758 array(&$this, "fTextile"), $text);
761 // -------------------------------------------------------------
762 function fTextile($m)
779 @list
(, $before, $notextile, $after) = $m;
780 $notextile = str_replace(array_keys($modifiers), array_values($modifiers), $notextile);
781 return $before . '<notextile>' . $notextile . '</notextile>' . $after;
784 // -------------------------------------------------------------
785 function footnoteRef($text)
787 return preg_replace('/\b\[([0-9]+)\](\s)?/Ue',
788 '$this->footnoteID(\'\1\',\'\2\')', $text);
791 // -------------------------------------------------------------
792 function footnoteID($id, $t)
794 if (empty($this->fn
[$id]))
795 $this->fn
[$id] = uniqid(rand());
796 $fnid = $this->fn
[$id];
797 return '<sup><a href="#fn'.$fnid.'">'.$id.'</a></sup>'.$t;
800 // -------------------------------------------------------------
801 function glyphs($text)
804 $text = preg_replace('/"\z/', "\" ", $text);
805 $pnc = '[[:punct:]]';
807 $glyph_search = array(
808 '/([^\s[{(>_*])?\'(?(1)|(?=\s|s\b|'.$pnc.'))/', // single closing
809 '/\'/', // single opening
810 '/([^\s[{(>_*])?"(?(1)|(?=\s|'.$pnc.'))/', // double closing
811 '/"/', // double opening
812 '/\b( )?\.{3}/', // ellipsis
813 '/\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])/', // 3+ uppercase acronym
814 '/\s?--\s?/', // em dash
815 '/\s-\s/', // en dash
816 '/(\d+) ?x ?(\d+)/', // dimension sign
817 '/\b ?[([]TM[])]/i', // trademark
818 '/\b ?[([]R[])]/i', // registered
819 '/\b ?[([]C[])]/i'); // copyright
821 $glyph_replace = array('$1’$2', // single closing
822 '‘', // single opening
823 '$1”', // double closing
824 '“', // double opening
825 '$1…', // ellipsis
826 '<acronym title="$2">$1</acronym>', // 3+ uppercase acronym
827 '—', // em dash
828 ' – ', // en dash
829 '$1×$2', // dimension sign
830 '™', // trademark
831 '®', // registered
832 '©'); // copyright
835 /* if no html, do a simple search and replace... */
836 if (!preg_match("/<.*>/", $text)) {
837 $text = preg_replace($glyph_search, $glyph_replace, $text);
841 $text = preg_split("/(<.*>)/U", $text, -1, PREG_SPLIT_DELIM_CAPTURE
);
842 foreach($text as $line) {
843 $offtags = ('code|pre|kbd|notextile|txp:php');
845 /* matches are off if we're between <code>, <pre> etc. */
846 if (preg_match('/<(' . $offtags . ')>/i', $line)) $codepre = true;
847 if (preg_match('/<\/(' . $offtags . ')>/i', $line)) $codepre = false;
849 if (!preg_match("/<.*>/", $line) && $codepre == false) {
850 $line = preg_replace($glyph_search, $glyph_replace, $line);
853 /* do htmlspecial if between <code> */
854 if ($codepre == true) {
855 $line = htmlspecialchars($line, ENT_NOQUOTES
, "UTF-8");
856 $line = preg_replace('/<(\/?' . $offtags . ')>/', "<$1>", $line);
857 $line = str_replace("&#","&#",$line);
860 $glyph_out[] = $line;
862 return join('', $glyph_out);
866 // -------------------------------------------------------------
873 return (isset($vals[$in])) ?
$vals[$in] : '';
876 // -------------------------------------------------------------
884 return (isset($vals[$in])) ?
$vals[$in] : '';
887 // -------------------------------------------------------------
894 return (isset($vals[$in])) ?
$vals[$in] : '';
897 // -------------------------------------------------------------
898 function encode_high($text, $charset = "UTF-8")
900 return mb_encode_numericentity($text, $this->cmap(), $charset);
903 // -------------------------------------------------------------
904 function decode_high($text, $charset = "UTF-8")
906 return mb_decode_numericentity($text, $this->cmap(), $charset);
909 // -------------------------------------------------------------
914 0x0080, 0xffff, 0, $f);
918 // -------------------------------------------------------------
919 function textile_popup_help($name, $helpvar, $windowW, $windowH)
921 return ' <a target="_blank" href="http://www.textpattern.com/help/?item=' . $helpvar . '" onclick="window.open(this.href, \'popupwindow\', \'width=' . $windowW . ',height=' . $windowH . ',scrollbars,resizable\'); return false;">' . $name . '</a><br />';
926 // -------------------------------------------------------------
927 function txtgps($thing)
929 if (isset($_POST[$thing])) {
930 if (get_magic_quotes_gpc()) {
931 return stripslashes($_POST[$thing]);
934 return $_POST[$thing];
942 // -------------------------------------------------------------
945 foreach (func_get_args() as $a)
946 echo "\n<pre>",(is_array($a)) ?
print_r($a) : $a, "</pre>\n";
949 // -------------------------------------------------------------
950 function blockLite($text)
952 $find = array('bq', 'p');
954 $text = preg_replace("/(.+)\n(?![#*\s|])/",
957 $text = explode("\n", $text);
958 array_push($text, " ");
960 foreach($text as $line) {
962 foreach($find as $tag) {
963 $line = preg_replace_callback("/^($tag)($this->a$this->c)\.(?::(\S+))? (.*)$/",
964 array(&$this, "fBlock"), $line);
967 $line = preg_replace('/^(?!\t|<\/?pre|<\/?code|$| )(.*)/', "\t<p>$1</p>", $line);
970 return join("\n", $out);