[ Index ] |
PHP Cross Reference of Textpattern 4.0.8 |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * Example: get XHTML from a given Textile-markup string ($string) 5 * 6 * $textile = new Textile; 7 * echo $textile->TextileThis($string); 8 * 9 */ 10 11 /* 12 $HeadURL: https://textpattern.googlecode.com/svn/releases/4.0.8/source/textpattern/lib/classTextile.php $ 13 $LastChangedRevision: 2812 $ 14 */ 15 16 /* 17 18 _____________ 19 T E X T I L E 20 21 A Humane Web Text Generator 22 23 Version 2.0 24 25 Copyright (c) 2003-2004, Dean Allen <dean@textism.com> 26 All rights reserved. 27 28 Thanks to Carlo Zottmann <carlo@g-blog.net> for refactoring 29 Textile's procedural code into a class framework 30 31 Additions and fixes Copyright (c) 2006 Alex Shiels http://thresholdstate.com/ 32 33 _____________ 34 L I C E N S E 35 36 Redistribution and use in source and binary forms, with or without 37 modification, are permitted provided that the following conditions are met: 38 39 * Redistributions of source code must retain the above copyright notice, 40 this list of conditions and the following disclaimer. 41 42 * Redistributions in binary form must reproduce the above copyright notice, 43 this list of conditions and the following disclaimer in the documentation 44 and/or other materials provided with the distribution. 45 46 * Neither the name Textile nor the names of its contributors may be used to 47 endorse or promote products derived from this software without specific 48 prior written permission. 49 50 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 51 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 52 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 53 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 54 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 55 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 56 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 57 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 58 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 59 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 60 POSSIBILITY OF SUCH DAMAGE. 61 62 _________ 63 U S A G E 64 65 Block modifier syntax: 66 67 Header: h(1-6). 68 Paragraphs beginning with 'hn. ' (where n is 1-6) are wrapped in header tags. 69 Example: h1. Header... -> <h1>Header...</h1> 70 71 Paragraph: p. (also applied by default) 72 Example: p. Text -> <p>Text</p> 73 74 Blockquote: bq. 75 Example: bq. Block quotation... -> <blockquote>Block quotation...</blockquote> 76 77 Blockquote with citation: bq.:http://citation.url 78 Example: bq.:http://textism.com/ Text... 79 -> <blockquote cite="http://textism.com">Text...</blockquote> 80 81 Footnote: fn(1-100). 82 Example: fn1. Footnote... -> <p id="fn1">Footnote...</p> 83 84 Numeric list: #, ## 85 Consecutive paragraphs beginning with # are wrapped in ordered list tags. 86 Example: <ol><li>ordered list</li></ol> 87 88 Bulleted list: *, ** 89 Consecutive paragraphs beginning with * are wrapped in unordered list tags. 90 Example: <ul><li>unordered list</li></ul> 91 92 Phrase modifier syntax: 93 94 _emphasis_ -> <em>emphasis</em> 95 __italic__ -> <i>italic</i> 96 *strong* -> <strong>strong</strong> 97 **bold** -> <b>bold</b> 98 ??citation?? -> <cite>citation</cite> 99 -deleted text- -> <del>deleted</del> 100 +inserted text+ -> <ins>inserted</ins> 101 ^superscript^ -> <sup>superscript</sup> 102 ~subscript~ -> <sub>subscript</sub> 103 @code@ -> <code>computer code</code> 104 %(bob)span% -> <span class="bob">span</span> 105 106 ==notextile== -> leave text alone (do not format) 107 108 "linktext":url -> <a href="url">linktext</a> 109 "linktext(title)":url -> <a href="url" title="title">linktext</a> 110 111 !imageurl! -> <img src="imageurl" /> 112 !imageurl(alt text)! -> <img src="imageurl" alt="alt text" /> 113 !imageurl!:linkurl -> <a href="linkurl"><img src="imageurl" /></a> 114 115 ABC(Always Be Closing) -> <acronym title="Always Be Closing">ABC</acronym> 116 117 118 Table syntax: 119 120 Simple tables: 121 122 |a|simple|table|row| 123 |And|Another|table|row| 124 125 |_. A|_. table|_. header|_.row| 126 |A|simple|table|row| 127 128 Tables with attributes: 129 130 table{border:1px solid black}. 131 {background:#ddd;color:red}. |{}| | | | 132 133 134 Applying Attributes: 135 136 Most anywhere Textile code is used, attributes such as arbitrary css style, 137 css classes, and ids can be applied. The syntax is fairly consistent. 138 139 The following characters quickly alter the alignment of block elements: 140 141 < -> left align ex. p<. left-aligned para 142 > -> right align h3>. right-aligned header 3 143 = -> centred h4=. centred header 4 144 <> -> justified p<>. justified paragraph 145 146 These will change vertical alignment in table cells: 147 148 ^ -> top ex. |^. top-aligned table cell| 149 - -> middle |-. middle aligned| 150 ~ -> bottom |~. bottom aligned cell| 151 152 Plain (parentheses) inserted between block syntax and the closing dot-space 153 indicate classes and ids: 154 155 p(hector). paragraph -> <p class="hector">paragraph</p> 156 157 p(#fluid). paragraph -> <p id="fluid">paragraph</p> 158 159 (classes and ids can be combined) 160 p(hector#fluid). paragraph -> <p class="hector" id="fluid">paragraph</p> 161 162 Curly {brackets} insert arbitrary css style 163 164 p{line-height:18px}. paragraph -> <p style="line-height:18px">paragraph</p> 165 166 h3{color:red}. header 3 -> <h3 style="color:red">header 3</h3> 167 168 Square [brackets] insert language attributes 169 170 p[no]. paragraph -> <p lang="no">paragraph</p> 171 172 %[fr]phrase% -> <span lang="fr">phrase</span> 173 174 Usually Textile block element syntax requires a dot and space before the block 175 begins, but since lists don't, they can be styled just using braces 176 177 #{color:blue} one -> <ol style="color:blue"> 178 # big <li>one</li> 179 # list <li>big</li> 180 <li>list</li> 181 </ol> 182 183 Using the span tag to style a phrase 184 185 It goes like this, %{color:red}the fourth the fifth% 186 -> It goes like this, <span style="color:red">the fourth the fifth</span> 187 188 */ 189 190 // define these before including this file to override the standard glyphs 191 @define('txt_quote_single_open', '‘'); 192 @define('txt_quote_single_close', '’'); 193 @define('txt_quote_double_open', '“'); 194 @define('txt_quote_double_close', '”'); 195 @define('txt_apostrophe', '’'); 196 @define('txt_prime', '′'); 197 @define('txt_prime_double', '″'); 198 @define('txt_ellipsis', '…'); 199 @define('txt_emdash', '—'); 200 @define('txt_endash', '–'); 201 @define('txt_dimension', '×'); 202 @define('txt_trademark', '™'); 203 @define('txt_registered', '®'); 204 @define('txt_copyright', '©'); 205 206 class Textile 207 { 208 var $hlgn; 209 var $vlgn; 210 var $clas; 211 var $lnge; 212 var $styl; 213 var $cspn; 214 var $rspn; 215 var $a; 216 var $s; 217 var $c; 218 var $pnct; 219 var $rel; 220 var $fn; 221 222 var $shelf = array(); 223 var $restricted = false; 224 var $noimage = false; 225 var $lite = false; 226 var $url_schemes = array(); 227 var $glyph = array(); 228 var $hu = ''; 229 230 var $ver = '2.0.0'; 231 var $rev = '$Rev: 2812 $'; 232 233 var $doc_root; 234 235 // ------------------------------------------------------------- 236 function Textile() 237 { 238 $this->hlgn = "(?:\<(?!>)|(?<!<)\>|\<\>|\=|[()]+(?! ))"; 239 $this->vlgn = "[\-^~]"; 240 $this->clas = "(?:\([^)]+\))"; 241 $this->lnge = "(?:\[[^]]+\])"; 242 $this->styl = "(?:\{[^}]+\})"; 243 $this->cspn = "(?:\\\\\d+)"; 244 $this->rspn = "(?:\/\d+)"; 245 $this->a = "(?:{$this->hlgn}|{$this->vlgn})*"; 246 $this->s = "(?:{$this->cspn}|{$this->rspn})*"; 247 $this->c = "(?:{$this->clas}|{$this->styl}|{$this->lnge}|{$this->hlgn})*"; 248 249 $this->pnct = '[\!"#\$%&\'()\*\+,\-\./:;<=>\?@\[\\\]\^_`{\|}\~]'; 250 $this->urlch = '[\w"$\-_.+!*\'(),";\/?:@=&%#{}|\\^~\[\]`]'; 251 252 $this->url_schemes = array('http','https','ftp','mailto'); 253 254 $this->btag = array('bq', 'bc', 'notextile', 'pre', 'h[1-6]', 'fn\d+', 'p'); 255 256 $this->glyph = array( 257 'quote_single_open' => txt_quote_single_open, 258 'quote_single_close' => txt_quote_single_close, 259 'quote_double_open' => txt_quote_double_open, 260 'quote_double_close' => txt_quote_double_close, 261 'apostrophe' => txt_apostrophe, 262 'prime' => txt_prime, 263 'prime_double' => txt_prime_double, 264 'ellipsis' => txt_ellipsis, 265 'emdash' => txt_emdash, 266 'endash' => txt_endash, 267 'dimension' => txt_dimension, 268 'trademark' => txt_trademark, 269 'registered' => txt_registered, 270 'copyright' => txt_copyright, 271 ); 272 273 if (defined('hu')) 274 $this->hu = hu; 275 276 if (defined('DIRECTORY_SEPARATOR')) 277 $this->ds = constant('DIRECTORY_SEPARATOR'); 278 else 279 $this->ds = '/'; 280 281 $this->doc_root = @$_SERVER['DOCUMENT_ROOT']; 282 if (!$this->doc_root) 283 $this->doc_root = @$_SERVER['PATH_TRANSLATED']; // IIS 284 285 $this->doc_root = rtrim($this->doc_root, $this->ds).$this->ds; 286 287 } 288 289 // ------------------------------------------------------------- 290 291 function TextileThis($text, $lite = '', $encode = '', $noimage = '', $strict = '', $rel = '') 292 { 293 $this->rel = ($rel) ? ' rel="'.$rel.'"' : ''; 294 295 $this->lite = $lite; 296 $this->noimage = $noimage; 297 298 if ($encode) { 299 $text = $this->incomingEntities($text); 300 $text = str_replace("x%x%", "&", $text); 301 return $text; 302 } else { 303 304 if(!$strict) { 305 $text = $this->cleanWhiteSpace($text); 306 } 307 308 if (!$lite) { 309 $text = $this->block($text); 310 } 311 312 $text = $this->retrieve($text); 313 $text = $this->retrieveURLs($text); 314 315 // just to be tidy 316 $text = str_replace("<br />", "<br />\n", $text); 317 318 return $text; 319 } 320 } 321 322 // ------------------------------------------------------------- 323 324 function TextileRestricted($text, $lite = 1, $noimage = 1, $rel = 'nofollow') 325 { 326 $this->restricted = true; 327 $this->lite = $lite; 328 $this->noimage = $noimage; 329 330 $this->rel = ($rel) ? ' rel="'.$rel.'"' : ''; 331 332 // escape any raw html 333 $text = $this->encode_html($text, 0); 334 335 $text = $this->cleanWhiteSpace($text); 336 337 if ($lite) { 338 $text = $this->blockLite($text); 339 } 340 else { 341 $text = $this->block($text); 342 } 343 344 $text = $this->retrieve($text); 345 $text = $this->retrieveURLs($text); 346 347 // just to be tidy 348 $text = str_replace("<br />", "<br />\n", $text); 349 350 return $text; 351 } 352 353 // ------------------------------------------------------------- 354 function pba($in, $element = "", $include_id = 1) // "parse block attributes" 355 { 356 $style = ''; 357 $class = ''; 358 $lang = ''; 359 $colspan = ''; 360 $rowspan = ''; 361 $id = ''; 362 $atts = ''; 363 364 if (!empty($in)) { 365 $matched = $in; 366 if ($element == 'td') { 367 if (preg_match("/\\\\(\d+)/", $matched, $csp)) $colspan = $csp[1]; 368 if (preg_match("/\/(\d+)/", $matched, $rsp)) $rowspan = $rsp[1]; 369 } 370 371 if ($element == 'td' or $element == 'tr') { 372 if (preg_match("/($this->vlgn)/", $matched, $vert)) 373 $style[] = "vertical-align:" . $this->vAlign($vert[1]) . ";"; 374 } 375 376 if (preg_match("/\{([^}]*)\}/", $matched, $sty)) { 377 $style[] = rtrim($sty[1], ';') . ';'; 378 $matched = str_replace($sty[0], '', $matched); 379 } 380 381 if (preg_match("/\[([^]]+)\]/U", $matched, $lng)) { 382 $lang = $lng[1]; 383 $matched = str_replace($lng[0], '', $matched); 384 } 385 386 if (preg_match("/\(([^()]+)\)/U", $matched, $cls)) { 387 $class = $cls[1]; 388 $matched = str_replace($cls[0], '', $matched); 389 } 390 391 if (preg_match("/([(]+)/", $matched, $pl)) { 392 $style[] = "padding-left:" . strlen($pl[1]) . "em;"; 393 $matched = str_replace($pl[0], '', $matched); 394 } 395 396 if (preg_match("/([)]+)/", $matched, $pr)) { 397 // $this->dump($pr); 398 $style[] = "padding-right:" . strlen($pr[1]) . "em;"; 399 $matched = str_replace($pr[0], '', $matched); 400 } 401 402 if (preg_match("/($this->hlgn)/", $matched, $horiz)) 403 $style[] = "text-align:" . $this->hAlign($horiz[1]) . ";"; 404 405 if (preg_match("/^(.*)#(.*)$/", $class, $ids)) { 406 $id = $ids[2]; 407 $class = $ids[1]; 408 } 409 410 if ($this->restricted) 411 return ($lang) ? ' lang="' . $lang .'"':''; 412 413 return join('',array( 414 ($style) ? ' style="' . join("", $style) .'"':'', 415 ($class) ? ' class="' . $class .'"':'', 416 ($lang) ? ' lang="' . $lang .'"':'', 417 ($id and $include_id) ? ' id="' . $id .'"':'', 418 ($colspan) ? ' colspan="' . $colspan .'"':'', 419 ($rowspan) ? ' rowspan="' . $rowspan .'"':'' 420 )); 421 } 422 return ''; 423 } 424 425 // ------------------------------------------------------------- 426 function hasRawText($text) 427 { 428 // checks whether the text has text not already enclosed by a block tag 429 $r = trim(preg_replace('@<(p|blockquote|div|form|table|ul|ol|pre|h\d)[^>]*?>.*</\1>@s', '', trim($text))); 430 $r = trim(preg_replace('@<(hr|br)[^>]*?/>@', '', $r)); 431 return '' != $r; 432 } 433 434 // ------------------------------------------------------------- 435 function table($text) 436 { 437 $text = $text . "\n\n"; 438 return preg_replace_callback("/^(?:table(_?{$this->s}{$this->a}{$this->c})\. ?\n)?^({$this->a}{$this->c}\.? ?\|.*\|)\n\n/smU", 439 array(&$this, "fTable"), $text); 440 } 441 442 // ------------------------------------------------------------- 443 function fTable($matches) 444 { 445 $tatts = $this->pba($matches[1], 'table'); 446 447 foreach(preg_split("/\|$/m", $matches[2], -1, PREG_SPLIT_NO_EMPTY) as $row) { 448 if (preg_match("/^($this->a$this->c\. )(.*)/m", ltrim($row), $rmtch)) { 449 $ratts = $this->pba($rmtch[1], 'tr'); 450 $row = $rmtch[2]; 451 } else $ratts = ''; 452 453 $cells = array(); 454 foreach(explode("|", $row) as $cell) { 455 $ctyp = "d"; 456 if (preg_match("/^_/", $cell)) $ctyp = "h"; 457 if (preg_match("/^(_?$this->s$this->a$this->c\. )(.*)/", $cell, $cmtch)) { 458 $catts = $this->pba($cmtch[1], 'td'); 459 $cell = $cmtch[2]; 460 } else $catts = ''; 461 462 $cell = $this->graf($cell); 463 464 if (trim($cell) != '') 465 $cells[] = $this->doTagBr("t$ctyp", "\t\t\t<t$ctyp$catts>$cell</t$ctyp>"); 466 } 467 $rows[] = "\t\t<tr$ratts>\n" . join("\n", $cells) . ($cells ? "\n" : "") . "\t\t</tr>"; 468 unset($cells, $catts); 469 } 470 return "\t<table$tatts>\n" . join("\n", $rows) . "\n\t</table>\n\n"; 471 } 472 473 // ------------------------------------------------------------- 474 function lists($text) 475 { 476 return preg_replace_callback("/^([#*]+$this->c .*)$(?![^#*])/smU", array(&$this, "fList"), $text); 477 } 478 479 // ------------------------------------------------------------- 480 function fList($m) 481 { 482 $text = preg_split('/\n(?=[*#])/m', $m[0]); 483 foreach($text as $nr => $line) { 484 $nextline = isset($text[$nr+1]) ? $text[$nr+1] : false; 485 if (preg_match("/^([#*]+)($this->a$this->c) (.*)$/s", $line, $m)) { 486 list(, $tl, $atts, $content) = $m; 487 $nl = ''; 488 if (preg_match("/^([#*]+)\s.*/", $nextline, $nm)) 489 $nl = $nm[1]; 490 if (!isset($lists[$tl])) { 491 $lists[$tl] = true; 492 $atts = $this->pba($atts); 493 $line = "\t<" . $this->lT($tl) . "l$atts>\n\t\t<li>" . rtrim($content); 494 } else { 495 $line = "\t\t<li>" . rtrim($content); 496 } 497 498 if(strlen($nl) <= strlen($tl)) $line .= "</li>"; 499 foreach(array_reverse($lists) as $k => $v) { 500 if(strlen($k) > strlen($nl)) { 501 $line .= "\n\t</" . $this->lT($k) . "l>"; 502 if(strlen($k) > 1) 503 $line .= "</li>"; 504 unset($lists[$k]); 505 } 506 } 507 } 508 else { 509 $line .= "\n"; 510 } 511 $out[] = $line; 512 } 513 return $this->doTagBr('li', join("\n", $out)); 514 } 515 516 // ------------------------------------------------------------- 517 function lT($in) 518 { 519 return preg_match("/^#+/", $in) ? 'o' : 'u'; 520 } 521 522 // ------------------------------------------------------------- 523 function doTagBr($tag, $in) 524 { 525 return preg_replace_callback('@<('.preg_quote($tag).')([^>]*?)>(.*)(</\1>)@s', array(&$this, 'doBr'), $in); 526 } 527 528 529 // ------------------------------------------------------------- 530 function doPBr($in) 531 { 532 return $this->doTagBr('p', $in); 533 } 534 535 // ------------------------------------------------------------- 536 function doBr($m) 537 { 538 $content = preg_replace("@(.+)(?<!<br>|<br />)\n(?![#*\s|])@", '$1<br />', $m[3]); 539 return '<'.$m[1].$m[2].'>'.$content.$m[4]; 540 } 541 542 // ------------------------------------------------------------- 543 function block($text) 544 { 545 $find = $this->btag; 546 $tre = join('|', $find); 547 548 $text = explode("\n\n", $text); 549 550 $tag = 'p'; 551 $atts = $cite = $graf = $ext = ''; 552 553 foreach($text as $line) { 554 $anon = 0; 555 if (preg_match("/^($tre)($this->a$this->c)\.(\.?)(?::(\S+))? (.*)$/s", $line, $m)) { 556 // last block was extended, so close it 557 if ($ext) 558 $out[count($out)-1] .= $c1; 559 // new block 560 list(,$tag,$atts,$ext,$cite,$graf) = $m; 561 list($o1, $o2, $content, $c2, $c1) = $this->fBlock(array(0,$tag,$atts,$ext,$cite,$graf)); 562 563 // leave off c1 if this block is extended, we'll close it at the start of the next block 564 if ($ext) 565 $line = $o1.$o2.$content.$c2; 566 else 567 $line = $o1.$o2.$content.$c2.$c1; 568 } 569 else { 570 // anonymous block 571 $anon = 1; 572 if ($ext or !preg_match('/^ /', $line)) { 573 list($o1, $o2, $content, $c2, $c1) = $this->fBlock(array(0,$tag,$atts,$ext,$cite,$line)); 574 // skip $o1/$c1 because this is part of a continuing extended block 575 if ($tag == 'p' and !$this->hasRawText($content)) { 576 $line = $content; 577 } 578 else { 579 $line = $o2.$content.$c2; 580 } 581 } 582 else { 583 $line = $this->graf($line); 584 } 585 } 586 587 $line = $this->doPBr($line); 588 $line = preg_replace('/<br>/', '<br />', $line); 589 590 if ($ext and $anon) 591 $out[count($out)-1] .= "\n".$line; 592 else 593 $out[] = $line; 594 595 if (!$ext) { 596 $tag = 'p'; 597 $atts = ''; 598 $cite = ''; 599 $graf = ''; 600 } 601 } 602 if ($ext) $out[count($out)-1] .= $c1; 603 return join("\n\n", $out); 604 } 605 606 607 608 // ------------------------------------------------------------- 609 function fBlock($m) 610 { 611 // $this->dump($m); 612 list(, $tag, $att, $ext, $cite, $content) = $m; 613 $atts = $this->pba($att); 614 615 $o1 = $o2 = $c2 = $c1 = ''; 616 617 if (preg_match("/fn(\d+)/", $tag, $fns)) { 618 $tag = 'p'; 619 $fnid = empty($this->fn[$fns[1]]) ? $fns[1] : $this->fn[$fns[1]]; 620 $atts .= ' id="fn' . $fnid . '"'; 621 if (strpos($atts, 'class=') === false) 622 $atts .= ' class="footnote"'; 623 $content = '<sup>' . $fns[1] . '</sup> ' . $content; 624 } 625 626 if ($tag == "bq") { 627 $cite = $this->shelveURL($cite); 628 $cite = ($cite != '') ? ' cite="' . $cite . '"' : ''; 629 $o1 = "\t<blockquote$cite$atts>\n"; 630 $o2 = "\t\t<p".$this->pba($att, '', 0).">"; 631 $c2 = "</p>"; 632 $c1 = "\n\t</blockquote>"; 633 } 634 elseif ($tag == 'bc') { 635 $o1 = "<pre$atts>"; 636 $o2 = "<code".$this->pba($att, '', 0).">"; 637 $c2 = "</code>"; 638 $c1 = "</pre>"; 639 $content = $this->shelve($this->r_encode_html(rtrim($content, "\n")."\n")); 640 } 641 elseif ($tag == 'notextile') { 642 $content = $this->shelve($content); 643 $o1 = $o2 = ''; 644 $c1 = $c2 = ''; 645 } 646 elseif ($tag == 'pre') { 647 $content = $this->shelve($this->r_encode_html(rtrim($content, "\n")."\n")); 648 $o1 = "<pre$atts>"; 649 $o2 = $c2 = ''; 650 $c1 = "</pre>"; 651 } 652 else { 653 $o2 = "\t<$tag$atts>"; 654 $c2 = "</$tag>"; 655 } 656 657 $content = $this->graf($content); 658 659 return array($o1, $o2, $content, $c2, $c1); 660 } 661 662 // ------------------------------------------------------------- 663 function graf($text) 664 { 665 // handle normal paragraph text 666 if (!$this->lite) { 667 $text = $this->noTextile($text); 668 $text = $this->code($text); 669 } 670 671 $text = $this->getRefs($text); 672 $text = $this->links($text); 673 if (!$this->noimage) 674 $text = $this->image($text); 675 676 if (!$this->lite) { 677 $text = $this->table($text); 678 $text = $this->lists($text); 679 } 680 681 $text = $this->span($text); 682 $text = $this->footnoteRef($text); 683 $text = $this->glyphs($text); 684 return rtrim($text, "\n"); 685 } 686 687 // ------------------------------------------------------------- 688 function span($text) 689 { 690 $qtags = array('\*\*','\*','\?\?','-','__','_','%','\+','~','\^'); 691 $pnct = ".,\"'?!;:"; 692 693 foreach($qtags as $f) { 694 $text = preg_replace_callback("/ 695 (^|(?<=[\s>$pnct\(])|[{[]) 696 ($f)(?!$f) 697 ({$this->c}) 698 (?::(\S+))? 699 ([^\s$f]+|\S.*?[^\s$f\n]) 700 ([$pnct]*) 701 $f 702 ($|[\]}]|(?=[[:punct:]]{1,2}|\s|\))) 703 /x", array(&$this, "fSpan"), $text); 704 } 705 return $text; 706 } 707 708 // ------------------------------------------------------------- 709 function fSpan($m) 710 { 711 $qtags = array( 712 '*' => 'strong', 713 '**' => 'b', 714 '??' => 'cite', 715 '_' => 'em', 716 '__' => 'i', 717 '-' => 'del', 718 '%' => 'span', 719 '+' => 'ins', 720 '~' => 'sub', 721 '^' => 'sup', 722 ); 723 724 list(, $pre, $tag, $atts, $cite, $content, $end, $tail) = $m; 725 $tag = $qtags[$tag]; 726 $atts = $this->pba($atts); 727 $atts .= ($cite != '') ? 'cite="' . $cite . '"' : ''; 728 729 $out = "<$tag$atts>$content$end</$tag>"; 730 731 if (($pre and !$tail) or ($tail and !$pre)) 732 $out = $pre.$out.$tail; 733 734 // $this->dump($out); 735 736 return $out; 737 738 } 739 740 // ------------------------------------------------------------- 741 function links($text) 742 { 743 return preg_replace_callback('/ 744 (^|(?<=[\s>.$pnct\(])|[{[]) # $pre 745 " # start 746 (' . $this->c . ') # $atts 747 ([^"]+?) # $text 748 (?:\(([^)]+?)\)(?="))? # $title 749 ": 750 ('.$this->urlch.'+?) # $url 751 (\/)? # $slash 752 ([^\w\/;]*?) # $post 753 ([\]}]|(?=\s|$|\))) 754 /x', array(&$this, "fLink"), $text); 755 } 756 757 // ------------------------------------------------------------- 758 function fLink($m) 759 { 760 list(, $pre, $atts, $text, $title, $url, $slash, $post, $tail) = $m; 761 762 $atts = $this->pba($atts); 763 $atts .= ($title != '') ? ' title="' . $this->encode_html($title) . '"' : ''; 764 765 if (!$this->noimage) 766 $text = $this->image($text); 767 768 $text = $this->span($text); 769 $text = $this->glyphs($text); 770 771 $url = $this->shelveURL($url.$slash); 772 773 $out = '<a href="' . $url . '"' . $atts . $this->rel . '>' . trim($text) . '</a>' . $post; 774 775 if (($pre and !$tail) or ($tail and !$pre)) 776 $out = $pre.$out.$tail; 777 778 // $this->dump($out); 779 return $this->shelve($out); 780 781 } 782 783 // ------------------------------------------------------------- 784 function getRefs($text) 785 { 786 return preg_replace_callback("/^\[(.+)\]((?:http:\/\/|\/)\S+)(?=\s|$)/Um", 787 array(&$this, "refs"), $text); 788 } 789 790 // ------------------------------------------------------------- 791 function refs($m) 792 { 793 list(, $flag, $url) = $m; 794 $this->urlrefs[$flag] = $url; 795 return ''; 796 } 797 798 // ------------------------------------------------------------- 799 function shelveURL($text) 800 { 801 if (!$text) return ''; 802 $ref = md5($text); 803 $this->urlshelf[$ref] = $text; 804 return 'urlref:'.$ref; 805 } 806 807 // ------------------------------------------------------------- 808 function retrieveURLs($text) 809 { 810 return preg_replace_callback('/urlref:(\w{32})/', 811 array(&$this, "retrieveURL"), $text); 812 } 813 814 // ------------------------------------------------------------- 815 function retrieveURL($m) 816 { 817 $ref = $m[1]; 818 if (!isset($this->urlshelf[$ref])) 819 return $ref; 820 $url = $this->urlshelf[$ref]; 821 if (isset($this->urlrefs[$url])) 822 $url = $this->urlrefs[$url]; 823 return $this->r_encode_html($this->relURL($url)); 824 } 825 826 // ------------------------------------------------------------- 827 function relURL($url) 828 { 829 $parts = @parse_url(urldecode($url)); 830 if ((empty($parts['scheme']) or @$parts['scheme'] == 'http') and 831 empty($parts['host']) and 832 preg_match('/^\w/', @$parts['path'])) 833 $url = $this->hu.$url; 834 if ($this->restricted and !empty($parts['scheme']) and 835 !in_array($parts['scheme'], $this->url_schemes)) 836 return '#'; 837 return $url; 838 } 839 840 // ------------------------------------------------------------- 841 function isRelURL($url) 842 { 843 $parts = @parse_url($url); 844 return (empty($parts['scheme']) and empty($parts['host'])); 845 } 846 847 // ------------------------------------------------------------- 848 function image($text) 849 { 850 return preg_replace_callback("/ 851 (?:[[{])? # pre 852 \! # opening ! 853 (\<|\=|\>)? # optional alignment atts 854 ($this->c) # optional style,class atts 855 (?:\. )? # optional dot-space 856 ([^\s(!]+) # presume this is the src 857 \s? # optional space 858 (?:\(([^\)]+)\))? # optional title 859 \! # closing 860 (?::(\S+))? # optional href 861 (?:[\]}]|(?=\s|$|\))) # lookahead: space or end of string 862 /x", array(&$this, "fImage"), $text); 863 } 864 865 // ------------------------------------------------------------- 866 function fImage($m) 867 { 868 list(, $algn, $atts, $url) = $m; 869 $atts = $this->pba($atts); 870 $atts .= ($algn != '') ? ' align="' . $this->iAlign($algn) . '"' : ''; 871 $atts .= (isset($m[4])) ? ' title="' . $m[4] . '"' : ''; 872 $atts .= (isset($m[4])) ? ' alt="' . $m[4] . '"' : ' alt=""'; 873 $size = false; 874 if ($this->isRelUrl($url)) 875 $size = @getimagesize(realpath($this->doc_root.ltrim($url, $this->ds))); 876 if ($size) $atts .= " $size[3]"; 877 878 $href = (isset($m[5])) ? $this->shelveURL($m[5]) : ''; 879 $url = $this->shelveURL($url); 880 881 $out = array( 882 ($href) ? '<a href="' . $href . '">' : '', 883 '<img src="' . $url . '"' . $atts . ' />', 884 ($href) ? '</a>' : '' 885 ); 886 887 return $this->shelve(join('',$out)); 888 } 889 890 // ------------------------------------------------------------- 891 function code($text) 892 { 893 $text = $this->doSpecial($text, '<code>', '</code>', 'fCode'); 894 $text = $this->doSpecial($text, '@', '@', 'fCode'); 895 $text = $this->doSpecial($text, '<pre>', '</pre>', 'fPre'); 896 return $text; 897 } 898 899 // ------------------------------------------------------------- 900 function fCode($m) 901 { 902 @list(, $before, $text, $after) = $m; 903 return $before.$this->shelve('<code>'.$this->r_encode_html($text).'</code>').$after; 904 } 905 906 // ------------------------------------------------------------- 907 function fPre($m) 908 { 909 @list(, $before, $text, $after) = $m; 910 return $before.'<pre>'.$this->shelve($this->r_encode_html($text)).'</pre>'.$after; 911 } 912 // ------------------------------------------------------------- 913 function shelve($val) 914 { 915 $i = uniqid(rand()); 916 $this->shelf[$i] = $val; 917 return $i; 918 } 919 920 // ------------------------------------------------------------- 921 function retrieve($text) 922 { 923 if (is_array($this->shelf)) 924 do { 925 $old = $text; 926 $text = strtr($text, $this->shelf); 927 } while ($text != $old); 928 929 return $text; 930 } 931 932 // ------------------------------------------------------------- 933 // NOTE: deprecated 934 function incomingEntities($text) 935 { 936 return preg_replace("/&(?![#a-z0-9]+;)/i", "x%x%", $text); 937 } 938 939 // ------------------------------------------------------------- 940 // NOTE: deprecated 941 function encodeEntities($text) 942 { 943 return (function_exists('mb_encode_numericentity')) 944 ? $this->encode_high($text) 945 : htmlentities($text, ENT_NOQUOTES, "utf-8"); 946 } 947 948 // ------------------------------------------------------------- 949 // NOTE: deprecated 950 function fixEntities($text) 951 { 952 /* de-entify any remaining angle brackets or ampersands */ 953 return str_replace(array(">", "<", "&"), 954 array(">", "<", "&"), $text); 955 } 956 957 // ------------------------------------------------------------- 958 function cleanWhiteSpace($text) 959 { 960 $out = str_replace("\r\n", "\n", $text); # DOS line endings 961 $out = preg_replace("/^[ \t]*\n/m", "\n", $out); # lines containing only whitespace 962 $out = preg_replace("/\n{3,}/", "\n\n", $out); # 3 or more line ends 963 $out = preg_replace("/^\n*/", "", $out); # leading blank lines 964 return $out; 965 } 966 967 // ------------------------------------------------------------- 968 function doSpecial($text, $start, $end, $method='fSpecial') 969 { 970 return preg_replace_callback('/(^|\s|[[({>])'.preg_quote($start, '/').'(.*?)'.preg_quote($end, '/').'(\s|$|[\])}])?/ms', 971 array(&$this, $method), $text); 972 } 973 974 // ------------------------------------------------------------- 975 function fSpecial($m) 976 { 977 // A special block like notextile or code 978 @list(, $before, $text, $after) = $m; 979 return $before.$this->shelve($this->encode_html($text)).$after; 980 } 981 982 // ------------------------------------------------------------- 983 function noTextile($text) 984 { 985 $text = $this->doSpecial($text, '<notextile>', '</notextile>', 'fTextile'); 986 return $this->doSpecial($text, '==', '==', 'fTextile'); 987 988 } 989 990 // ------------------------------------------------------------- 991 function fTextile($m) 992 { 993 @list(, $before, $notextile, $after) = $m; 994 #$notextile = str_replace(array_keys($modifiers), array_values($modifiers), $notextile); 995 return $before.$this->shelve($notextile).$after; 996 } 997 998 // ------------------------------------------------------------- 999 function footnoteRef($text) 1000 { 1001 return preg_replace('/(?<=\S)\[([0-9]+)\](\s)?/Ue', 1002 '$this->footnoteID(\'\1\',\'\2\')', $text); 1003 } 1004 1005 // ------------------------------------------------------------- 1006 function footnoteID($id, $t) 1007 { 1008 if (empty($this->fn[$id])) 1009 $this->fn[$id] = uniqid(rand()); 1010 $fnid = $this->fn[$id]; 1011 return '<sup class="footnote"><a href="#fn'.$fnid.'">'.$id.'</a></sup>'.$t; 1012 } 1013 1014 // ------------------------------------------------------------- 1015 function glyphs($text) 1016 { 1017 1018 // fix: hackish 1019 $text = preg_replace('/"\z/', "\" ", $text); 1020 $pnc = '[[:punct:]]'; 1021 1022 $glyph_search = array( 1023 '/(\w)\'(\w)/', // apostrophe's 1024 '/(\s)\'(\d+\w?)\b(?!\')/', // back in '88 1025 '/(\S)\'(?=\s|'.$pnc.'|<|$)/', // single closing 1026 '/\'/', // single opening 1027 '/(\S)\"(?=\s|'.$pnc.'|<|$)/', // double closing 1028 '/"/', // double opening 1029 '/\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])/', // 3+ uppercase acronym 1030 '/(?<=\s|^|[>(;-])([A-Z]{3,})([a-z]*)(?=\s|'.$pnc.'|<|$)/', // 3+ uppercase 1031 '/([^.]?)\.{3}/', // ellipsis 1032 '/(\s?)--(\s?)/', // em dash 1033 '/\s-(?:\s|$)/', // en dash 1034 '/(\d+)( ?)x( ?)(?=\d+)/', // dimension sign 1035 '/(\b ?|\s|^)[([]TM[])]/i', // trademark 1036 '/(\b ?|\s|^)[([]R[])]/i', // registered 1037 '/(\b ?|\s|^)[([]C[])]/i', // copyright 1038 ); 1039 1040 extract($this->glyph, EXTR_PREFIX_ALL, 'txt'); 1041 1042 $glyph_replace = array( 1043 '$1'.$txt_apostrophe.'$2', // apostrophe's 1044 '$1'.$txt_apostrophe.'$2', // back in '88 1045 '$1'.$txt_quote_single_close, // single closing 1046 $txt_quote_single_open, // single opening 1047 '$1'.$txt_quote_double_close, // double closing 1048 $txt_quote_double_open, // double opening 1049 '<acronym title="$2">$1</acronym>', // 3+ uppercase acronym 1050 '<span class="caps">$1</span>$2', // 3+ uppercase 1051 '$1'.$txt_ellipsis, // ellipsis 1052 '$1'.$txt_emdash.'$2', // em dash 1053 ' '.$txt_endash.' ', // en dash 1054 '$1$2'.$txt_dimension.'$3', // dimension sign 1055 '$1'.$txt_trademark, // trademark 1056 '$1'.$txt_registered, // registered 1057 '$1'.$txt_copyright, // copyright 1058 ); 1059 1060 $text = preg_split("@(<[\w/!?].*>)@Us", $text, -1, PREG_SPLIT_DELIM_CAPTURE); 1061 $i = 0; 1062 foreach($text as $line) { 1063 // text tag text tag text ... 1064 if (++$i % 2) { 1065 // raw < > & chars are already entity encoded in restricted mode 1066 if (!$this->restricted) { 1067 $line = $this->encode_raw_amp($line); 1068 $line = $this->encode_lt_gt($line); 1069 } 1070 $line = preg_replace($glyph_search, $glyph_replace, $line); 1071 } 1072 $glyph_out[] = $line; 1073 } 1074 return join('', $glyph_out); 1075 } 1076 1077 // ------------------------------------------------------------- 1078 function iAlign($in) 1079 { 1080 $vals = array( 1081 '<' => 'left', 1082 '=' => 'center', 1083 '>' => 'right'); 1084 return (isset($vals[$in])) ? $vals[$in] : ''; 1085 } 1086 1087 // ------------------------------------------------------------- 1088 function hAlign($in) 1089 { 1090 $vals = array( 1091 '<' => 'left', 1092 '=' => 'center', 1093 '>' => 'right', 1094 '<>' => 'justify'); 1095 return (isset($vals[$in])) ? $vals[$in] : ''; 1096 } 1097 1098 // ------------------------------------------------------------- 1099 function vAlign($in) 1100 { 1101 $vals = array( 1102 '^' => 'top', 1103 '-' => 'middle', 1104 '~' => 'bottom'); 1105 return (isset($vals[$in])) ? $vals[$in] : ''; 1106 } 1107 1108 // ------------------------------------------------------------- 1109 // NOTE: deprecated 1110 function encode_high($text, $charset = "UTF-8") 1111 { 1112 return mb_encode_numericentity($text, $this->cmap(), $charset); 1113 } 1114 1115 // ------------------------------------------------------------- 1116 // NOTE: deprecated 1117 function decode_high($text, $charset = "UTF-8") 1118 { 1119 return mb_decode_numericentity($text, $this->cmap(), $charset); 1120 } 1121 1122 // ------------------------------------------------------------- 1123 // NOTE: deprecated 1124 function cmap() 1125 { 1126 $f = 0xffff; 1127 $cmap = array( 1128 0x0080, 0xffff, 0, $f); 1129 return $cmap; 1130 } 1131 1132 // ------------------------------------------------------------- 1133 function encode_raw_amp($text) 1134 { 1135 return preg_replace('/&(?!#?[a-z0-9]+;)/i', '&', $text); 1136 } 1137 1138 // ------------------------------------------------------------- 1139 function encode_lt_gt($text) 1140 { 1141 return strtr($text, array('<' => '<', '>' => '>')); 1142 } 1143 1144 // ------------------------------------------------------------- 1145 function encode_html($str, $quotes=1) 1146 { 1147 $a = array( 1148 '&' => '&', 1149 '<' => '<', 1150 '>' => '>', 1151 ); 1152 if ($quotes) $a = $a + array( 1153 "'" => ''', // numeric, as in htmlspecialchars 1154 '"' => '"', 1155 ); 1156 1157 return strtr($str, $a); 1158 } 1159 1160 // ------------------------------------------------------------- 1161 function r_encode_html($str, $quotes=1) 1162 { 1163 // in restricted mode, input has already been escaped 1164 if ($this->restricted) 1165 return $str; 1166 return $this->encode_html($str, $quotes); 1167 } 1168 1169 // ------------------------------------------------------------- 1170 function textile_popup_help($name, $helpvar, $windowW, $windowH) 1171 { 1172 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 />'; 1173 1174 return $out; 1175 } 1176 1177 // ------------------------------------------------------------- 1178 // NOTE: deprecated 1179 function txtgps($thing) 1180 { 1181 if (isset($_POST[$thing])) { 1182 if (get_magic_quotes_gpc()) { 1183 return stripslashes($_POST[$thing]); 1184 } 1185 else { 1186 return $_POST[$thing]; 1187 } 1188 } 1189 else { 1190 return ''; 1191 } 1192 } 1193 1194 // ------------------------------------------------------------- 1195 // NOTE: deprecated 1196 function dump() 1197 { 1198 foreach (func_get_args() as $a) 1199 echo "\n<pre>",(is_array($a)) ? print_r($a) : $a, "</pre>\n"; 1200 } 1201 1202 // ------------------------------------------------------------- 1203 1204 function blockLite($text) 1205 { 1206 $this->btag = array('bq', 'p'); 1207 return $this->block($text."\n\n"); 1208 } 1209 1210 1211 } // end class 1212 1213 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu May 21 23:03:01 2009 | Cross-referenced by PHPXref 0.7 |