Fixing file upload params ($_FILES) normalization. Closes #75
[akelos.git] / vendor / TextParsers / Textile.php
blobf7e80c874854dfec2b6863139562847990732efe
1 <?php
3 /**
4 * Example: get XHTML from a given Textile-markup string ($string)
6 * $textile = new Textile;
7 * echo $textile->TextileThis($string);
9 */
12 $HeadURL$
13 $LastChangedRevision$
18 _____________
19 T E X T I L E
21 A Humane Web Text Generator
23 Version 2.0 beta
25 Copyright (c) 2003-2004, Dean Allen <dean@textism.com>
26 All rights reserved.
28 Thanks to Carlo Zottmann <carlo@g-blog.net> for refactoring
29 Textile's procedural code into a class framework
31 _____________
32 L I C E N S E
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.
60 _________
61 U S A G E
63 Block modifier syntax:
65 Header: h(1-6).
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>
72 Blockquote: bq.
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>
79 Footnote: fn(1-100).
80 Example: fn1. Footnote... -> <p id="fn1">Footnote...</p>
82 Numeric list: #, ##
83 Consecutive paragraphs beginning with # are wrapped in ordered list tags.
84 Example: <ol><li>ordered list</li></ol>
86 Bulleted list: *, **
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>
116 Table syntax:
118 Simple tables:
120 |a|simple|table|row|
121 |And|Another|table|row|
123 |_. A|_. table|_. header|_.row|
124 |A|simple|table|row|
126 Tables with attributes:
128 table{border:1px solid black}.
129 {background:#ddd;color:red}. |{}| | | |
132 Applying Attributes:
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">
176 # big <li>one</li>
177 # list <li>big</li>
178 <li>list</li>
179 </ol>
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>
188 class Textile
190 var $hlgn;
191 var $vlgn;
192 var $clas;
193 var $lnge;
194 var $styl;
195 var $cspn;
196 var $rspn;
197 var $a;
198 var $s;
199 var $c;
200 var $pnct;
201 var $rel;
202 var $fn;
204 // -------------------------------------------------------------
205 function Textile()
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='')
224 if ($rel)
225 $this->rel = ' rel="'.$rel.'" ';
227 $text = $this->incomingEntities($text);
229 if ($encode) {
230 $text = str_replace("x%x%", "&#38;", $text);
231 return $text;
232 } else {
234 if(!$strict) {
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);
243 if (!$noimage) {
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);
252 if (!$lite) {
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%", "&#38;", $text);
264 // just to be tidy
265 $text = str_replace("<br />", "<br />\n", $text);
267 return $text;
271 // -------------------------------------------------------------
272 function pba($in, $element = "") // "parse block attributes"
274 $style = '';
275 $class = '';
276 $lang = '';
277 $colspan = '';
278 $rowspan = '';
279 $id = '';
280 $atts = '';
282 if (!empty($in)) {
283 $matched = $in;
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)) {
298 $lang = $lng[1];
299 $matched = str_replace($lng[0], '', $matched);
302 if (preg_match("/\(([^()]+)\)/U", $matched, $cls)) {
303 $class = $cls[1];
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)) {
313 // $this->dump($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)) {
322 $id = $ids[2];
323 $class = $ids[1];
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 .'"':''
335 return '';
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');
361 $row = $rmtch[2];
362 } else $ratts = '';
364 foreach(explode("|", $row) as $cell) {
365 $ctyp = "d";
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');
369 $cell = $cmtch[2];
370 } else $catts = '';
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 // -------------------------------------------------------------
390 function fList($m)
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])) {
399 $lists[$tl] = true;
400 $atts = $this->pba($atts);
401 $line = "\t<" . $this->lT($tl) . "l$atts>\n\t\t<li>" . $content;
402 } else {
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>";
410 if(strlen($k) > 1)
411 $line .= "</li>";
412 unset($lists[$k]);
416 $out[] = $line;
418 return join("\n", $out);
421 // -------------------------------------------------------------
422 function lT($in)
424 return preg_match("/^#+/", $in) ? 'o' : 'u';
427 // -------------------------------------------------------------
428 function doPBr($in)
430 return preg_replace_callback('@<(p)([^>]*?)>(.*)(</\1>)@s', array(&$this, 'doBr'), $in);
433 // -------------------------------------------------------------
434 function doBr($m)
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)) {
451 $pre = true;
453 elseif (preg_match('/<txp:php>/i', $line)) {
454 $php = true;
456 elseif (preg_match('/^\s*<txp:/i', $line)) {
457 $txp = true;
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)
464 : $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)) {
475 $pre = false;
477 elseif (preg_match('/<\/txp:php>/i', $line)) {
478 $php = false;
480 if ($txp == true) $txp = false;
481 $out[] = $line;
483 return join("\n", $out);
486 // -------------------------------------------------------------
487 function fBlock($m)
489 // $this->dump($m);
490 list(, $tag, $atts, $cite, $content) = $m;
492 $atts = $this->pba($atts);
494 if (preg_match("/fn(\d+)/", $tag, $fns)) {
495 $tag = 'p';
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;
501 $start = "\t<$tag";
502 $end = "</$tag>";
504 if ($tag == "bq") {
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 // -------------------------------------------------------------
515 function span($text)
517 $qtags = array('\*\*','\*','\?\?','-','__','_','%','\+','~','\^');
518 $pnct = ".,\"'?!;:";
520 foreach($qtags as $f) {
521 $text = preg_replace_callback("/
522 (?:^|(?<=[\s>$pnct])|([{[]))
523 ($f)
524 ($this->c)
525 (?::(\S+))?
526 ([^\s$f]+|\S[^$f]*[^\s$f])
527 ([$pnct]*)
529 (?:$|([\]}])|(?=[[:punct:]]{1,2}|\s))
530 /x", array(&$this, "fSpan"), $text);
532 return $text;
535 // -------------------------------------------------------------
536 function fSpan($m)
538 $qtags = array(
539 '*' => 'strong',
540 '**' => 'b',
541 '??' => 'cite',
542 '_' => 'em',
543 '__' => 'i',
544 '-' => 'del',
545 '%' => 'span',
546 '+' => 'ins',
547 '~' => 'sub',
548 '^' => 'sup',
551 list(,, $tag, $atts, $cite, $content, $end) = $m;
552 $tag = $qtags[$tag];
553 $atts = $this->pba($atts);
554 $atts .= ($cite != '') ? 'cite="' . $cite . '"' : '';
556 $out = "<$tag$atts>$content$end</$tag>";
558 // $this->dump($out);
560 return $out;
564 // -------------------------------------------------------------
565 function links($text)
567 return preg_replace_callback('/
568 ([\s[{(]|[[:punct:]])? # $pre
569 " # start
570 (' . $this->c . ') # $atts
571 ([^"]+) # $text
573 (?:\(([^)]+)\)(?="))? # $title
575 ('.$this->urlch.'+) # $url
576 (\/)? # $slash
577 ([^\w\/;]*) # $post
578 (?=\s|$)
579 /Ux', array(&$this, "fLink"), $text);
582 // -------------------------------------------------------------
583 function fLink($m)
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);
599 return $out;
603 // -------------------------------------------------------------
604 function getRefs($text)
606 return preg_replace_callback("/(?<=^|\s)\[(.+)\]((?:http:\/\/|\/)\S+)(?=\s|$)/U",
607 array(&$this, "refs"), $text);
609 // -------------------------------------------------------------
611 function refs($m)
613 list(, $flag, $url) = $m;
614 $this->urlrefs[$flag] = $url;
615 return '';
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']))
631 $url = hu.$url;
632 return $url;
635 // -------------------------------------------------------------
636 function image($text)
638 return preg_replace_callback("/
639 \! # opening !
640 (\<|\=|\>)?? # optional alignment atts
641 ($this->c) # optional style,class atts
642 (?:\. )? # optional dot-space
643 ([^\s(!]+) # presume this is the src
644 \s? # optional space
645 (?:\(([^\)]+)\))? # optional title
646 \! # closing
647 (?::(\S+))? # optional href
648 (?=\s|$|[\]})]) # lookahead: space or end of string
649 /Ux", array(&$this, "fImage"), $text);
652 // -------------------------------------------------------------
653 function fImage($m)
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);
668 $out = array(
669 ($href) ? '<a href="' . $href . '">' : '',
670 '<img src="' . $url . '"' . $atts . ' />',
671 ($href) ? '</a>' : ''
674 return join('',$out);
677 // -------------------------------------------------------------
678 function code($text)
680 return preg_replace_callback("/
681 (?:^|(?<=[\s\(])|([[{])) # before
683 (?:\|(\w+)\|)? # lang
684 (.+) # code
686 (?:$|([\]}])|
687 (?=[[:punct:]]{1,2}|
688 \s|$)) # after
689 /Ux", array(&$this, "fCode"), $text);
692 // -------------------------------------------------------------
693 function fCode($m)
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)
710 $i = 0;
711 if (isset($this->shelf) && is_array($this->shelf)) {
712 foreach($this->shelf as $r) {
713 $i++;
714 $text = str_replace("<$i>", $r, $text);
717 return $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("&gt;", "&lt;", "&amp;"),
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);
749 return $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)
764 $modifiers = array(
765 '"' => '&#34;',
766 '%' => '&#37;',
767 '*' => '&#42;',
768 '+' => '&#43;',
769 '-' => '&#45;',
770 '<' => '&#60;',
771 '=' => '&#61;',
772 '>' => '&#62;',
773 '?' => '&#63;',
774 '^' => '&#94;',
775 '_' => '&#95;',
776 '~' => '&#126;',
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)
803 // fix: hackish
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&#8217;$2', // single closing
822 '&#8216;', // single opening
823 '$1&#8221;', // double closing
824 '&#8220;', // double opening
825 '$1&#8230;', // ellipsis
826 '<acronym title="$2">$1</acronym>', // 3+ uppercase acronym
827 '&#8212;', // em dash
828 ' &#8211; ', // en dash
829 '$1&#215;$2', // dimension sign
830 '&#8482;', // trademark
831 '&#174;', // registered
832 '&#169;'); // copyright
834 $codepre = false;
835 /* if no html, do a simple search and replace... */
836 if (!preg_match("/<.*>/", $text)) {
837 $text = preg_replace($glyph_search, $glyph_replace, $text);
838 return $text;
840 else {
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('/&lt;(\/?' . $offtags . ')&gt;/', "<$1>", $line);
857 $line = str_replace("&amp;#","&#",$line);
860 $glyph_out[] = $line;
862 return join('', $glyph_out);
866 // -------------------------------------------------------------
867 function iAlign($in)
869 $vals = array(
870 '<' => 'left',
871 '=' => 'center',
872 '>' => 'right');
873 return (isset($vals[$in])) ? $vals[$in] : '';
876 // -------------------------------------------------------------
877 function hAlign($in)
879 $vals = array(
880 '<' => 'left',
881 '=' => 'center',
882 '>' => 'right',
883 '<>' => 'justify');
884 return (isset($vals[$in])) ? $vals[$in] : '';
887 // -------------------------------------------------------------
888 function vAlign($in)
890 $vals = array(
891 '^' => 'top',
892 '-' => 'middle',
893 '~' => 'bottom');
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 // -------------------------------------------------------------
910 function cmap()
912 $f = 0xffff;
913 $cmap = array(
914 0x0080, 0xffff, 0, $f);
915 return $cmap;
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 />';
923 return $out;
926 // -------------------------------------------------------------
927 function txtgps($thing)
929 if (isset($_POST[$thing])) {
930 if (get_magic_quotes_gpc()) {
931 return stripslashes($_POST[$thing]);
933 else {
934 return $_POST[$thing];
937 else {
938 return '';
942 // -------------------------------------------------------------
943 function dump()
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|])/",
955 "$1<br />", $text);
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);
968 $out[] = $line;
970 return join("\n", $out);
974 } // end class