[ Index ] |
PHP Cross Reference of phpwcms V1.5.0 _r431 (28.01.12) |
[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: http://textpattern.googlecode.com/svn/development/4.x/textpattern/lib/classTextile.php $ 13 $LastChangedRevision: 3566 $ 14 */ 15 16 /* 17 18 _____________ 19 T E X T I L E 20 21 A Humane Web Text Generator 22 23 Version 2.2 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 Definition list: 93 Terms ;, ;; 94 Definitions :, :: 95 Consecutive paragraphs beginning with ; or : are wrapped in definition list tags. 96 Example: <dl><dt>term</dt><dd>definition</dd></dl> 97 98 Phrase modifier syntax: 99 100 _emphasis_ -> <em>emphasis</em> 101 __italic__ -> <i>italic</i> 102 *strong* -> <strong>strong</strong> 103 **bold** -> <b>bold</b> 104 ??citation?? -> <cite>citation</cite> 105 -deleted text- -> <del>deleted</del> 106 +inserted text+ -> <ins>inserted</ins> 107 ^superscript^ -> <sup>superscript</sup> 108 ~subscript~ -> <sub>subscript</sub> 109 @code@ -> <code>computer code</code> 110 %(bob)span% -> <span class="bob">span</span> 111 112 ==notextile== -> leave text alone (do not format) 113 114 "linktext":url -> <a href="url">linktext</a> 115 "linktext(title)":url -> <a href="url" title="title">linktext</a> 116 "$":url -> <a href="url">url</a> 117 "$(title)":url -> <a href="url" title="title">url</a> 118 119 !imageurl! -> <img src="imageurl" /> 120 !imageurl(alt text)! -> <img src="imageurl" alt="alt text" /> 121 !imageurl!:linkurl -> <a href="linkurl"><img src="imageurl" /></a> 122 123 ABC(Always Be Closing) -> <acronym title="Always Be Closing">ABC</acronym> 124 125 126 Linked Notes: 127 ============ 128 129 Allows the generation of an automated list of notes with links. 130 131 Linked notes are composed of three parts, a set of named _definitions_, a set of 132 _references_ to those definitions and one or more _placeholders_ indicating where 133 the consolidated list of notes is to be placed in your document. 134 135 Definitions. 136 ----------- 137 138 Each note definition must occur in its own paragraph and should look like this... 139 140 note#mynotelabel. Your definition text here. 141 142 You are free to use whatever label you wish after the # as long as it is made up 143 of letters, numbers, colon(:) or dash(-). 144 145 References. 146 ---------- 147 148 Each note reference is marked in your text like this[#mynotelabel] and 149 it will be replaced with a superscript reference that links into the list of 150 note definitions. 151 152 List Placeholder(s). 153 ------------------- 154 155 The note list can go anywhere in your document. You have to indicate where 156 like this... 157 158 notelist. 159 160 notelist can take attributes (class#id) like this: notelist(class#id). 161 162 By default, the note list will show each definition in the order that they 163 are referenced in the text by the _references_. It will show each definition with 164 a full list of backlinks to each reference. If you do not want this, you can choose 165 to override the backlinks like this... 166 167 notelist(class#id)!. Produces a list with no backlinks. 168 notelist(class#id)^. Produces a list with only the first backlink. 169 170 Should you wish to have a specific definition display backlinks differently to this 171 then you can override the backlink method by appending a link override to the 172 _definition_ you wish to customise. 173 174 note#label. Uses the citelist's setting for backlinks. 175 note#label!. Causes that definition to have no backlinks. 176 note#label^. Causes that definition to have one backlink (to the first ref.) 177 note#label*. Causes that definition to have all backlinks. 178 179 Any unreferenced notes will be left out of the list unless you explicitly state 180 you want them by adding a '+'. Like this... 181 182 notelist(class#id)!+. Giving a list of all notes without any backlinks. 183 184 You can mix and match the list backlink control and unreferenced links controls 185 but the backlink control (if any) must go first. Like so: notelist^+. , not 186 like this: notelist+^. 187 188 Example... 189 Scientists say[#lavader] the moon is small. 190 191 note#other. An unreferenced note. 192 193 note#lavader(myliclass). "Proof":url of a small moon. 194 195 notelist(myclass#myid)+. 196 197 Would output (the actual IDs used would be randomised)... 198 199 <p>Scientists say<sup><a href="#def_id_1" id="ref_id_1a">1</sup> the moon is small.</p> 200 201 <ol class="myclass" id="myid"> 202 <li class="myliclass"><a href="#ref_id_1a"><sup>a</sup></a><span id="def_id_1"> </span><a href="url">Proof</a> of a small moon.</li> 203 <li>An unreferenced note.</li> 204 </ol> 205 206 The 'a b c' backlink characters can be altered too. 207 For example if you wanted the notes to have numeric backlinks starting from 1: 208 209 notelist:1. 210 211 Table syntax: 212 213 Simple tables: 214 215 |a|simple|table|row| 216 |And|Another|table|row| 217 |With an||empty|cell| 218 219 |=. My table caption goes here 220 |_. A|_. table|_. header|_.row| 221 |A|simple|table|row| 222 223 Tables with attributes: 224 225 table{border:1px solid black}. My table summary here 226 {background:#ddd;color:red}. |{}| | | | 227 228 To specify thead / tfoot / tbody groups, add one of these on its own line 229 above the row(s) you wish to wrap (you may specify attributes before the dot): 230 231 |^. # thead 232 |-. # tbody 233 |~. # tfoot 234 235 Column groups: 236 237 |:\3. 100 238 239 Becomes: 240 <colgroup span="3" width="100"></colgroup> 241 242 You can omit either of the \N or width values. You may also 243 add cells after the colgroup definition to specify span/width/attributes: 244 245 |:\5. 50 |(firstcol). |\2. 250||300| 246 247 Becomes: 248 <colgroup span="5" width="50"> 249 <col class="firstcol" /> 250 <col span="2" width="250" /> 251 <col /> 252 <col width="300" /> 253 </colgroup> 254 255 Applying Attributes: 256 257 Most anywhere Textile code is used, attributes such as arbitrary css style, 258 css classes, and ids can be applied. The syntax is fairly consistent. 259 260 The following characters quickly alter the alignment of block elements: 261 262 < -> left align ex. p<. left-aligned para 263 > -> right align h3>. right-aligned header 3 264 = -> centred h4=. centred header 4 265 <> -> justified p<>. justified paragraph 266 267 These will change vertical alignment in table cells: 268 269 ^ -> top ex. |^. top-aligned table cell| 270 - -> middle |-. middle aligned| 271 ~ -> bottom |~. bottom aligned cell| 272 273 Plain (parentheses) inserted between block syntax and the closing dot-space 274 indicate classes and ids: 275 276 p(hector). paragraph -> <p class="hector">paragraph</p> 277 278 p(#fluid). paragraph -> <p id="fluid">paragraph</p> 279 280 (classes and ids can be combined) 281 p(hector#fluid). paragraph -> <p class="hector" id="fluid">paragraph</p> 282 283 Curly {brackets} insert arbitrary css style 284 285 p{line-height:18px}. paragraph -> <p style="line-height:18px">paragraph</p> 286 287 h3{color:red}. header 3 -> <h3 style="color:red">header 3</h3> 288 289 Square [brackets] insert language attributes 290 291 p[no]. paragraph -> <p lang="no">paragraph</p> 292 293 %[fr]phrase% -> <span lang="fr">phrase</span> 294 295 Usually Textile block element syntax requires a dot and space before the block 296 begins, but since lists don't, they can be styled just using braces 297 298 #{color:blue} one -> <ol style="color:blue"> 299 # big <li>one</li> 300 # list <li>big</li> 301 <li>list</li> 302 </ol> 303 304 Using the span tag to style a phrase 305 306 It goes like this, %{color:red}the fourth the fifth% 307 -> It goes like this, <span style="color:red">the fourth the fifth</span> 308 309 */ 310 311 // define these before including this file to override the standard glyphs 312 @define('txt_quote_single_open', '‘'); 313 @define('txt_quote_single_close', '’'); 314 @define('txt_quote_double_open', '“'); 315 @define('txt_quote_double_close', '”'); 316 @define('txt_apostrophe', '’'); 317 @define('txt_prime', '′'); 318 @define('txt_prime_double', '″'); 319 @define('txt_ellipsis', '…'); 320 @define('txt_emdash', '—'); 321 @define('txt_endash', '–'); 322 @define('txt_dimension', '×'); 323 @define('txt_trademark', '™'); 324 @define('txt_registered', '®'); 325 @define('txt_copyright', '©'); 326 @define('txt_half', '½'); 327 @define('txt_quarter', '¼'); 328 @define('txt_threequarters', '¾'); 329 @define('txt_degrees', '°'); 330 @define('txt_plusminus', '±'); 331 @define('txt_has_unicode', @preg_match('/\pL/u', 'a')); // Detect if Unicode is compiled into PCRE 332 333 class Textile 334 { 335 var $hlgn; 336 var $vlgn; 337 var $clas; 338 var $lnge; 339 var $styl; 340 var $cspn; 341 var $rspn; 342 var $a; 343 var $s; 344 var $c; 345 var $pnct; 346 var $rel; 347 var $fn; 348 349 var $shelf = array(); 350 var $restricted = false; 351 var $noimage = false; 352 var $lite = false; 353 var $url_schemes = array(); 354 var $glyph = array(); 355 var $hu = ''; 356 var $max_span_depth = 5; 357 358 var $ver = '2.2.0'; 359 var $rev = '$Rev: 3566 $'; 360 361 var $doc_root; 362 363 // ------------------------------------------------------------- 364 function Textile() 365 { 366 $this->hlgn = "(?:\<(?!>)|(?<!<)\>|\<\>|\=|[()]+(?! ))"; 367 $this->vlgn = "[\-^~]"; 368 $this->clas = "(?:\([^)\n]+\))"; # Don't allow classes/ids/languages/styles to span across newlines 369 $this->lnge = "(?:\[[^]\n]+\])"; 370 $this->styl = "(?:\{[^}\n]+\})"; 371 $this->cspn = "(?:\\\\\d+)"; 372 $this->rspn = "(?:\/\d+)"; 373 $this->a = "(?:{$this->hlgn}|{$this->vlgn})*"; 374 $this->s = "(?:{$this->cspn}|{$this->rspn})*"; 375 $this->c = "(?:{$this->clas}|{$this->styl}|{$this->lnge}|{$this->hlgn})*"; 376 $this->lc = "(?:{$this->clas}|{$this->styl}|{$this->lnge})*"; 377 378 $this->pnct = '[\!"#\$%&\'()\*\+,\-\./:;<=>\?@\[\\\]\^_`{\|}\~]'; 379 $this->urlch = '[\w"$\-_.+!*\'(),";\/?:@=&%#{}|\\^~\[\]`]'; 380 $pnc = '[[:punct:]]'; 381 382 $this->url_schemes = array('http','https','ftp','mailto'); 383 384 $this->btag = array('bq', 'bc', 'notextile', 'pre', 'h[1-6]', 'fn\d+', 'p', '###' ); 385 386 if (txt_has_unicode) { 387 $this->regex_snippets = array( 388 'acr' => '\p{Lu}\p{Nd}', 389 'abr' => '\p{Lu}', 390 'nab' => '\p{Ll}', 391 'wrd' => '(?:\p{L}|\p{M}|\p{N}|\p{Pc})', 392 'mod' => 'u', # Make sure to mark the unicode patterns as such, Some servers seem to need this. 393 ); 394 } else { 395 $this->regex_snippets = array( 396 'acr' => 'A-Z0-9', 397 'abr' => 'A-Z', 398 'nab' => 'a-z', 399 'wrd' => '\w', 400 'mod' => '', 401 ); 402 } 403 extract( $this->regex_snippets ); 404 405 $this->glyph_search = array( 406 '/('.$wrd.')\'('.$wrd.')/'.$mod, // I'm an apostrophe 407 '/(\s)\'(\d+'.$wrd.'?)\b(?![.]?['.$wrd.']*?\')/'.$mod, // back in '88/the '90s but not in his '90s', '1', '1.' '10m' or '5.png' 408 '/(\S)\'(?=\s|'.$pnc.'|<|$)/', // single closing 409 '/\'/', // single opening 410 '/(\S)\"(?=\s|'.$pnc.'|<|$)/', // double closing 411 '/"/', // double opening 412 '/\b(['.$abr.']['.$acr.']{2,})\b(?:[(]([^)]*)[)])/'.$mod, // 3+ uppercase acronym 413 '/(?<=\s|^|[>(;-])(['.$abr.']{3,})(['.$nab.']*)(?=\s|'.$pnc.'|<|$)(?=[^">]*?(<|$))/'.$mod, // 3+ uppercase 414 '/([^.]?)\.{3}/', // ellipsis 415 '/(\s?)--(\s?)/', // em dash 416 '/\s-(?:\s|$)/', // en dash 417 '/(\d+)( ?)x( ?)(?=\d+)/', // dimension sign 418 '/(\b ?|\s|^)[([]TM[])]/i', // trademark 419 '/(\b ?|\s|^)[([]R[])]/i', // registered 420 '/(\b ?|\s|^)[([]C[])]/i', // copyright 421 '/[([]1\/4[])]/', // 1/4 422 '/[([]1\/2[])]/', // 1/2 423 '/[([]3\/4[])]/', // 3/4 424 '/[([]o[])]/', // degrees -- that's a small 'oh' 425 '/[([]\+\/-[])]/', // plus minus 426 ); 427 428 $this->glyph_replace = array( 429 '$1'.txt_apostrophe.'$2', // I'm an apostrophe 430 '$1'.txt_apostrophe.'$2', // back in '88 431 '$1'.txt_quote_single_close, // single closing 432 txt_quote_single_open, // single opening 433 '$1'.txt_quote_double_close, // double closing 434 txt_quote_double_open, // double opening 435 '<acronym title="$2">$1</acronym>', // 3+ uppercase acronym 436 '<span class="caps">glyph:$1</span>$2', // 3+ uppercase 437 '$1'.txt_ellipsis, // ellipsis 438 '$1'.txt_emdash.'$2', // em dash 439 ' '.txt_endash.' ', // en dash 440 '$1$2'.txt_dimension.'$3', // dimension sign 441 '$1'.txt_trademark, // trademark 442 '$1'.txt_registered, // registered 443 '$1'.txt_copyright, // copyright 444 txt_quarter, // 1/4 445 txt_half, // 1/2 446 txt_threequarters, // 3/4 447 txt_degrees, // degrees 448 txt_plusminus, // plus minus 449 ); 450 451 if (defined('hu')) 452 $this->hu = hu; 453 454 if (defined('DIRECTORY_SEPARATOR')) 455 $this->ds = constant('DIRECTORY_SEPARATOR'); 456 else 457 $this->ds = '/'; 458 459 $this->doc_root = @$_SERVER['DOCUMENT_ROOT']; 460 if (!$this->doc_root) 461 $this->doc_root = @$_SERVER['PATH_TRANSLATED']; // IIS 462 463 $this->doc_root = rtrim($this->doc_root, $this->ds).$this->ds; 464 } 465 466 // ------------------------------------------------------------- 467 468 function TextileThis($text, $lite = '', $encode = '', $noimage = '', $strict = '', $rel = '') 469 { 470 $this->span_depth = 0; 471 $this->tag_index = 1; 472 $this->notes = $this->unreferencedNotes = $this->notelist_cache = array(); 473 $this->note_index = 1; 474 $this->rel = ($rel) ? ' rel="'.$rel.'"' : ''; 475 476 $this->lite = $lite; 477 $this->noimage = $noimage; 478 479 if ($encode) 480 { 481 $text = $this->incomingEntities($text); 482 $text = str_replace("x%x%", "&", $text); 483 return $text; 484 } else { 485 if(!$strict) { 486 $text = $this->cleanWhiteSpace($text); 487 } 488 489 if(!$lite) { 490 $text = $this->block($text); 491 $text = $this->placeNoteLists($text); 492 } 493 494 $text = $this->retrieve($text); 495 $text = $this->replaceGlyphs($text); 496 $text = $this->retrieveTags($text); 497 $text = $this->retrieveURLs($text); 498 $this->span_depth = 0; 499 500 // just to be tidy 501 $text = str_replace("<br />", "<br />\n", $text); 502 503 return $text; 504 } 505 } 506 507 // ------------------------------------------------------------- 508 509 function TextileRestricted($text, $lite = 1, $noimage = 1, $rel = 'nofollow') 510 { 511 $this->restricted = true; 512 $this->lite = $lite; 513 $this->noimage = $noimage; 514 515 $this->span_depth = 0; 516 $this->tag_index = 1; 517 $this->notes = $this->unreferencedNotes = $this->notelist_cache = array(); 518 $this->note_index = 1; 519 520 $this->rel = ($rel) ? ' rel="'.$rel.'"' : ''; 521 522 // escape any raw html 523 $text = $this->encode_html($text, 0); 524 525 $text = $this->cleanWhiteSpace($text); 526 527 if($lite) { 528 $text = $this->blockLite($text); 529 } else { 530 $text = $this->block($text); 531 $text = $this->placeNoteLists($text); 532 } 533 534 $text = $this->retrieve($text); 535 $text = $this->replaceGlyphs($text); 536 $text = $this->retrieveTags($text); 537 $text = $this->retrieveURLs($text); 538 $this->span_depth = 0; 539 540 // just to be tidy 541 $text = str_replace("<br />", "<br />\n", $text); 542 543 return $text; 544 } 545 546 // ------------------------------------------------------------- 547 function pba($in, $element = "", $include_id = 1) // "parse block attributes" 548 { 549 $style = ''; 550 $class = ''; 551 $lang = ''; 552 $colspan = ''; 553 $rowspan = ''; 554 $span = ''; 555 $width = ''; 556 $id = ''; 557 $atts = ''; 558 559 if (!empty($in)) { 560 $matched = $in; 561 if ($element == 'td') { 562 if (preg_match("/\\\\(\d+)/", $matched, $csp)) $colspan = $csp[1]; 563 if (preg_match("/\/(\d+)/", $matched, $rsp)) $rowspan = $rsp[1]; 564 } 565 566 if ($element == 'td' or $element == 'tr') { 567 if (preg_match("/($this->vlgn)/", $matched, $vert)) 568 $style[] = "vertical-align:" . $this->vAlign($vert[1]); 569 } 570 571 if (preg_match("/\{([^}]*)\}/", $matched, $sty)) { 572 $style[] = rtrim($sty[1], ';'); 573 $matched = str_replace($sty[0], '', $matched); 574 } 575 576 if (preg_match("/\[([^]]+)\]/U", $matched, $lng)) { 577 $lang = $lng[1]; 578 $matched = str_replace($lng[0], '', $matched); 579 } 580 581 if (preg_match("/\(([^()]+)\)/U", $matched, $cls)) { 582 $class = $cls[1]; 583 $matched = str_replace($cls[0], '', $matched); 584 } 585 586 if (preg_match("/([(]+)/", $matched, $pl)) { 587 $style[] = "padding-left:" . strlen($pl[1]) . "em"; 588 $matched = str_replace($pl[0], '', $matched); 589 } 590 591 if (preg_match("/([)]+)/", $matched, $pr)) { 592 $style[] = "padding-right:" . strlen($pr[1]) . "em"; 593 $matched = str_replace($pr[0], '', $matched); 594 } 595 596 if (preg_match("/($this->hlgn)/", $matched, $horiz)) 597 $style[] = "text-align:" . $this->hAlign($horiz[1]); 598 599 if (preg_match("/^(.*)#(.*)$/", $class, $ids)) { 600 $id = $ids[2]; 601 $class = $ids[1]; 602 } 603 604 if ($element == 'col') { 605 if (preg_match("/(?:\\\\(\d+))?\s*(\d+)?/", $matched, $csp)) { 606 $span = isset($csp[1]) ? $csp[1] : ''; 607 $width = isset($csp[2]) ? $csp[2] : ''; 608 } 609 } 610 611 if ($this->restricted) 612 return ($lang) ? ' lang="' . $lang . '"':''; 613 614 $o = ''; 615 if( $style ) { 616 foreach($style as $s) { 617 $parts = explode(';', $s); 618 foreach( $parts as $p ) { 619 $p = trim($p, '; '); 620 if( !empty( $p ) ) 621 $o .= $p.'; '; 622 } 623 } 624 $style = trim( strtr($o, array("\n"=>'',';;'=>';')) ); 625 } 626 627 return join('',array( 628 ($style) ? ' style="' . $style .'"':'', 629 ($class) ? ' class="' . $class .'"':'', 630 ($lang) ? ' lang="' . $lang .'"':'', 631 ($id and $include_id) ? ' id="' . $id .'"':'', 632 ($colspan) ? ' colspan="' . $colspan .'"':'', 633 ($rowspan) ? ' rowspan="' . $rowspan .'"':'', 634 ($span) ? ' span="' . $span .'"':'', 635 ($width) ? ' width="' . $width .'"':'', 636 )); 637 } 638 return ''; 639 } 640 641 // ------------------------------------------------------------- 642 function hasRawText($text) 643 { 644 // checks whether the text has text not already enclosed by a block tag 645 $r = trim(preg_replace('@<(p|blockquote|div|form|table|ul|ol|dl|pre|h\d)[^>]*?>.*</\1>@s', '', trim($text))); 646 $r = trim(preg_replace('@<(hr|br)[^>]*?/>@', '', $r)); 647 return '' != $r; 648 } 649 650 // ------------------------------------------------------------- 651 function table($text) 652 { 653 $text = $text . "\n\n"; 654 return preg_replace_callback("/^(?:table(_?{$this->s}{$this->a}{$this->c})\.(.*)?\n)?^({$this->a}{$this->c}\.? ?\|.*\|)[\s]*\n\n/smU", 655 array(&$this, "fTable"), $text); 656 } 657 658 // ------------------------------------------------------------- 659 function fTable($matches) 660 { 661 $tatts = $this->pba($matches[1], 'table'); 662 663 $sum = trim($matches[2]) ? ' summary="'.htmlspecialchars(trim($matches[2])).'"' : ''; 664 $cap = ''; 665 $colgrp = $last_rgrp = ''; 666 foreach(preg_split("/\|\s*?$/m", $matches[3], -1, PREG_SPLIT_NO_EMPTY) as $row) { 667 // Caption 668 if (preg_match("/^\|\=($this->s$this->a$this->c)\. ([^\|\n]*)(.*)/s", ltrim($row), $cmtch)) { 669 $capts = $this->pba($cmtch[1]); 670 $cap = "\t<caption".$capts.">".trim($cmtch[2])."</caption>\n"; 671 $row = $cmtch[3]; 672 } 673 674 // Colgroup 675 if (preg_match("/^\|:($this->s$this->a$this->c\. .*)/m", ltrim($row), $gmtch)) { 676 $idx=0; 677 foreach (explode('|', str_replace('.', '', $gmtch[1])) as $col) { 678 $gatts = $this->pba(trim($col), 'col'); 679 $colgrp .= "\t<col".(($idx==0) ? "group".$gatts.">" : $gatts." />")."\n"; 680 $idx++; 681 } 682 $colgrp .= "\t</colgroup>\n"; 683 continue; 684 } 685 686 preg_match("/(:?^\|($this->vlgn)($this->s$this->a$this->c)\.\s*$\n)?^(.*)/sm", ltrim($row), $grpmatch); 687 688 // Row group 689 $rgrp = isset($grpmatch[2]) ? (($grpmatch[2] == '^') ? 'head' : ( ($grpmatch[2] == '~') ? 'foot' : (($grpmatch[2] == '-') ? 'body' : '' ) ) ) : ''; 690 $rgrpatts = isset($grpmatch[3]) ? $this->pba($grpmatch[3]) : ''; 691 $row = $grpmatch[4]; 692 693 if (preg_match("/^($this->a$this->c\. )(.*)/m", ltrim($row), $rmtch)) { 694 $ratts = $this->pba($rmtch[1], 'tr'); 695 $row = $rmtch[2]; 696 } else $ratts = ''; 697 698 $cells = array(); 699 $cellctr = 0; 700 foreach(explode("|", $row) as $cell) { 701 $ctyp = "d"; 702 if (preg_match("/^_/", $cell)) $ctyp = "h"; 703 if (preg_match("/^(_?$this->s$this->a$this->c\. )(.*)/", $cell, $cmtch)) { 704 $catts = $this->pba($cmtch[1], 'td'); 705 $cell = $cmtch[2]; 706 } else $catts = ''; 707 708 $cell = $this->graf($cell); 709 710 if ($cellctr>0) // Ignore first 'cell': it precedes the opening pipe 711 $cells[] = $this->doTagBr("t$ctyp", "\t\t\t<t$ctyp$catts>$cell</t$ctyp>"); 712 713 $cellctr++; 714 } 715 $grp = (($rgrp && $last_rgrp) ? "\t</t".$last_rgrp.">\n" : '') . (($rgrp) ? "\t<t".$rgrp.$rgrpatts.">\n" : ''); 716 $last_rgrp = ($rgrp) ? $rgrp : $last_rgrp; 717 $rows[] = $grp."\t\t<tr$ratts>\n" . join("\n", $cells) . ($cells ? "\n" : "") . "\t\t</tr>"; 718 unset($cells, $catts); 719 } 720 721 return "\t<table{$tatts}{$sum}>\n" .$cap. $colgrp. join("\n", $rows) . "\n".(($last_rgrp) ? "\t</t".$last_rgrp.">\n" : '')."\t</table>\n\n"; 722 } 723 724 // ------------------------------------------------------------- 725 function lists($text) 726 { 727 return preg_replace_callback("/^([#*;:]+$this->lc[ .].*)$(?![^#*;:])/smU", array(&$this, "fList"), $text); 728 } 729 730 // ------------------------------------------------------------- 731 function fList($m) 732 { 733 $text = preg_split('/\n(?=[*#;:])/m', $m[0]); 734 $pt = ''; 735 foreach($text as $nr => $line) { 736 $nextline = isset($text[$nr+1]) ? $text[$nr+1] : false; 737 if (preg_match("/^([#*;:]+)($this->lc)[ .](.*)$/s", $line, $m)) { 738 list(, $tl, $atts, $content) = $m; 739 $content = trim($content); 740 $nl = ''; 741 $ltype = $this->lT($tl); 742 $litem = (strpos($tl, ';') !== false) ? 'dt' : ((strpos($tl, ':') !== false) ? 'dd' : 'li'); 743 $showitem = (strlen($content) > 0); 744 745 if (preg_match("/^([#*;:]+)($this->lc)[ .].*/", $nextline, $nm)) 746 $nl = $nm[1]; 747 748 if ((strpos($pt, ';') !== false) && (strpos($tl, ':') !== false)) { 749 $lists[$tl] = 2; // We're already in a <dl> so flag not to start another 750 } 751 752 $atts = $this->pba($atts); 753 if (!isset($lists[$tl])) { 754 $lists[$tl] = 1; 755 $line = "\t<" . $ltype . "l$atts>" . (($showitem) ? "\n\t\t<$litem>" . $content : ''); 756 } else { 757 $line = ($showitem) ? "\t\t<$litem$atts>" . $content : ''; 758 } 759 760 if((strlen($nl) <= strlen($tl))) $line .= (($showitem) ? "</$litem>" : ''); 761 foreach(array_reverse($lists) as $k => $v) { 762 if(strlen($k) > strlen($nl)) { 763 $line .= ($v==2) ? '' : "\n\t</" . $this->lT($k) . "l>"; 764 if((strlen($k) > 1) && ($v != 2)) 765 $line .= "</".$litem.">"; 766 unset($lists[$k]); 767 } 768 } 769 $pt = $tl; // Remember the current Textile tag 770 } 771 else { 772 $line .= "\n"; 773 } 774 $out[] = $line; 775 } 776 return $this->doTagBr($litem, join("\n", $out)); 777 } 778 779 // ------------------------------------------------------------- 780 function lT($in) 781 { 782 return preg_match("/^#+/", $in) ? 'o' : ((preg_match("/^\*+/", $in)) ? 'u' : 'd'); 783 } 784 785 // ------------------------------------------------------------- 786 function doTagBr($tag, $in) 787 { 788 return preg_replace_callback('@<('.preg_quote($tag).')([^>]*?)>(.*)(</\1>)@s', array(&$this, 'fBr'), $in); 789 } 790 791 // ------------------------------------------------------------- 792 function doPBr($in) 793 { 794 return preg_replace_callback('@<(p)([^>]*?)>(.*)(</\1>)@s', array(&$this, 'fPBr'), $in); 795 } 796 797 // ------------------------------------------------------------- 798 function fPBr($m) 799 { 800 # Less restrictive version of fBr() ... used only in paragraphs where the next 801 # row may start with a smiley or perhaps something like '#8 bolt...' or '*** stars...' 802 $content = preg_replace("@(.+)(?<!<br>|<br />)\n(?![\s|])@", '$1<br />', $m[3]); 803 return '<'.$m[1].$m[2].'>'.$content.$m[4]; 804 } 805 806 // ------------------------------------------------------------- 807 function fBr($m) 808 { 809 $content = preg_replace("@(.+)(?<!<br>|<br />)\n(?![#*;:\s|])@", '$1<br />', $m[3]); 810 return '<'.$m[1].$m[2].'>'.$content.$m[4]; 811 } 812 813 // ------------------------------------------------------------- 814 function block($text) 815 { 816 $find = $this->btag; 817 $tre = join('|', $find); 818 819 $text = explode("\n\n", $text); 820 821 $tag = 'p'; 822 $atts = $cite = $graf = $ext = ''; 823 $eat = false; 824 825 $out = array(); 826 827 foreach($text as $line) { 828 $anon = 0; 829 if (preg_match("/^($tre)($this->a$this->c)\.(\.?)(?::(\S+))? (.*)$/s", $line, $m)) { 830 // last block was extended, so close it 831 if ($ext) 832 $out[count($out)-1] .= $c1; 833 // new block 834 list(,$tag,$atts,$ext,$cite,$graf) = $m; 835 list($o1, $o2, $content, $c2, $c1, $eat) = $this->fBlock(array(0,$tag,$atts,$ext,$cite,$graf)); 836 837 // leave off c1 if this block is extended, we'll close it at the start of the next block 838 if ($ext) 839 $line = $o1.$o2.$content.$c2; 840 else 841 $line = $o1.$o2.$content.$c2.$c1; 842 } 843 else { 844 // anonymous block 845 $anon = 1; 846 if ($ext or !preg_match('/^ /', $line)) { 847 list($o1, $o2, $content, $c2, $c1, $eat) = $this->fBlock(array(0,$tag,$atts,$ext,$cite,$line)); 848 // skip $o1/$c1 because this is part of a continuing extended block 849 if ($tag == 'p' and !$this->hasRawText($content)) { 850 $line = $content; 851 } 852 else { 853 $line = $o2.$content.$c2; 854 } 855 } 856 else { 857 $line = $this->graf($line); 858 } 859 } 860 861 $line = $this->doPBr($line); 862 $line = preg_replace('/<br>/', '<br />', $line); 863 864 if ($ext and $anon) 865 $out[count($out)-1] .= "\n".$line; 866 elseif(!$eat) 867 $out[] = $line; 868 869 if (!$ext) { 870 $tag = 'p'; 871 $atts = ''; 872 $cite = ''; 873 $graf = ''; 874 $eat = false; 875 } 876 } 877 if ($ext) $out[count($out)-1] .= $c1; 878 return join("\n\n", $out); 879 } 880 881 // ------------------------------------------------------------- 882 function fBlock($m) 883 { 884 extract($this->regex_snippets); 885 list(, $tag, $att, $ext, $cite, $content) = $m; 886 $atts = $this->pba($att); 887 888 $o1 = $o2 = $c2 = $c1 = ''; 889 $eat = false; 890 891 if( $tag === 'p' ) { 892 # Is this an anonymous block with a note definition? 893 $notedef = preg_replace_callback("/ 894 ^note\# # start of note def marker 895 ([$wrd:-]+) # !label 896 ([*!^]?) # !link 897 ({$this->c}) # !att 898 \.[\s]+ # end of def marker 899 (.*)$ # !content 900 /x$mod", array(&$this, "fParseNoteDefs"), $content); 901 if( empty($notedef) ) # It will be empty if the regex matched and ate it. 902 return array($o1, $o2, $notedef, $c2, $c1, true); 903 } 904 905 if (preg_match("/fn(\d+)/", $tag, $fns)) { 906 $tag = 'p'; 907 $fnid = empty($this->fn[$fns[1]]) ? $fns[1] : $this->fn[$fns[1]]; 908 909 # If there is an author-specified ID goes on the wrapper & the auto-id gets pushed to the <sup> 910 $supp_id = ''; 911 if (strpos($atts, ' id=') === false) 912 $atts .= ' id="fn' . $fnid . '"'; 913 else 914 $supp_id = ' id="fn' . $fnid . '"'; 915 916 if (strpos($atts, 'class=') === false) 917 $atts .= ' class="footnote"'; 918 919 $backlink = (strpos($att, '^') === false) ? $fns[1] : '<a href="#fnrev' . $fnid . '">'.$fns[1].'</a>'; 920 $sup = "<sup$supp_id>$backlink</sup>"; 921 922 $content = $sup . ' ' . $content; 923 } 924 925 if ($tag == "bq") { 926 $cite = $this->shelveURL($cite); 927 $cite = ($cite != '') ? ' cite="' . $cite . '"' : ''; 928 $o1 = "\t<blockquote$cite$atts>\n"; 929 $o2 = "\t\t<p".$this->pba($att, '', 0).">"; 930 $c2 = "</p>"; 931 $c1 = "\n\t</blockquote>"; 932 } 933 elseif ($tag == 'bc') { 934 $o1 = "<pre$atts>"; 935 $o2 = "<code".$this->pba($att, '', 0).">"; 936 $c2 = "</code>"; 937 $c1 = "</pre>"; 938 $content = $this->shelve($this->r_encode_html(rtrim($content, "\n")."\n")); 939 } 940 elseif ($tag == 'notextile') { 941 $content = $this->shelve($content); 942 $o1 = $o2 = ''; 943 $c1 = $c2 = ''; 944 } 945 elseif ($tag == 'pre') { 946 $content = $this->shelve($this->r_encode_html(rtrim($content, "\n")."\n")); 947 $o1 = "<pre$atts>"; 948 $o2 = $c2 = ''; 949 $c1 = "</pre>"; 950 } 951 elseif ($tag == '###') { 952 $eat = true; 953 } 954 else { 955 $o2 = "\t<$tag$atts>"; 956 $c2 = "</$tag>"; 957 } 958 959 $content = (!$eat) ? $this->graf($content) : ''; 960 961 return array($o1, $o2, $content, $c2, $c1, $eat); 962 } 963 964 // ------------------------------------------------------------- 965 function graf($text) 966 { 967 // handle normal paragraph text 968 if (!$this->lite) { 969 $text = $this->noTextile($text); 970 $text = $this->code($text); 971 } 972 973 $text = $this->getRefs($text); 974 $text = $this->links($text); 975 if (!$this->noimage) 976 $text = $this->image($text); 977 978 if (!$this->lite) { 979 $text = $this->table($text); 980 $text = $this->lists($text); 981 } 982 983 $text = $this->span($text); 984 $text = $this->footnoteRef($text); 985 $text = $this->noteRef($text); 986 $text = $this->glyphs($text); 987 return rtrim($text, "\n"); 988 } 989 990 // ------------------------------------------------------------- 991 function span($text) 992 { 993 $qtags = array('\*\*','\*','\?\?','-','__','_','%','\+','~','\^'); 994 $pnct = ".,\"'?!;:"; 995 $this->span_depth++; 996 997 if( $this->span_depth <= $this->max_span_depth ) 998 { 999 foreach($qtags as $f) 1000 { 1001 $text = preg_replace_callback("/ 1002 (^|(?<=[\s>$pnct\(])|[{[]) # pre 1003 ($f)(?!$f) # tag 1004 ({$this->c}) # atts 1005 (?::(\S+))? # cite 1006 ([^\s$f]+|\S.*?[^\s$f\n]) # content 1007 ([$pnct]*) # end 1008 $f 1009 ($|[\]}]|(?=[[:punct:]]{1,2}|\s|\))) # tail 1010 /x", array(&$this, "fSpan"), $text); 1011 } 1012 } 1013 $this->span_depth--; 1014 return $text; 1015 } 1016 1017 // ------------------------------------------------------------- 1018 function fSpan($m) 1019 { 1020 $qtags = array( 1021 '*' => 'strong', 1022 '**' => 'b', 1023 '??' => 'cite', 1024 '_' => 'em', 1025 '__' => 'i', 1026 '-' => 'del', 1027 '%' => 'span', 1028 '+' => 'ins', 1029 '~' => 'sub', 1030 '^' => 'sup', 1031 ); 1032 1033 list(, $pre, $tag, $atts, $cite, $content, $end, $tail) = $m; 1034 1035 $tag = $qtags[$tag]; 1036 $atts = $this->pba($atts); 1037 $atts .= ($cite != '') ? 'cite="' . $cite . '"' : ''; 1038 1039 $content = $this->span($content); 1040 1041 $opentag = '<'.$tag.$atts.'>'; 1042 $closetag = '</'.$tag.'>'; 1043 $tags = $this->storeTags($opentag, $closetag); 1044 $out = "{$tags['open']}{$content}{$end}{$tags['close']}"; 1045 1046 if (($pre and !$tail) or ($tail and !$pre)) 1047 $out = $pre.$out.$tail; 1048 1049 return $out; 1050 } 1051 1052 // ------------------------------------------------------------- 1053 function storeTags($opentag,$closetag='') 1054 { 1055 $key = ($this->tag_index++); 1056 1057 $key = str_pad( (string)$key, 10, '0', STR_PAD_LEFT ); # $key must be of fixed length to allow proper matching in retrieveTags 1058 $this->tagCache[$key] = array('open'=>$opentag, 'close'=>$closetag); 1059 $tags = array( 1060 'open' => "textileopentag{$key} ", 1061 'close' => " textileclosetag{$key}", 1062 ); 1063 return $tags; 1064 } 1065 1066 // ------------------------------------------------------------- 1067 function retrieveTags($text) 1068 { 1069 $text = preg_replace_callback('/textileopentag([\d]{10}) /' , array(&$this, 'fRetrieveOpenTags'), $text); 1070 $text = preg_replace_callback('/ textileclosetag([\d]{10})/', array(&$this, 'fRetrieveCloseTags'), $text); 1071 return $text; 1072 } 1073 1074 // ------------------------------------------------------------- 1075 function fRetrieveOpenTags($m) 1076 { 1077 list(, $key ) = $m; 1078 return $this->tagCache[$key]['open']; 1079 } 1080 1081 // ------------------------------------------------------------- 1082 function fRetrieveCloseTags($m) 1083 { 1084 list(, $key ) = $m; 1085 return $this->tagCache[$key]['close']; 1086 } 1087 1088 // ------------------------------------------------------------- 1089 function placeNoteLists($text) 1090 { 1091 extract($this->regex_snippets); 1092 1093 # Sequence all referenced definitions... 1094 if( !empty($this->notes) ) { 1095 $o = array(); 1096 foreach( $this->notes as $label=>$info ) { 1097 $i = @$info['seq']; 1098 if( !empty($i) ) { 1099 $info['seq'] = $label; 1100 $o[$i] = $info; 1101 } else { 1102 $this->unreferencedNotes[] = $info; # unreferenced definitions go here for possible future use. 1103 } 1104 } 1105 if( !empty($o) ) ksort($o); 1106 $this->notes = $o; 1107 } 1108 1109 # Replace list markers... 1110 $text = preg_replace_callback("@<p>notelist({$this->c})(?:\:($wrd))?([\^!]?)(\+?)\.[\s]*</p>@U$mod", array(&$this, "fNoteLists"), $text ); 1111 1112 return $text; 1113 } 1114 1115 // ------------------------------------------------------------- 1116 function fParseNoteDefs($m) 1117 { 1118 list(, $label, $link, $att, $content) = $m; 1119 1120 # Assign an id if the note reference parse hasn't found the label yet. 1121 $id = @$this->notes[$label]['id']; 1122 if( !$id ) 1123 $this->notes[$label]['id'] = uniqid(rand()); 1124 1125 if( empty($this->notes[$label]['def']) ) # Ignores subsequent defs using the same label 1126 { 1127 $this->notes[$label]['def'] = array( 1128 'atts' => $this->pba($att), 1129 'content' => $this->graf($content), 1130 'link' => $link, 1131 ); 1132 } 1133 return ''; 1134 } 1135 1136 // ------------------------------------------------------------- 1137 function noteRef($text) 1138 { 1139 $text = preg_replace_callback("/ 1140 \[ # start 1141 ({$this->c}) # !atts 1142 \# 1143 ([^\]!]+?) # !label 1144 ([!]?) # !nolink 1145 \] 1146 /Ux", array(&$this, "fParseNoteRefs"), $text); 1147 return $text; 1148 } 1149 1150 // ------------------------------------------------------------- 1151 function fParseNoteRefs($m) 1152 { 1153 # By the time this function is called, all the defs will have been processed 1154 # into the notes array. So now we can resolve the link numbers in the order 1155 # we process the refs... 1156 1157 list(, $atts, $label, $nolink) = $m; 1158 $atts = $this->pba($atts); 1159 $nolink = ($nolink === '!'); 1160 1161 # Assign a sequence number to this reference if there isn't one already... 1162 $num = @$this->notes[$label]['seq']; 1163 if( !$num ) 1164 $num = $this->notes[$label]['seq'] = ($this->note_index++); 1165 1166 # Make our anchor point & stash it for possible use in backlinks when the 1167 # note list is generated later... 1168 $this->notes[$label]['refids'][] = $refid = uniqid(rand()); 1169 1170 # If we are referencing a note that hasn't had the definition parsed yet, then assign it an ID... 1171 $id = @$this->notes[$label]['id']; 1172 if( !$id ) 1173 $id = $this->notes[$label]['id'] = uniqid(rand()); 1174 1175 # Build the link (if any)... 1176 $_ = '<span id="noteref'.$refid.'">'.$num.'</span>'; 1177 if( !$nolink ) 1178 $_ = '<a href="#note'.$id.'">'.$_.'</a>'; 1179 1180 # Build the reference... 1181 $_ = '<sup'.$atts.'>'.$_.'</sup>'; 1182 1183 return $_; 1184 } 1185 1186 // ------------------------------------------------------------- 1187 function fNoteLists($m) 1188 { 1189 list(, $att, $start_char, $g_links, $extras) = $m; 1190 if( !$start_char ) $start_char = 'a'; 1191 $index = $g_links.$extras.$start_char; 1192 1193 if( empty($this->notelist_cache[$index]) ) { # If not in cache, build the entry... 1194 $o = array(); 1195 1196 if( !empty($this->notes)) { 1197 foreach($this->notes as $seq=>$info) { 1198 $links = $this->makeBackrefLink($info, $g_links, $start_char ); 1199 if( !empty($info['def'])) { 1200 $id = $info['id']; 1201 extract($info['def']); 1202 $o[] = "\t".'<li'.$atts.'>'.$links.'<span id="note'.$id.'"> </span>'.$content.'</li>'; 1203 } else { 1204 $o[] = "\t".'<li'.$atts.'>'.$links.' Undefined Note [#'.$info['seq'].'].</li>'; 1205 } 1206 } 1207 } 1208 if( '+' == $extras && !empty($this->unreferencedNotes) ) { 1209 foreach($this->unreferencedNotes as $seq=>$info) { 1210 if( !empty($info['def'])) { 1211 extract($info['def']); 1212 $o[] = "\t".'<li'.$atts.'>'.$content.'</li>'; 1213 } 1214 } 1215 } 1216 1217 $this->notelist_cache[$index] = join("\n",$o); 1218 } 1219 1220 $_ = ($this->notelist_cache[$index]) ? $this->notelist_cache[$index] : ''; 1221 1222 if( !empty($_) ) { 1223 $list_atts = $this->pba($att); 1224 $_ = "<ol$list_atts>\n$_\n</ol>"; 1225 } 1226 1227 return $_; 1228 } 1229 1230 // ------------------------------------------------------------- 1231 function makeBackrefLink( &$info, $g_links, $i ) 1232 { 1233 $atts = $content = $id = $link = ''; 1234 @extract( $info['def'] ); 1235 $backlink_type = ($link) ? $link : $g_links; 1236 1237 $i_ = strtr( $this->encode_high($i) , array('&'=>'', ';'=>'', '#'=>'')); 1238 $decode = (strlen($i) !== strlen($i_)); 1239 1240 if( $backlink_type === '!' ) 1241 return ''; 1242 elseif( $backlink_type === '^' ) 1243 return '<a href="#noteref'.$info['refids'][0].'"><sup>'.$i.'</sup></a>'; 1244 else { 1245 $_ = array(); 1246 foreach( $info['refids'] as $id ) { 1247 $_[] = '<a href="#noteref'.$id.'"><sup>'. ( ($decode) ? $this->decode_high('&#'.$i_.';') : $i_ ) .'</sup></a>'; 1248 $i_++; 1249 } 1250 $_ = join( ' ', $_ ); 1251 return $_; 1252 } 1253 1254 return ''; 1255 } 1256 1257 // ------------------------------------------------------------- 1258 function links($text) 1259 { 1260 return preg_replace_callback('/ 1261 (^|(?<=[\s>.\(])|[{[]) # $pre 1262 " # start 1263 (' . $this->c . ') # $atts 1264 ([^"]+?) # $text 1265 (?:\(([^)]+?)\)(?="))? # $title 1266 ": 1267 ('.$this->urlch.'+?) # $url 1268 (\/)? # $slash 1269 ([^\w\/;]*?) # $post 1270 ([\]}]|(?=\s|$|\))) 1271 /x', array(&$this, "fLink"), $text); 1272 } 1273 1274 // ------------------------------------------------------------- 1275 function fLink($m) 1276 { 1277 list(, $pre, $atts, $text, $title, $url, $slash, $post, $tail) = $m; 1278 1279 if( '$' === $text ) $text = $url; 1280 1281 $atts = $this->pba($atts); 1282 $atts .= ($title != '') ? ' title="' . $this->encode_html($title) . '"' : ''; 1283 1284 if (!$this->noimage) 1285 $text = $this->image($text); 1286 1287 $text = $this->span($text); 1288 $text = $this->glyphs($text); 1289 $url = $this->shelveURL($url.$slash); 1290 1291 $opentag = '<a href="' . $url . '"' . $atts . $this->rel . '>'; 1292 $closetag = '</a>'; 1293 $tags = $this->storeTags($opentag, $closetag); 1294 $out = $tags['open'].trim($text).$tags['close']; 1295 1296 if (($pre and !$tail) or ($tail and !$pre)) 1297 { 1298 $out = $pre.$out.$post.$tail; 1299 $post = ''; 1300 } 1301 1302 return $this->shelve($out).$post; 1303 } 1304 1305 // ------------------------------------------------------------- 1306 function getRefs($text) 1307 { 1308 return preg_replace_callback("/^\[(.+)\]((?:http:\/\/|\/)\S+)(?=\s|$)/Um", 1309 array(&$this, "refs"), $text); 1310 } 1311 1312 // ------------------------------------------------------------- 1313 function refs($m) 1314 { 1315 list(, $flag, $url) = $m; 1316 $this->urlrefs[$flag] = $url; 1317 return ''; 1318 } 1319 1320 // ------------------------------------------------------------- 1321 function shelveURL($text) 1322 { 1323 if (!$text) return ''; 1324 $ref = md5($text); 1325 $this->urlshelf[$ref] = $text; 1326 return 'urlref:'.$ref; 1327 } 1328 1329 // ------------------------------------------------------------- 1330 function retrieveURLs($text) 1331 { 1332 return preg_replace_callback('/urlref:(\w{32})/', 1333 array(&$this, "retrieveURL"), $text); 1334 } 1335 1336 // ------------------------------------------------------------- 1337 function retrieveURL($m) 1338 { 1339 $ref = $m[1]; 1340 if (!isset($this->urlshelf[$ref])) 1341 return $ref; 1342 $url = $this->urlshelf[$ref]; 1343 if (isset($this->urlrefs[$url])) 1344 $url = $this->urlrefs[$url]; 1345 return $this->r_encode_html($this->relURL($url)); 1346 } 1347 1348 // ------------------------------------------------------------- 1349 function relURL($url) 1350 { 1351 $parts = @parse_url(urldecode($url)); 1352 if ((empty($parts['scheme']) or @$parts['scheme'] == 'http') and 1353 empty($parts['host']) and 1354 preg_match('/^\w/', @$parts['path'])) 1355 $url = $this->hu.$url; 1356 if ($this->restricted and !empty($parts['scheme']) and 1357 !in_array($parts['scheme'], $this->url_schemes)) 1358 return '#'; 1359 return $url; 1360 } 1361 1362 // ------------------------------------------------------------- 1363 function isRelURL($url) 1364 { 1365 $parts = @parse_url($url); 1366 return (empty($parts['scheme']) and empty($parts['host'])); 1367 } 1368 1369 // ------------------------------------------------------------- 1370 function image($text) 1371 { 1372 return preg_replace_callback("/ 1373 (?:[[{])? # pre 1374 \! # opening ! 1375 (\<|\=|\>)? # optional alignment atts 1376 ($this->c) # optional style,class atts 1377 (?:\. )? # optional dot-space 1378 ([^\s(!]+) # presume this is the src 1379 \s? # optional space 1380 (?:\(([^\)]+)\))? # optional title 1381 \! # closing 1382 (?::(\S+))? # optional href 1383 (?:[\]}]|(?=\s|$|\))) # lookahead: space or end of string 1384 /x", array(&$this, "fImage"), $text); 1385 } 1386 1387 // ------------------------------------------------------------- 1388 function fImage($m) 1389 { 1390 list(, $algn, $atts, $url) = $m; 1391 $url = htmlspecialchars($url); 1392 $atts = $this->pba($atts); 1393 $atts .= ($algn != '') ? ' align="' . $this->iAlign($algn) . '"' : ''; 1394 if (isset($m[4])) { 1395 $m[4] = htmlspecialchars($m[4]); 1396 $atts .= ' title="' . $m[4] . '" alt="' . $m[4] . '"'; 1397 } else { 1398 $atts .= ' alt=""'; 1399 } 1400 1401 $size = false; 1402 if ($this->isRelUrl($url)) 1403 $size = @getimagesize(realpath($this->doc_root.ltrim($url, $this->ds))); 1404 if ($size) $atts .= " $size[3]"; 1405 1406 $href = (isset($m[5])) ? $this->shelveURL($m[5]) : ''; 1407 $url = $this->shelveURL($url); 1408 1409 $out = array( 1410 ($href) ? '<a href="' . $href . '">' : '', 1411 '<img src="' . $url . '"' . $atts . ' />', 1412 ($href) ? '</a>' : '' 1413 ); 1414 1415 return $this->shelve(join('',$out)); 1416 } 1417 1418 // ------------------------------------------------------------- 1419 function code($text) 1420 { 1421 $text = $this->doSpecial($text, '<code>', '</code>', 'fCode'); 1422 $text = $this->doSpecial($text, '@', '@', 'fCode'); 1423 $text = $this->doSpecial($text, '<pre>', '</pre>', 'fPre'); 1424 return $text; 1425 } 1426 1427 // ------------------------------------------------------------- 1428 function fCode($m) 1429 { 1430 @list(, $before, $text, $after) = $m; 1431 return $before.$this->shelve('<code>'.$this->r_encode_html($text).'</code>').$after; 1432 } 1433 1434 // ------------------------------------------------------------- 1435 function fPre($m) 1436 { 1437 @list(, $before, $text, $after) = $m; 1438 return $before.'<pre>'.$this->shelve($this->r_encode_html($text)).'</pre>'.$after; 1439 } 1440 1441 // ------------------------------------------------------------- 1442 function shelve($val) 1443 { 1444 $i = uniqid(rand()); 1445 $this->shelf[$i] = $val; 1446 return $i; 1447 } 1448 1449 // ------------------------------------------------------------- 1450 function retrieve($text) 1451 { 1452 if (is_array($this->shelf)) 1453 do { 1454 $old = $text; 1455 $text = strtr($text, $this->shelf); 1456 } while ($text != $old); 1457 1458 return $text; 1459 } 1460 1461 // ------------------------------------------------------------- 1462 // NOTE: deprecated 1463 function incomingEntities($text) 1464 { 1465 return preg_replace("/&(?![#a-z0-9]+;)/i", "x%x%", $text); 1466 } 1467 1468 // ------------------------------------------------------------- 1469 // NOTE: deprecated 1470 function encodeEntities($text) 1471 { 1472 return (function_exists('mb_encode_numericentity')) 1473 ? $this->encode_high($text) 1474 : htmlentities($text, ENT_NOQUOTES, "utf-8"); 1475 } 1476 1477 // ------------------------------------------------------------- 1478 // NOTE: deprecated 1479 function fixEntities($text) 1480 { 1481 /* de-entify any remaining angle brackets or ampersands */ 1482 return str_replace(array(">", "<", "&"), 1483 array(">", "<", "&"), $text); 1484 } 1485 1486 // ------------------------------------------------------------- 1487 function cleanWhiteSpace($text) 1488 { 1489 $out = preg_replace("/^\xEF\xBB\xBF|\x1A/", '', $text); # Byte order mark (if present) 1490 $out = preg_replace("/\r\n?/", "\n", $out); # DOS and MAC line endings to *NIX style endings 1491 $out = preg_replace("/^[ \t]*\n/m", "\n", $out); # lines containing only whitespace 1492 $out = preg_replace("/\n{3,}/", "\n\n", $out); # 3 or more line ends 1493 $out = preg_replace("/^\n*/", "", $out); # leading blank lines 1494 return $out; 1495 } 1496 1497 // ------------------------------------------------------------- 1498 function doSpecial($text, $start, $end, $method='fSpecial') 1499 { 1500 return preg_replace_callback('/(^|\s|[[({>])'.preg_quote($start, '/').'(.*?)'.preg_quote($end, '/').'(\s|$|[\])}])?/ms', 1501 array(&$this, $method), $text); 1502 } 1503 1504 // ------------------------------------------------------------- 1505 function fSpecial($m) 1506 { 1507 // A special block like notextile or code 1508 @list(, $before, $text, $after) = $m; 1509 return $before.$this->shelve($this->encode_html($text)).$after; 1510 } 1511 1512 // ------------------------------------------------------------- 1513 function noTextile($text) 1514 { 1515 $text = $this->doSpecial($text, '<notextile>', '</notextile>', 'fTextile'); 1516 return $this->doSpecial($text, '==', '==', 'fTextile'); 1517 1518 } 1519 1520 // ------------------------------------------------------------- 1521 function fTextile($m) 1522 { 1523 @list(, $before, $notextile, $after) = $m; 1524 #$notextile = str_replace(array_keys($modifiers), array_values($modifiers), $notextile); 1525 return $before.$this->shelve($notextile).$after; 1526 } 1527 1528 // ------------------------------------------------------------- 1529 function footnoteRef($text) 1530 { 1531 return preg_replace('/(?<=\S)\[([0-9]+)([\!]?)\](\s)?/Ue', 1532 '$this->footnoteID(\'\1\',\'\2\',\'\3\')', $text); 1533 } 1534 1535 // ------------------------------------------------------------- 1536 function footnoteID($id, $nolink, $t) 1537 { 1538 $backref = ''; 1539 if (empty($this->fn[$id])) { 1540 $this->fn[$id] = $a = uniqid(rand()); 1541 $backref = 'id="fnrev'.$a.'" '; 1542 } 1543 1544 $fnid = $this->fn[$id]; 1545 1546 $footref = ( '!' == $nolink ) ? $id : '<a href="#fn'.$fnid.'">'.$id.'</a>'; 1547 $footref = '<sup '.$backref.'class="footnote">'.$footref.'</sup>'; 1548 1549 return $footref; 1550 } 1551 1552 // ------------------------------------------------------------- 1553 function glyphs($text) 1554 { 1555 // fix: hackish -- adds a space if final char of text is a double quote. 1556 $text = preg_replace('/"\z/', "\" ", $text); 1557 1558 $text = preg_split("@(<[\w/!?].*>)@Us", $text, -1, PREG_SPLIT_DELIM_CAPTURE); 1559 $i = 0; 1560 foreach($text as $line) { 1561 // text tag text tag text ... 1562 if (++$i % 2) { 1563 // raw < > & chars are already entity encoded in restricted mode 1564 if (!$this->restricted) { 1565 $line = $this->encode_raw_amp($line); 1566 $line = $this->encode_lt_gt($line); 1567 } 1568 $line = preg_replace($this->glyph_search, $this->glyph_replace, $line); 1569 } 1570 $glyph_out[] = $line; 1571 } 1572 return join('', $glyph_out); 1573 } 1574 1575 // ------------------------------------------------------------- 1576 function replaceGlyphs($text) 1577 { 1578 return preg_replace('/glyph:([^<]+)/','$1',$text); 1579 } 1580 1581 // ------------------------------------------------------------- 1582 function iAlign($in) 1583 { 1584 $vals = array( 1585 '<' => 'left', 1586 '=' => 'center', 1587 '>' => 'right'); 1588 return (isset($vals[$in])) ? $vals[$in] : ''; 1589 } 1590 1591 // ------------------------------------------------------------- 1592 function hAlign($in) 1593 { 1594 $vals = array( 1595 '<' => 'left', 1596 '=' => 'center', 1597 '>' => 'right', 1598 '<>' => 'justify'); 1599 return (isset($vals[$in])) ? $vals[$in] : ''; 1600 } 1601 1602 // ------------------------------------------------------------- 1603 function vAlign($in) 1604 { 1605 $vals = array( 1606 '^' => 'top', 1607 '-' => 'middle', 1608 '~' => 'bottom'); 1609 return (isset($vals[$in])) ? $vals[$in] : ''; 1610 } 1611 1612 // ------------------------------------------------------------- 1613 // NOTE: used in notelists 1614 function encode_high($text, $charset = "UTF-8") 1615 { 1616 return mb_encode_numericentity($text, $this->cmap(), $charset); 1617 } 1618 1619 // ------------------------------------------------------------- 1620 // NOTE: used in notelists 1621 function decode_high($text, $charset = "UTF-8") 1622 { 1623 return mb_decode_numericentity($text, $this->cmap(), $charset); 1624 } 1625 1626 // ------------------------------------------------------------- 1627 // NOTE: deprecated 1628 function cmap() 1629 { 1630 $f = 0xffff; 1631 $cmap = array( 1632 0x0080, 0xffff, 0, $f); 1633 return $cmap; 1634 } 1635 1636 // ------------------------------------------------------------- 1637 function encode_raw_amp($text) 1638 { 1639 return preg_replace('/&(?!#?[a-z0-9]+;)/i', '&', $text); 1640 } 1641 1642 // ------------------------------------------------------------- 1643 function encode_lt_gt($text) 1644 { 1645 return strtr($text, array('<' => '<', '>' => '>')); 1646 } 1647 1648 // ------------------------------------------------------------- 1649 function encode_html($str, $quotes=1) 1650 { 1651 $a = array( 1652 '&' => '&', 1653 '<' => '<', 1654 '>' => '>', 1655 ); 1656 if ($quotes) $a = $a + array( 1657 "'" => ''', // numeric, as in htmlspecialchars 1658 '"' => '"', 1659 ); 1660 1661 return strtr($str, $a); 1662 } 1663 1664 // ------------------------------------------------------------- 1665 function r_encode_html($str, $quotes=1) 1666 { 1667 // in restricted mode, input has already been escaped 1668 if ($this->restricted) 1669 return $str; 1670 return $this->encode_html($str, $quotes); 1671 } 1672 1673 // ------------------------------------------------------------- 1674 function textile_popup_help($name, $helpvar, $windowW, $windowH) 1675 { 1676 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 />'; 1677 1678 return $out; 1679 } 1680 1681 // ------------------------------------------------------------- 1682 // NOTE: deprecated 1683 function txtgps($thing) 1684 { 1685 if (isset($_POST[$thing])) { 1686 if (get_magic_quotes_gpc()) { 1687 return stripslashes($_POST[$thing]); 1688 } 1689 else { 1690 return $_POST[$thing]; 1691 } 1692 } 1693 else { 1694 return ''; 1695 } 1696 } 1697 1698 // ------------------------------------------------------------- 1699 // NOTE: deprecated 1700 function dump() 1701 { 1702 static $bool = array( 0=>'false', 1=>'true' ); 1703 foreach (func_get_args() as $a) 1704 echo "\n<pre>",(is_array($a)) ? print_r($a) : ((is_bool($a)) ? $bool[(int)$a] : $a), "</pre>\n"; 1705 return $this; 1706 } 1707 1708 // ------------------------------------------------------------- 1709 1710 function blockLite($text) 1711 { 1712 $this->btag = array('bq', 'p'); 1713 return $this->block($text."\n\n"); 1714 } 1715 1716 1717 } // end class
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Jan 29 16:31:14 2012 | Cross-referenced by PHPXref 0.7.1 |