| Textpattern | PHP Cross Reference | Content Management Systems |
Description: Example: get XHTML from a given Textile-markup string ($string)
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.5.4/source/textpattern/lib/classTextile.php $ 13 $LastChangedRevision: 4096 $ 14 */ 15 16 /* 17 18 _____________ 19 T E X T I L E 20 21 A Humane Web Text Generator 22 23 Version 2.4.1 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 Additions and fixes Copyright (c) 2010 Stef Dawson http://stefdawson.com/ 33 Additions and fixes Copyright (c) 2010-12 Netcarver http://github.com/netcarver 34 Additions and fixes Copyright (c) 2011 Jeff Soo http://ipsedixit.net 35 Additions and fixes Copyright (c) 2012 Robert Wetzlmayr http://wetzlmayr.com/ 36 37 _____________ 38 L I C E N S E 39 40 Redistribution and use in source and binary forms, with or without 41 modification, are permitted provided that the following conditions are met: 42 43 * Redistributions of source code must retain the above copyright notice, 44 this list of conditions and the following disclaimer. 45 46 * Redistributions in binary form must reproduce the above copyright notice, 47 this list of conditions and the following disclaimer in the documentation 48 and/or other materials provided with the distribution. 49 50 * Neither the name Textile nor the names of its contributors may be used to 51 endorse or promote products derived from this software without specific 52 prior written permission. 53 54 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 55 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 56 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 57 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 58 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 59 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 60 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 61 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 62 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 63 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 64 POSSIBILITY OF SUCH DAMAGE. 65 66 _________ 67 U S A G E 68 69 Block modifier syntax: 70 71 Header: h(1-6). 72 Paragraphs beginning with 'hn. ' (where n is 1-6) are wrapped in header tags. 73 Example: h1. Header... -> <h1>Header...</h1> 74 75 Paragraph: p. (also applied by default) 76 Example: p. Text -> <p>Text</p> 77 78 Blockquote: bq. 79 Example: bq. Block quotation... -> <blockquote>Block quotation...</blockquote> 80 81 Blockquote with citation: bq.:http://citation.url 82 Example: bq.:http://textism.com/ Text... 83 -> <blockquote cite="http://textism.com">Text...</blockquote> 84 85 Footnote: fn(1-100). 86 Example: fn1. Footnote... -> <p id="fn1">Footnote...</p> 87 88 Numeric list: #, ## 89 Consecutive paragraphs beginning with # are wrapped in ordered list tags. 90 Example: <ol><li>ordered list</li></ol> 91 92 Bulleted list: *, ** 93 Consecutive paragraphs beginning with * are wrapped in unordered list tags. 94 Example: <ul><li>unordered list</li></ul> 95 96 Definition list: 97 Terms ;, ;; 98 Definitions :, :: 99 Consecutive paragraphs beginning with ; or : are wrapped in definition list tags. 100 Example: <dl><dt>term</dt><dd>definition</dd></dl> 101 102 Redcloth-style Definition list: 103 - Term1 := Definition1 104 - Term2 := Extended 105 definition =: 106 107 Phrase modifier syntax: 108 109 _emphasis_ -> <em>emphasis</em> 110 __italic__ -> <i>italic</i> 111 *strong* -> <strong>strong</strong> 112 **bold** -> <b>bold</b> 113 ??citation?? -> <cite>citation</cite> 114 -deleted text- -> <del>deleted</del> 115 +inserted text+ -> <ins>inserted</ins> 116 ^superscript^ -> <sup>superscript</sup> 117 ~subscript~ -> <sub>subscript</sub> 118 @code@ -> <code>computer code</code> 119 %(bob)span% -> <span class="bob">span</span> 120 121 ==notextile== -> leave text alone (do not format) 122 123 "linktext":url -> <a href="url">linktext</a> 124 "linktext(title)":url -> <a href="url" title="title">linktext</a> 125 "$":url -> <a href="url">url</a> 126 "$(title)":url -> <a href="url" title="title">url</a> 127 128 !imageurl! -> <img src="imageurl" /> 129 !imageurl(alt text)! -> <img src="imageurl" alt="alt text" /> 130 !imageurl!:linkurl -> <a href="linkurl"><img src="imageurl" /></a> 131 132 ABC(Always Be Closing) -> <acronym title="Always Be Closing">ABC</acronym> 133 134 135 Linked Notes: 136 ============ 137 138 Allows the generation of an automated list of notes with links. 139 140 Linked notes are composed of three parts, a set of named _definitions_, a set of 141 _references_ to those definitions and one or more _placeholders_ indicating where 142 the consolidated list of notes is to be placed in your document. 143 144 Definitions. 145 ----------- 146 147 Each note definition must occur in its own paragraph and should look like this... 148 149 note#mynotelabel. Your definition text here. 150 151 You are free to use whatever label you wish after the # as long as it is made up 152 of letters, numbers, colon(:) or dash(-). 153 154 References. 155 ---------- 156 157 Each note reference is marked in your text like this[#mynotelabel] and 158 it will be replaced with a superscript reference that links into the list of 159 note definitions. 160 161 List Placeholder(s). 162 ------------------- 163 164 The note list can go anywhere in your document. You have to indicate where 165 like this... 166 167 notelist. 168 169 notelist can take attributes (class#id) like this: notelist(class#id). 170 171 By default, the note list will show each definition in the order that they 172 are referenced in the text by the _references_. It will show each definition with 173 a full list of backlinks to each reference. If you do not want this, you can choose 174 to override the backlinks like this... 175 176 notelist(class#id)!. Produces a list with no backlinks. 177 notelist(class#id)^. Produces a list with only the first backlink. 178 179 Should you wish to have a specific definition display backlinks differently to this 180 then you can override the backlink method by appending a link override to the 181 _definition_ you wish to customise. 182 183 note#label. Uses the citelist's setting for backlinks. 184 note#label!. Causes that definition to have no backlinks. 185 note#label^. Causes that definition to have one backlink (to the first ref.) 186 note#label*. Causes that definition to have all backlinks. 187 188 Any unreferenced notes will be left out of the list unless you explicitly state 189 you want them by adding a '+'. Like this... 190 191 notelist(class#id)!+. Giving a list of all notes without any backlinks. 192 193 You can mix and match the list backlink control and unreferenced links controls 194 but the backlink control (if any) must go first. Like so: notelist^+. , not 195 like this: notelist+^. 196 197 Example... 198 Scientists say[#lavader] the moon is small. 199 200 note#other. An unreferenced note. 201 202 note#lavader(myliclass). "Proof":url of a small moon. 203 204 notelist(myclass#myid)+. 205 206 Would output (the actual IDs used would be randomised)... 207 208 <p>Scientists say<sup><a href="#def_id_1" id="ref_id_1a">1</sup> the moon is small.</p> 209 210 <ol class="myclass" id="myid"> 211 <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> 212 <li>An unreferenced note.</li> 213 </ol> 214 215 The 'a b c' backlink characters can be altered too. 216 For example if you wanted the notes to have numeric backlinks starting from 1: 217 218 notelist:1. 219 220 Table syntax: 221 222 Simple tables: 223 224 |a|simple|table|row| 225 |And|Another|table|row| 226 |With an||empty|cell| 227 228 |=. My table caption goes here 229 |_. A|_. table|_. header|_.row| 230 |A|simple|table|row| 231 232 Tables with attributes: 233 234 table{border:1px solid black}. My table summary here 235 {background:#ddd;color:red}. |{}| | | | 236 237 To specify thead / tfoot / tbody groups, add one of these on its own line 238 above the row(s) you wish to wrap (you may specify attributes before the dot): 239 240 |^. # thead 241 |-. # tbody 242 |~. # tfoot 243 244 Column groups: 245 246 |:\3. 100| 247 248 Becomes: 249 <colgroup span="3" width="100"></colgroup> 250 251 You can omit either or both of the \N or width values. You may also 252 add cells after the colgroup definition to specify col elements with 253 span, width, or standard Textile attributes: 254 255 |:. 50|(firstcol). |\2. 250||300| 256 257 Becomes: 258 <colgroup width="50"> 259 <col class="firstcol" /> 260 <col span="2" width="250" /> 261 <col /> 262 <col width="300" /> 263 </colgroup> 264 265 (Note that, per the HTML specification, you should not add span 266 to the colgroup if specifying col elements.) 267 268 Applying Attributes: 269 270 Most anywhere Textile code is used, attributes such as arbitrary css style, 271 css classes, and ids can be applied. The syntax is fairly consistent. 272 273 The following characters quickly alter the alignment of block elements: 274 275 < -> left align ex. p<. left-aligned para 276 > -> right align h3>. right-aligned header 3 277 = -> centred h4=. centred header 4 278 <> -> justified p<>. justified paragraph 279 280 These will change vertical alignment in table cells: 281 282 ^ -> top ex. |^. top-aligned table cell| 283 - -> middle |-. middle aligned| 284 ~ -> bottom |~. bottom aligned cell| 285 286 Plain (parentheses) inserted between block syntax and the closing dot-space 287 indicate classes and ids: 288 289 p(hector). paragraph -> <p class="hector">paragraph</p> 290 291 p(#fluid). paragraph -> <p id="fluid">paragraph</p> 292 293 (classes and ids can be combined) 294 p(hector#fluid). paragraph -> <p class="hector" id="fluid">paragraph</p> 295 296 Curly {brackets} insert arbitrary css style 297 298 p{line-height:18px}. paragraph -> <p style="line-height:18px">paragraph</p> 299 300 h3{color:red}. header 3 -> <h3 style="color:red">header 3</h3> 301 302 Square [brackets] insert language attributes 303 304 p[no]. paragraph -> <p lang="no">paragraph</p> 305 306 %[fr]phrase% -> <span lang="fr">phrase</span> 307 308 Usually Textile block element syntax requires a dot and space before the block 309 begins, but since lists don't, they can be styled just using braces 310 311 #{color:blue} one -> <ol style="color:blue"> 312 # big <li>one</li> 313 # list <li>big</li> 314 <li>list</li> 315 </ol> 316 317 Using the span tag to style a phrase 318 319 It goes like this, %{color:red}the fourth the fifth% 320 -> It goes like this, <span style="color:red">the fourth the fifth</span> 321 322 Ordered List Start & Continuation: 323 324 You can control the start attribute of an ordered list like so; 325 326 #5 Item 5 327 # Item 6 328 329 You can resume numbering list items after some intervening anonymous block like so... 330 331 #_ Item 7 332 # Item 8 333 334 */ 335 336 // define these before including this file to override the standard glyphs 337 @define('txt_quote_single_open', '‘'); 338 @define('txt_quote_single_close', '’'); 339 @define('txt_quote_double_open', '“'); 340 @define('txt_quote_double_close', '”'); 341 @define('txt_apostrophe', '’'); 342 @define('txt_prime', '′'); 343 @define('txt_prime_double', '″'); 344 @define('txt_ellipsis', '…'); 345 @define('txt_emdash', '—'); 346 @define('txt_endash', '–'); 347 @define('txt_dimension', '×'); 348 @define('txt_trademark', '™'); 349 @define('txt_registered', '®'); 350 @define('txt_copyright', '©'); 351 @define('txt_half', '½'); 352 @define('txt_quarter', '¼'); 353 @define('txt_threequarters', '¾'); 354 @define('txt_degrees', '°'); 355 @define('txt_plusminus', '±'); 356 @define('txt_has_unicode', @preg_match('/\pL/u', 'a')); // Detect if Unicode is compiled into PCRE 357 @define('txt_fn_ref_pattern', '<sup{atts}>{marker}</sup>'); 358 @define('txt_fn_foot_pattern', '<sup{atts}>{marker}</sup>'); 359 @define('txt_nl_ref_pattern', '<sup{atts}>{marker}</sup>'); 360 361 class Textile 362 { 363 var $hlgn; 364 var $vlgn; 365 var $clas; 366 var $lnge; 367 var $styl; 368 var $cspn; 369 var $rspn; 370 var $a; 371 var $s; 372 var $c; 373 var $pnct; 374 var $rel; 375 var $fn; 376 377 var $shelf = array(); 378 var $restricted = false; 379 var $noimage = false; 380 var $lite = false; 381 var $url_schemes = array(); 382 var $glyph = array(); 383 var $hu = ''; 384 var $max_span_depth = 5; 385 386 var $ver = '2.4.1'; 387 var $rev = '$Rev: 4096 $'; 388 389 var $doc_root; 390 391 var $doctype; 392 393 // ------------------------------------------------------------- 394 function Textile( $doctype = 'xhtml' ) 395 { 396 $doctype_whitelist = array( # All lower case please... 397 'xhtml', 398 'html5', 399 ); 400 $doctype = strtolower( $doctype ); 401 if( !in_array( $doctype, $doctype_whitelist ) ) 402 $this->doctype = 'xhtml'; 403 else 404 $this->doctype = $doctype; 405 406 $this->hlgn = "(?:\<(?!>)|(?<!<)\>|\<\>|\=|[()]+(?! ))"; 407 $this->vlgn = "[\-^~]"; 408 $this->clas = "(?:\([^)\n]+\))"; # Don't allow classes/ids/languages/styles to span across newlines 409 $this->lnge = "(?:\[[^]\n]+\])"; 410 $this->styl = "(?:\{[^}\n]+\})"; 411 $this->cspn = "(?:\\\\\d+)"; 412 $this->rspn = "(?:\/\d+)"; 413 $this->a = "(?:{$this->hlgn}|{$this->vlgn})*"; 414 $this->s = "(?:{$this->cspn}|{$this->rspn})*"; 415 $this->c = "(?:{$this->clas}|{$this->styl}|{$this->lnge}|{$this->hlgn})*"; 416 $this->lc = "(?:{$this->clas}|{$this->styl}|{$this->lnge})*"; 417 418 $this->pnct = '[\!"#\$%&\'()\*\+,\-\./:;<=>\?@\[\\\]\^_`{\|}\~]'; 419 $this->urlch = '[\w"$\-_.+!*\'(),";\/?:@=&%#{}|\\^~\[\]`]'; 420 $this->syms = '¤§µ¶†‡•∗∴◊♠♣♥♦'; 421 422 $pnc = '[[:punct:]]'; 423 424 $this->restricted_url_schemes = array('http','https','ftp','mailto'); 425 $this->unrestricted_url_schemes = array('http','https','ftp','mailto','file','tel','callto','sftp'); 426 427 $this->btag = array('bq', 'bc', 'notextile', 'pre', 'h[1-6]', 'fn\d+', 'p', '###' ); 428 429 if (txt_has_unicode) { 430 $this->regex_snippets = array( 431 'acr' => '\p{Lu}\p{Nd}', 432 'abr' => '\p{Lu}', 433 'nab' => '\p{Ll}', 434 'wrd' => '(?:\p{L}|\p{M}|\p{N}|\p{Pc})', 435 'mod' => 'u', # Make sure to mark the unicode patterns as such, Some servers seem to need this. 436 ); 437 } else { 438 $this->regex_snippets = array( 439 'acr' => 'A-Z0-9', 440 'abr' => 'A-Z', 441 'nab' => 'a-z', 442 'wrd' => '\w', 443 'mod' => '', 444 ); 445 } 446 extract( $this->regex_snippets ); 447 $this->urlch = '['.$wrd.'"$\-_.+!*\'(),";\/?:@=&%#{}|\\^~\[\]`]'; 448 449 $this->glyph_search = array( 450 '/('.$wrd.'|\))\'('.$wrd.')/'.$mod, // I'm an apostrophe 451 '/(\s)\'(\d+'.$wrd.'?)\b(?![.]?['.$wrd.']*?\')/'.$mod, // back in '88/the '90s but not in his '90s', '1', '1.' '10m' or '5.png' 452 '/(\S)\'(?=\s|'.$pnc.'|<|$)/', // single closing 453 '/\'/', // single opening 454 '/(\S)\"(?=\s|'.$pnc.'|<|$)/', // double closing 455 '/"/', // double opening 456 '/\b(['.$abr.']['.$acr.']{2,})\b(?:[(]([^)]*)[)])/'.$mod, // 3+ uppercase acronym 457 '/(?<=\s|^|[>(;-])(['.$abr.']{3,})(['.$nab.']*)(?=\s|'.$pnc.'|<|$)(?=[^">]*?(<|$))/'.$mod, // 3+ uppercase 458 '/([^.]?)\.{3}/', // ellipsis 459 '/(\s?)--(\s?)/', // em dash 460 '/( )-( )/', // en dash 461 '/(\d+)( ?)x( ?)(?=\d+)/', // dimension sign 462 '/(\b ?|\s|^)[([]TM[])]/i', // trademark 463 '/(\b ?|\s|^)[([]R[])]/i', // registered 464 '/(\b ?|\s|^)[([]C[])]/i', // copyright 465 '/[([]1\/4[])]/', // 1/4 466 '/[([]1\/2[])]/', // 1/2 467 '/[([]3\/4[])]/', // 3/4 468 '/[([]o[])]/', // degrees -- that's a small 'oh' 469 '/[([]\+\/-[])]/', // plus minus 470 ); 471 472 $this->glyph_replace = array( 473 '$1'.txt_apostrophe.'$2', // I'm an apostrophe 474 '$1'.txt_apostrophe.'$2', // back in '88 475 '$1'.txt_quote_single_close, // single closing 476 txt_quote_single_open, // single opening 477 '$1'.txt_quote_double_close, // double closing 478 txt_quote_double_open, // double opening 479 (('html5' === $this->doctype) ? '<abbr title="$2">$1</abbr>' : '<acronym title="$2">$1</acronym>'), // 3+ uppercase acronym 480 '<span class="caps">glyph:$1</span>$2', // 3+ uppercase 481 '$1'.txt_ellipsis, // ellipsis 482 '$1'.txt_emdash.'$2', // em dash 483 '$1'.txt_endash.'$2', // en dash 484 '$1$2'.txt_dimension.'$3', // dimension sign 485 '$1'.txt_trademark, // trademark 486 '$1'.txt_registered, // registered 487 '$1'.txt_copyright, // copyright 488 txt_quarter, // 1/4 489 txt_half, // 1/2 490 txt_threequarters, // 3/4 491 txt_degrees, // degrees 492 txt_plusminus, // plus minus 493 ); 494 495 if (defined('hu')) 496 $this->hu = hu; 497 498 if (defined('DIRECTORY_SEPARATOR')) 499 $this->ds = constant('DIRECTORY_SEPARATOR'); 500 else 501 $this->ds = '/'; 502 503 $this->doc_root = @$_SERVER['DOCUMENT_ROOT']; 504 if (!$this->doc_root) 505 $this->doc_root = @$_SERVER['PATH_TRANSLATED']; // IIS 506 507 $this->doc_root = rtrim($this->doc_root, $this->ds).$this->ds; 508 } 509 510 // ------------------------------------------------------------- 511 512 function TextileThis($text, $lite = '', $encode = '', $noimage = '', $strict = '', $rel = '') 513 { 514 $this->span_depth = 0; 515 $this->tag_index = 1; 516 $this->notes = $this->unreferencedNotes = $this->notelist_cache = array(); 517 $this->note_index = 1; 518 $this->rel = ($rel) ? ' rel="'.$rel.'"' : ''; 519 520 $this->lite = $lite; 521 $this->noimage = $noimage; 522 523 $this->url_schemes = $this->unrestricted_url_schemes; 524 525 if ($encode) 526 { 527 $text = $this->incomingEntities($text); 528 $text = str_replace("x%x%", "&", $text); 529 return $text; 530 } else { 531 if(!$strict) { 532 $text = $this->cleanWhiteSpace($text); 533 } 534 535 if(!$lite) { 536 $text = $this->block($text); 537 $text = $this->placeNoteLists($text); 538 } 539 540 $text = $this->retrieve($text); 541 $text = $this->replaceGlyphs($text); 542 $text = $this->retrieveTags($text); 543 $text = $this->retrieveURLs($text); 544 $this->span_depth = 0; 545 546 // just to be tidy 547 $text = str_replace("<br />", "<br />\n", $text); 548 549 return $text; 550 } 551 } 552 553 // ------------------------------------------------------------- 554 555 function TextileRestricted($text, $lite = 1, $noimage = 1, $rel = 'nofollow') 556 { 557 $this->restricted = true; 558 $this->lite = $lite; 559 $this->noimage = $noimage; 560 561 $this->url_schemes = $this->restricted_url_schemes; 562 563 $this->span_depth = 0; 564 $this->tag_index = 1; 565 $this->notes = $this->unreferencedNotes = $this->notelist_cache = array(); 566 $this->note_index = 1; 567 568 $this->rel = ($rel) ? ' rel="'.$rel.'"' : ''; 569 570 // escape any raw html 571 $text = $this->encode_html($text, 0); 572 $text = $this->cleanWhiteSpace($text); 573 574 if($lite) { 575 $text = $this->blockLite($text); 576 } else { 577 $text = $this->block($text); 578 $text = $this->placeNoteLists($text); 579 } 580 581 $text = $this->retrieve($text); 582 $text = $this->replaceGlyphs($text); 583 $text = $this->retrieveTags($text); 584 $text = $this->retrieveURLs($text); 585 $this->span_depth = 0; 586 587 // just to be tidy 588 $text = str_replace("<br />", "<br />\n", $text); 589 590 return $text; 591 } 592 593 // ------------------------------------------------------------- 594 function cleanba( $in ) 595 { 596 $tmp = $in; 597 $before = -1; 598 $after = 0; 599 $max = 3; 600 $i = 0; 601 while( ($after != $before) && ($i < $max) ) 602 { 603 $before = strlen( $tmp ); 604 $tmp = rawurldecode($tmp); 605 $after = strlen( $tmp ); 606 $i++; 607 } 608 609 if( $i === $max ) # If we hit the max allowed decodes, assume the input is tainted and consume it. 610 $out = ''; 611 else 612 $out = strtr( $tmp, array( 613 '"'=>'', 614 "'"=>'', 615 '='=>'', 616 )); 617 return $out; 618 } 619 620 // ------------------------------------------------------------- 621 function pba($in, $element = "", $include_id = 1, $autoclass = '') // "parse block attributes" 622 { 623 $style = ''; 624 $class = ''; 625 $lang = ''; 626 $colspan = ''; 627 $rowspan = ''; 628 $span = ''; 629 $width = ''; 630 $id = ''; 631 $atts = ''; 632 $align = ''; 633 634 $matched = $in; 635 if ($element == 'td') { 636 if (preg_match("/\\\\(\d+)/", $matched, $csp)) $colspan = $csp[1]; 637 if (preg_match("/\/(\d+)/", $matched, $rsp)) $rowspan = $rsp[1]; 638 } 639 640 if ($element == 'td' or $element == 'tr') { 641 if (preg_match("/($this->vlgn)/", $matched, $vert)) 642 $style[] = "vertical-align:" . $this->vAlign($vert[1]); 643 } 644 645 if (preg_match("/\{([^}]*)\}/", $matched, $sty)) { 646 $style[] = rtrim($sty[1], ';'); 647 $matched = str_replace($sty[0], '', $matched); 648 } 649 650 if (preg_match("/\[([^]]+)\]/U", $matched, $lng)) { 651 $matched = str_replace($lng[0], '', $matched); # Consume entire lang block -- valid or invalid... 652 if (preg_match("/\[([a-zA-Z]{2}(?:[\-\_][a-zA-Z]{2})?)\]/U", $lng[0], $lng)) { 653 $lang = $lng[1]; 654 } 655 } 656 657 if (preg_match("/\(([^()]+)\)/U", $matched, $cls)) { 658 $matched = str_replace($cls[0], '', $matched); # Consume entire class block -- valid or invalid... 659 # Only allow a restricted subset of the CSS standard characters for classes/ids. No encoding markers allowed... 660 if (preg_match("/\(([-a-zA-Z 0-9_\.\:\#]+)\)/U", $cls[0], $cls)) { 661 $hashpos = strpos( $cls[1], '#' ); 662 # If a textile class block attribute was found with a '#' in it 663 # split it into the css class and css id... 664 if( false !== $hashpos ) { 665 if (preg_match("/#([-a-zA-Z0-9_\.\:]*)$/", substr( $cls[1], $hashpos ), $ids)) 666 $id = $ids[1]; 667 668 if (preg_match("/^([-a-zA-Z 0-9_]*)/", substr( $cls[1], 0, $hashpos ), $ids)) 669 $class = $ids[1]; 670 } 671 else { 672 if (preg_match("/^([-a-zA-Z 0-9_]*)$/", $cls[1], $ids)) 673 $class = $ids[1]; 674 } 675 } 676 } 677 678 if (preg_match("/([(]+)/", $matched, $pl)) { 679 $style[] = "padding-left:" . strlen($pl[1]) . "em"; 680 $matched = str_replace($pl[0], '', $matched); 681 } 682 683 if (preg_match("/([)]+)/", $matched, $pr)) { 684 $style[] = "padding-right:" . strlen($pr[1]) . "em"; 685 $matched = str_replace($pr[0], '', $matched); 686 } 687 688 if (preg_match("/($this->hlgn)/", $matched, $horiz)) 689 $style[] = "text-align:" . $this->hAlign($horiz[1]); 690 691 if ($element == 'col') { 692 if (preg_match("/(?:\\\\(\d+))?\s*(\d+)?/", $matched, $csp)) { 693 $span = isset($csp[1]) ? $csp[1] : ''; 694 $width = isset($csp[2]) ? $csp[2] : ''; 695 } 696 } 697 698 if ($this->restricted) { 699 $class = trim( $autoclass ); 700 return join( '', array( 701 ($lang) ? ' lang="' . $this->cleanba($lang) . '"': '', 702 ($class) ? ' class="' . $this->cleanba($class) . '"': '', 703 )); 704 } 705 else 706 $class = trim( $class . ' ' . $autoclass ); 707 708 $o = ''; 709 if( $style ) { 710 foreach($style as $s) { 711 $parts = explode(';', $s); 712 foreach( $parts as $p ) { 713 $p = trim($p, '; '); 714 if( !empty( $p ) ) 715 $o .= $p.'; '; 716 } 717 } 718 $style = trim( strtr($o, array("\n"=>'',';;'=>';')) ); 719 } 720 721 return join('',array( 722 ($style) ? ' style="' . $this->cleanba($style) .'"' : '', 723 ($class) ? ' class="' . $this->cleanba($class) .'"' : '', 724 ($lang) ? ' lang="' . $this->cleanba($lang) .'"' : '', 725 ($id and $include_id) ? ' id="' . $this->cleanba($id) .'"' : '', 726 ($colspan) ? ' colspan="' . $this->cleanba($colspan) .'"' : '', 727 ($rowspan) ? ' rowspan="' . $this->cleanba($rowspan) .'"' : '', 728 ($span) ? ' span="' . $this->cleanba($span) .'"' : '', 729 ($width) ? ' width="' . $this->cleanba($width) .'"' : '', 730 )); 731 } 732 733 // ------------------------------------------------------------- 734 function hasRawText($text) 735 { 736 // checks whether the text has text not already enclosed by a block tag 737 $r = trim(preg_replace('@<(p|blockquote|div|form|table|ul|ol|dl|pre|h\d)[^>]*?'.chr(62).'.*</\1>@s', '', trim($text))); 738 $r = trim(preg_replace('@<(hr|br)[^>]*?/>@', '', $r)); 739 return '' != $r; 740 } 741 742 // ------------------------------------------------------------- 743 function table($text) 744 { 745 $text = $text . "\n\n"; 746 return preg_replace_callback("/^(?:table(_?{$this->s}{$this->a}{$this->c})\.(.*)?\n)?^({$this->a}{$this->c}\.? ?\|.*\|)[\s]*\n\n/smU", 747 array(&$this, "fTable"), $text); 748 } 749 750 // ------------------------------------------------------------- 751 function fTable($matches) 752 { 753 $tatts = $this->pba($matches[1], 'table'); 754 755 $sum = trim($matches[2]) ? ' summary="'.htmlspecialchars(trim($matches[2])).'"' : ''; 756 $cap = ''; 757 $colgrp = $last_rgrp = ''; 758 $c_row = 1; 759 foreach(preg_split("/\|\s*?$/m", $matches[3], -1, PREG_SPLIT_NO_EMPTY) as $row) { 760 761 $row = ltrim($row); 762 763 // Caption -- can only occur on row 1, otherwise treat '|=. foo |...' as a normal center-aligned cell. 764 if ( ($c_row <= 1) && preg_match("/^\|\=($this->s$this->a$this->c)\. ([^\n]*)(.*)/s", ltrim($row), $cmtch)) { 765 $capts = $this->pba($cmtch[1]); 766 $cap = "\t<caption".$capts.">".trim($cmtch[2])."</caption>\n"; 767 $row = ltrim($cmtch[3]); 768 if( empty($row) ) 769 continue; 770 } 771 $c_row += 1; 772 773 // Colgroup 774 if (preg_match("/^\|:($this->s$this->a$this->c\. .*)/m", ltrim($row), $gmtch)) { 775 $nl = strpos($row,"\n"); # Is this colgroup def missing a closing pipe? If so, there will be a newline in the middle of $row somewhere. 776 $idx=0; 777 foreach (explode('|', str_replace('.', '', $gmtch[1])) as $col) { 778 $gatts = $this->pba(trim($col), 'col'); 779 $colgrp .= "\t<col".(($idx==0) ? "group".$gatts.">" : $gatts." />")."\n"; 780 $idx++; 781 } 782 $colgrp .= "\t</colgroup>\n"; 783 784 if($nl === false) { 785 continue; 786 } 787 else { 788 $row = ltrim(substr( $row, $nl )); # Recover from our missing pipe and process the rest of the line... 789 } 790 } 791 792 preg_match("/(:?^\|($this->vlgn)($this->s$this->a$this->c)\.\s*$\n)?^(.*)/sm", ltrim($row), $grpmatch); 793 794 // Row group 795 $rgrp = isset($grpmatch[2]) ? (($grpmatch[2] == '^') ? 'head' : ( ($grpmatch[2] == '~') ? 'foot' : (($grpmatch[2] == '-') ? 'body' : '' ) ) ) : ''; 796 $rgrpatts = isset($grpmatch[3]) ? $this->pba($grpmatch[3]) : ''; 797 $row = $grpmatch[4]; 798 799 if (preg_match("/^($this->a$this->c\. )(.*)/m", ltrim($row), $rmtch)) { 800 $ratts = $this->pba($rmtch[1], 'tr'); 801 $row = $rmtch[2]; 802 } else $ratts = ''; 803 804 $cells = array(); 805 $cellctr = 0; 806 foreach(explode("|", $row) as $cell) { 807 $ctyp = "d"; 808 if (preg_match("/^_/", $cell)) $ctyp = "h"; 809 if (preg_match("/^(_?$this->s$this->a$this->c\. )(.*)/", $cell, $cmtch)) { 810 $catts = $this->pba($cmtch[1], 'td'); 811 $cell = $cmtch[2]; 812 } else $catts = ''; 813 814 if (!$this->lite) { 815 $cell = $this->redcloth_lists($cell); 816 $cell = $this->lists($cell); 817 } 818 819 if ($cellctr>0) // Ignore first 'cell': it precedes the opening pipe 820 $cells[] = $this->doTagBr("t$ctyp", "\t\t\t<t$ctyp$catts>$cell</t$ctyp>"); 821 822 $cellctr++; 823 } 824 $grp = (($rgrp && $last_rgrp) ? "\t</t".$last_rgrp.">\n" : '') . (($rgrp) ? "\t<t".$rgrp.$rgrpatts.">\n" : ''); 825 $last_rgrp = ($rgrp) ? $rgrp : $last_rgrp; 826 $rows[] = $grp."\t\t<tr$ratts>\n" . join("\n", $cells) . ($cells ? "\n" : "") . "\t\t</tr>"; 827 unset($cells, $catts); 828 } 829 830 return "\t<table{$tatts}{$sum}>\n" .$cap. $colgrp. join("\n", $rows) . "\n".(($last_rgrp) ? "\t</t".$last_rgrp.">\n" : '')."\t</table>\n\n"; 831 } 832 833 // ------------------------------------------------------------- 834 function redcloth_lists($text) 835 { 836 return preg_replace_callback("/^([-]+$this->lc[ .].*:=.*)$(?![^-])/smU", array(&$this, "fRCList"), $text); 837 } 838 839 // ------------------------------------------------------------- 840 function fRCList($m) 841 { 842 $out = array(); 843 $text = preg_split('/\n(?=[-])/m', $m[0]); 844 foreach($text as $nr => $line) { 845 if (preg_match("/^[-]+($this->lc)[ .](.*)$/s", $line, $m)) { 846 list(, $atts, $content) = $m; 847 $content = trim($content); 848 $atts = $this->pba($atts); 849 850 preg_match( "/^(.*?)[\s]*:=(.*?)[\s]*(=:|:=)?[\s]*$/s", $content, $xm ); 851 list( , $term, $def, ) = $xm; 852 $term = trim( $term ); 853 $def = trim( $def, ' ' ); 854 855 if( empty( $out ) ) { 856 if(''==$def) 857 $out[] = "<dl$atts>"; 858 else 859 $out[] = '<dl>'; 860 } 861 862 if( '' != $def && '' != $term ) 863 { 864 $pos = strpos( $def, "\n" ); 865 $def = str_replace( "\n", "<br />", trim($def) ); 866 if( 0 === $pos ) 867 $def = '<p>' . $def . '</p>'; 868 869 $term = $this->graf($term); 870 $def = $this->graf($def); 871 872 $out[] = "\t<dt$atts>$term</dt>"; 873 $out[] = "\t<dd>$def</dd>"; 874 } 875 } 876 } 877 $out[] = '</dl>'; 878 return implode("\n", $out); 879 } 880 881 882 // ------------------------------------------------------------- 883 function lists($text) 884 { 885 return preg_replace_callback("/^((?:[*;:]+|[*;:#]*#(?:_|\d+)?)$this->lc[ .].*)$(?![^#*;:])/smU", array(&$this, "fList"), $text); 886 } 887 888 // ------------------------------------------------------------- 889 function fList($m) 890 { 891 $text = preg_split('/\n(?=[*#;:])/m', $m[0]); 892 $pt = ''; 893 foreach($text as $nr => $line) { 894 $nextline = isset($text[$nr+1]) ? $text[$nr+1] : false; 895 if (preg_match("/^([#*;:]+)(_|\d+)?($this->lc)[ .](.*)$/s", $line, $m)) { 896 list(, $tl, $st, $atts, $content) = $m; 897 $content = trim($content); 898 $nl = ''; 899 $ltype = $this->lT($tl); 900 $litem = (strpos($tl, ';') !== false) ? 'dt' : ((strpos($tl, ':') !== false) ? 'dd' : 'li'); 901 $showitem = (strlen($content) > 0); 902 903 if( 'o' === $ltype ) { // handle list continuation/start attribute on ordered lists... 904 if( !isset($this->olstarts[$tl]) ) 905 $this->olstarts[$tl] = 1; 906 907 if( strlen($tl) > strlen($pt) ) { // first line of this level of ol -- has a start attribute? 908 if( '' == $st ) 909 $this->olstarts[$tl] = 1; // no => reset count to 1. 910 elseif( '_' !== $st ) 911 $this->olstarts[$tl] = (int)$st; // yes, and numeric => reset to given. 912 // TRICKY: the '_' continuation marker just means 913 // output the count so don't need to do anything 914 // here. 915 } 916 917 if( (strlen($tl) > strlen($pt)) && '' !== $st) // output the start attribute if needed... 918 $st = ' start="' . $this->olstarts[$tl] . '"'; 919 920 if( $showitem ) // TRICKY: Only increment the count for list items; not when a list definition line is encountered. 921 $this->olstarts[$tl] += 1; 922 } 923 924 if (preg_match("/^([#*;:]+)(_|[\d]+)?($this->lc)[ .].*/", $nextline, $nm)) 925 $nl = $nm[1]; 926 927 if ((strpos($pt, ';') !== false) && (strpos($tl, ':') !== false)) { 928 $lists[$tl] = 2; // We're already in a <dl> so flag not to start another 929 } 930 931 $atts = $this->pba($atts); 932 if (!isset($lists[$tl])) { 933 $lists[$tl] = 1; 934 $line = "\t<" . $ltype . "l$atts$st>" . (($showitem) ? "\n\t\t<$litem>" . $content : ''); 935 } else { 936 $line = ($showitem) ? "\t\t<$litem$atts>" . $content : ''; 937 } 938 939 if((strlen($nl) <= strlen($tl))) $line .= (($showitem) ? "</$litem>" : ''); 940 foreach(array_reverse($lists) as $k => $v) { 941 if(strlen($k) > strlen($nl)) { 942 $line .= ($v==2) ? '' : "\n\t</" . $this->lT($k) . "l>"; 943 if((strlen($k) > 1) && ($v != 2)) 944 $line .= "</".$litem.">"; 945 unset($lists[$k]); 946 } 947 } 948 $pt = $tl; // Remember the current Textile tag 949 } 950 else { 951 $line .= "\n"; 952 } 953 $out[] = $line; 954 } 955 return $this->doTagBr($litem, join("\n", $out)); 956 } 957 958 // ------------------------------------------------------------- 959 function lT($in) 960 { 961 return preg_match("/^#+/", $in) ? 'o' : ((preg_match("/^\*+/", $in)) ? 'u' : 'd'); 962 } 963 964 // ------------------------------------------------------------- 965 function doTagBr($tag, $in) 966 { 967 return preg_replace_callback('@<('.preg_quote($tag).')([^>]*?)>(.*)(</\1>)@s', array(&$this, 'fBr'), $in); 968 } 969 970 // ------------------------------------------------------------- 971 function doPBr($in) 972 { 973 return preg_replace_callback('@<(p)([^>]*?)>(.*)(</\1>)@s', array(&$this, 'fPBr'), $in); 974 } 975 976 // ------------------------------------------------------------- 977 function fPBr($m) 978 { 979 # Less restrictive version of fBr() ... used only in paragraphs where the next 980 # row may start with a smiley or perhaps something like '#8 bolt...' or '*** stars...' 981 $content = preg_replace("@(.+)(?<!<br>|<br />)\n(?![\s|])@", '$1<br />', $m[3]); 982 return '<'.$m[1].$m[2].'>'.$content.$m[4]; 983 } 984 985 // ------------------------------------------------------------- 986 function fBr($m) 987 { 988 $content = preg_replace("@(.+)(?<!<br>|<br />)\n(?![#*;:\s|])@", '$1<br />', $m[3]); 989 return '<'.$m[1].$m[2].'>'.$content.$m[4]; 990 } 991 992 // ------------------------------------------------------------- 993 function block($text) 994 { 995 $find = $this->btag; 996 $tre = join('|', $find); 997 998 $text = explode("\n\n", $text); 999 1000 $tag = 'p'; 1001 $atts = $cite = $graf = $ext = ''; 1002 $eat = false; 1003 1004 $out = array(); 1005 1006 foreach($text as $line) { 1007 $anon = 0; 1008 if (preg_match("/^($tre)($this->a$this->c)\.(\.?)(?::(\S+))? (.*)$/s", $line, $m)) { 1009 // last block was extended, so close it 1010 if ($ext) 1011 $out[count($out)-1] .= $c1; 1012 // new block 1013 list(,$tag,$atts,$ext,$cite,$graf) = $m; 1014 list($o1, $o2, $content, $c2, $c1, $eat) = $this->fBlock(array(0,$tag,$atts,$ext,$cite,$graf)); 1015 1016 // leave off c1 if this block is extended, we'll close it at the start of the next block 1017 if ($ext) 1018 $line = $o1.$o2.$content.$c2; 1019 else 1020 $line = $o1.$o2.$content.$c2.$c1; 1021 } 1022 else { 1023 // anonymous block 1024 $anon = 1; 1025 if ($ext or !preg_match('/^ /', $line)) { 1026 list($o1, $o2, $content, $c2, $c1, $eat) = $this->fBlock(array(0,$tag,$atts,$ext,$cite,$line)); 1027 // skip $o1/$c1 because this is part of a continuing extended block 1028 if ($tag == 'p' and !$this->hasRawText($content)) { 1029 $line = $content; 1030 } 1031 else { 1032 $line = $o2.$content.$c2; 1033 } 1034 } 1035 else { 1036 $line = $this->graf($line); 1037 } 1038 } 1039 1040 $line = $this->doPBr($line); 1041 $line = preg_replace('/<br>/', '<br />', $line); 1042 1043 if ($ext and $anon) 1044 $out[count($out)-1] .= "\n".$line; 1045 elseif(!$eat) 1046 $out[] = $line; 1047 1048 if (!$ext) { 1049 $tag = 'p'; 1050 $atts = ''; 1051 $cite = ''; 1052 $graf = ''; 1053 $eat = false; 1054 } 1055 } 1056 if ($ext) $out[count($out)-1] .= $c1; 1057 return join("\n\n", $out); 1058 } 1059 1060 // ------------------------------------------------------------- 1061 function formatFootnote( $marker, $atts='', $anchor=true ) 1062 { 1063 $pattern = ($anchor) ? txt_fn_foot_pattern : txt_fn_ref_pattern; 1064 return $this->replaceMarkers( $pattern, array( 'atts' => $atts, 'marker' => $marker ) ); 1065 } 1066 1067 // ------------------------------------------------------------- 1068 function replaceMarkers( $text, $replacements ) 1069 { 1070 if( !empty( $replacements ) ) 1071 foreach( $replacements as $k => $r ) 1072 $text = str_replace( '{'.$k.'}', $r, $text ); 1073 return $text; 1074 } 1075 1076 // ------------------------------------------------------------- 1077 function fBlock($m) 1078 { 1079 extract($this->regex_snippets); 1080 list(, $tag, $att, $ext, $cite, $content) = $m; 1081 $atts = $this->pba($att); 1082 1083 $o1 = $o2 = $c2 = $c1 = ''; 1084 $eat = false; 1085 1086 if( $tag === 'p' ) { 1087 # Is this an anonymous block with a note definition? 1088 $notedef = preg_replace_callback("/ 1089 ^note\# # start of note def marker 1090 ([^%<*!@#^([{ \s.]+) # !label 1091 ([*!^]?) # !link 1092 ({$this->c}) # !att 1093 \.? # optional period. 1094 [\s]+ # whitespace ends def marker 1095 (.*)$ # !content 1096 /x$mod", array(&$this, "fParseNoteDefs"), $content); 1097 1098 if( '' === $notedef ) # It will be empty if the regex matched and ate it. 1099 return array($o1, $o2, $notedef, $c2, $c1, true); 1100 } 1101 1102 if (preg_match("/fn(\d+)/", $tag, $fns)) { 1103 $tag = 'p'; 1104 $fnid = empty($this->fn[$fns[1]]) ? $fns[1] : $this->fn[$fns[1]]; 1105 1106 # If there is an author-specified ID goes on the wrapper & the auto-id gets pushed to the <sup> 1107 $supp_id = ''; 1108 if (strpos($atts, ' id=') === false) 1109 $atts .= ' id="fn' . $fnid . '"'; 1110 else 1111 $supp_id = ' id="fn' . $fnid . '"'; 1112 1113 if (strpos($atts, 'class=') === false) 1114 $atts .= ' class="footnote"'; 1115 1116 $sup = (strpos($att, '^') === false) ? $this->formatFootnote($fns[1], $supp_id) : $this->formatFootnote('<a href="#fnrev' . $fnid . '">'.$fns[1] .'</a>', $supp_id); 1117 1118 $content = $sup . ' ' . $content; 1119 } 1120 1121 if ($tag == "bq") { 1122 $cite = $this->shelveURL($cite); 1123 $cite = ($cite != '') ? ' cite="' . $cite . '"' : ''; 1124 $o1 = "\t<blockquote$cite$atts>\n"; 1125 $o2 = "\t\t<p".$this->pba($att, '', 0).">"; 1126 $c2 = "</p>"; 1127 $c1 = "\n\t</blockquote>"; 1128 } 1129 elseif ($tag == 'bc') { 1130 $o1 = "<pre$atts>"; 1131 $o2 = "<code>"; 1132 $c2 = "</code>"; 1133 $c1 = "</pre>"; 1134 $content = $this->shelve($this->r_encode_html(rtrim($content, "\n")."\n")); 1135 } 1136 elseif ($tag == 'notextile') { 1137 $content = $this->shelve($content); 1138 $o1 = $o2 = ''; 1139 $c1 = $c2 = ''; 1140 } 1141 elseif ($tag == 'pre') { 1142 $content = $this->shelve($this->r_encode_html(rtrim($content, "\n")."\n")); 1143 $o1 = "<pre$atts>"; 1144 $o2 = $c2 = ''; 1145 $c1 = "</pre>"; 1146 } 1147 elseif ($tag == '###') { 1148 $eat = true; 1149 } 1150 else { 1151 $o2 = "\t<$tag$atts>"; 1152 $c2 = "</$tag>"; 1153 } 1154 1155 $content = (!$eat) ? $this->graf($content) : ''; 1156 1157 return array($o1, $o2, $content, $c2, $c1, $eat); 1158 } 1159 1160 // ------------------------------------------------------------- 1161 function fParseHTMLComments($m) 1162 { 1163 list( , $content ) = $m; 1164 if( $this->restricted ) 1165 $content = $this->shelve($this->r_encode_html($content)); 1166 else 1167 $content = $this->shelve($content); 1168 return "<!--$content-->"; 1169 } 1170 1171 1172 function getHTMLComments($text) 1173 { 1174 $text = preg_replace_callback("/ 1175 \<!-- # start 1176 (.*?) # !content 1177 --> # end 1178 /sx", array(&$this, "fParseHTMLComments"), $text); 1179 return $text; 1180 } 1181 1182 // ------------------------------------------------------------- 1183 function graf($text) 1184 { 1185 // handle normal paragraph text 1186 if (!$this->lite) { 1187 $text = $this->noTextile($text); 1188 $text = $this->code($text); 1189 } 1190 1191 $text = $this->getHTMLComments($text); 1192 $text = $this->getRefs($text); 1193 $text = $this->links($text); 1194 if (!$this->noimage) 1195 $text = $this->image($text); 1196 1197 if (!$this->lite) { 1198 $text = $this->table($text); 1199 $text = $this->redcloth_lists($text); 1200 $text = $this->lists($text); 1201 } 1202 1203 $text = $this->span($text); 1204 $text = $this->footnoteRef($text); 1205 $text = $this->noteRef($text); 1206 $text = $this->glyphs($text); 1207 1208 return rtrim($text, "\n"); 1209 } 1210 1211 // ------------------------------------------------------------- 1212 function span($text) 1213 { 1214 $qtags = array('\*\*','\*','\?\?','-','__','_','%','\+','~','\^'); 1215 $pnct = ".,\"'?!;:‹›«»„“”‚‘’"; 1216 $this->span_depth++; 1217 1218 if( $this->span_depth <= $this->max_span_depth ) 1219 { 1220 foreach($qtags as $f) 1221 { 1222 $text = preg_replace_callback("/ 1223 (^|(?<=[\s>$pnct\(])|[{[]) # pre 1224 ($f)(?!$f) # tag 1225 ({$this->c}) # atts 1226 (?::(\S+))? # cite 1227 ([^\s$f]+|\S.*?[^\s$f\n]) # content 1228 ([$pnct]*) # end 1229 $f 1230 ($|[\[\]}<]|(?=[$pnct]{1,2}|\s|\))) # tail 1231 /x".$this->regex_snippets['mod'], array(&$this, "fSpan"), $text); 1232 } 1233 } 1234 $this->span_depth--; 1235 return $text; 1236 } 1237 1238 // ------------------------------------------------------------- 1239 function fSpan($m) 1240 { 1241 $qtags = array( 1242 '*' => 'strong', 1243 '**' => 'b', 1244 '??' => 'cite', 1245 '_' => 'em', 1246 '__' => 'i', 1247 '-' => 'del', 1248 '%' => 'span', 1249 '+' => 'ins', 1250 '~' => 'sub', 1251 '^' => 'sup', 1252 ); 1253 1254 list(, $pre, $tag, $atts, $cite, $content, $end, $tail) = $m; 1255 1256 $tag = $qtags[$tag]; 1257 $atts = $this->pba($atts); 1258 $atts .= ($cite != '') ? 'cite="' . $cite . '"' : ''; 1259 1260 $content = $this->span($content); 1261 1262 $opentag = '<'.$tag.$atts.'>'; 1263 $closetag = '</'.$tag.'>'; 1264 $tags = $this->storeTags($opentag, $closetag); 1265 $out = "{$tags['open']}{$content}{$end}{$tags['close']}"; 1266 1267 if (($pre and !$tail) or ($tail and !$pre)) 1268 $out = $pre.$out.$tail; 1269 1270 return $out; 1271 } 1272 1273 // ------------------------------------------------------------- 1274 function storeTags($opentag,$closetag='') 1275 { 1276 $key = ($this->tag_index++); 1277 1278 $key = str_pad( (string)$key, 10, '0', STR_PAD_LEFT ); # $key must be of fixed length to allow proper matching in retrieveTags 1279 $this->tagCache[$key] = array('open'=>$opentag, 'close'=>$closetag); 1280 $tags = array( 1281 'open' => "textileopentag{$key} ", 1282 'close' => " textileclosetag{$key}", 1283 ); 1284 return $tags; 1285 } 1286 1287 // ------------------------------------------------------------- 1288 function retrieveTags($text) 1289 { 1290 $text = preg_replace_callback('/textileopentag([\d]{10}) /' , array(&$this, 'fRetrieveOpenTags'), $text); 1291 $text = preg_replace_callback('/ textileclosetag([\d]{10})/', array(&$this, 'fRetrieveCloseTags'), $text); 1292 return $text; 1293 } 1294 1295 // ------------------------------------------------------------- 1296 function fRetrieveOpenTags($m) 1297 { 1298 list(, $key ) = $m; 1299 return $this->tagCache[$key]['open']; 1300 } 1301 1302 // ------------------------------------------------------------- 1303 function fRetrieveCloseTags($m) 1304 { 1305 list(, $key ) = $m; 1306 return $this->tagCache[$key]['close']; 1307 } 1308 1309 // ------------------------------------------------------------- 1310 function placeNoteLists($text) 1311 { 1312 extract($this->regex_snippets); 1313 1314 # Sequence all referenced definitions... 1315 if( !empty($this->notes) ) { 1316 $o = array(); 1317 foreach( $this->notes as $label=>$info ) { 1318 $i = @$info['seq']; 1319 if( !empty($i) ) { 1320 $info['seq'] = $label; 1321 $o[$i] = $info; 1322 } else { 1323 $this->unreferencedNotes[] = $info; # unreferenced definitions go here for possible future use. 1324 } 1325 } 1326 if( !empty($o) ) ksort($o); 1327 $this->notes = $o; 1328 } 1329 1330 # Replace list markers... 1331 $text = preg_replace_callback("@<p>notelist({$this->c})(?:\:([$wrd|{$this->syms}]))?([\^!]?)(\+?)\.?[\s]*</p>@U$mod", array(&$this, "fNoteLists"), $text ); 1332 1333 return $text; 1334 } 1335 1336 // ------------------------------------------------------------- 1337 function fNoteLists($m) 1338 { 1339 list(, $att, $start_char, $g_links, $extras) = $m; 1340 if( !$start_char ) $start_char = 'a'; 1341 $index = $g_links.$extras.$start_char; 1342 1343 if( empty($this->notelist_cache[$index]) ) { # If not in cache, build the entry... 1344 $o = array(); 1345 1346 if( !empty($this->notes)) { 1347 foreach($this->notes as $seq=>$info) { 1348 $links = $this->makeBackrefLink($info, $g_links, $start_char ); 1349 $atts = ''; 1350 if( !empty($info['def'])) { 1351 $id = $info['id']; 1352 extract($info['def']); 1353 $o[] = "\t".'<li'.$atts.'>'.$links.'<span id="note'.$id.'"> </span>'.$content.'</li>'; 1354 } else { 1355 $o[] = "\t".'<li'.$atts.'>'.$links.' Undefined Note [#'.$info['seq'].'].</li>'; 1356 } 1357 } 1358 } 1359 if( '+' == $extras && !empty($this->unreferencedNotes) ) { 1360 foreach($this->unreferencedNotes as $seq=>$info) { 1361 if( !empty($info['def'])) { 1362 extract($info['def']); 1363 $o[] = "\t".'<li'.$atts.'>'.$content.'</li>'; 1364 } 1365 } 1366 } 1367 1368 $this->notelist_cache[$index] = join("\n",$o); 1369 } 1370 1371 $_ = ($this->notelist_cache[$index]) ? $this->notelist_cache[$index] : ''; 1372 1373 if( !empty($_) ) { 1374 $list_atts = $this->pba($att); 1375 $_ = "<ol$list_atts>\n$_\n</ol>"; 1376 } 1377 1378 return $_; 1379 } 1380 1381 // ------------------------------------------------------------- 1382 function makeBackrefLink( &$info, $g_links, $i ) 1383 { 1384 $atts = $content = $id = $link = ''; 1385 @extract( $info['def'] ); 1386 $backlink_type = ($link) ? $link : $g_links; 1387 $allow_inc = (false === strpos( $this->syms, $i ) ); 1388 1389 $i_ = strtr( $this->encode_high($i) , array('&'=>'', ';'=>'', '#'=>'')); 1390 $decode = (strlen($i) !== strlen($i_)); 1391 1392 if( $backlink_type === '!' ) 1393 return ''; 1394 elseif( $backlink_type === '^' ) 1395 return '<sup><a href="#noteref'.$info['refids'][0].'">'.$i.'</a></sup>'; 1396 else { 1397 $_ = array(); 1398 foreach( $info['refids'] as $id ) { 1399 $_[] = '<sup><a href="#noteref'.$id.'">'. ( ($decode) ? $this->decode_high('&#'.$i_.';') : $i_ ) .'</a></sup>'; 1400 if( $allow_inc ) 1401 $i_++; 1402 } 1403 $_ = join( ' ', $_ ); 1404 return $_; 1405 } 1406 } 1407 1408 1409 // ------------------------------------------------------------- 1410 function fParseNoteDefs($m) 1411 { 1412 list(, $label, $link, $att, $content) = $m; 1413 # Assign an id if the note reference parse hasn't found the label yet. 1414 $id = @$this->notes[$label]['id']; 1415 if( !$id ) 1416 $this->notes[$label]['id'] = uniqid(rand()); 1417 1418 if( empty($this->notes[$label]['def']) ) # Ignores subsequent defs using the same label 1419 { 1420 $this->notes[$label]['def'] = array( 1421 'atts' => $this->pba($att), 1422 'content' => $this->graf($content), 1423 'link' => $link, 1424 ); 1425 } 1426 return ''; 1427 } 1428 1429 // ------------------------------------------------------------- 1430 function noteRef($text) 1431 { 1432 $text = preg_replace_callback("/ 1433 \[ # start 1434 ({$this->c}) # !atts 1435 \# 1436 ([^\]!]+?) # !label 1437 ([!]?) # !nolink 1438 \] 1439 /Ux", array(&$this, "fParseNoteRefs"), $text); 1440 return $text; 1441 } 1442 1443 // ------------------------------------------------------------- 1444 function fParseNoteRefs($m) 1445 { 1446 # By the time this function is called, all the defs will have been processed 1447 # into the notes array. So now we can resolve the link numbers in the order 1448 # we process the refs... 1449 1450 list(, $atts, $label, $nolink) = $m; 1451 $atts = $this->pba($atts); 1452 $nolink = ($nolink === '!'); 1453 1454 # Assign a sequence number to this reference if there isn't one already... 1455 $num = @$this->notes[$label]['seq']; 1456 if( !$num ) 1457 $num = $this->notes[$label]['seq'] = ($this->note_index++); 1458 1459 # Make our anchor point & stash it for possible use in backlinks when the 1460 # note list is generated later... 1461 $this->notes[$label]['refids'][] = $refid = uniqid(rand()); 1462 1463 # If we are referencing a note that hasn't had the definition parsed yet, then assign it an ID... 1464 $id = @$this->notes[$label]['id']; 1465 if( !$id ) 1466 $id = $this->notes[$label]['id'] = uniqid(rand()); 1467 1468 # Build the link (if any)... 1469 $_ = '<span id="noteref'.$refid.'">'.$num.'</span>'; 1470 if( !$nolink ) 1471 $_ = '<a href="#note'.$id.'">'.$_.'</a>'; 1472 1473 # Build the reference... 1474 $_ = $this->replaceMarkers( txt_nl_ref_pattern, array( 'atts' => $atts, 'marker' => $_ ) ); 1475 1476 return $_; 1477 } 1478 1479 // ------------------------------------------------------------- 1480 /** 1481 * Parse URI 1482 * 1483 * Regex taken from the RFC at http://tools.ietf.org/html/rfc3986#appendix-B 1484 **/ 1485 function parseURI( $uri, &$m ) 1486 { 1487 $r = "@^((?P<scheme>[^:/?#]+):)?(//(?P<authority>[^/?#]*))?(?P<path>[^?#]*)(\?(?P<query>[^#]*))?(#(?P<fragment>.*))?@"; 1488 # 12 3 4 5 6 7 8 9 1489 # 1490 # scheme = $2 1491 # authority = $4 1492 # path = $5 1493 # query = $7 1494 # fragment = $9 1495 1496 $ok = preg_match( $r, $uri, $m ); 1497 return $ok; 1498 } 1499 1500 function addPart( &$mask, $name, &$parts ) { 1501 return (in_array($name, $mask) && isset( $parts[$name]) && '' !== $parts[$name]); 1502 } 1503 1504 1505 // ------------------------------------------------------------- 1506 /** 1507 * Rebuild a URI from parsed parts and a mask. 1508 * 1509 * Algorithm based on example from http://tools.ietf.org/html/rfc3986#section-5.3 1510 **/ 1511 function rebuildURI( $parts, $mask='scheme,authority,path,query,fragment', $encode=true ) 1512 { 1513 $mask = explode( ',', $mask ); 1514 $out = ''; 1515 1516 if( $this->addPart( $mask, 'scheme', $parts ) ) { 1517 $out .= $parts['scheme'] . ':'; 1518 } 1519 1520 if( $this->addPart( $mask, 'authority', $parts) ) { 1521 $out .= '//' . $parts['authority']; 1522 } 1523 1524 if( $this->addPart( $mask, 'path', $parts ) ) { 1525 if( !$encode ) 1526 $out .= $parts['path']; 1527 else { 1528 $pp = explode( '/', $parts['path'] ); 1529 foreach( $pp as &$p ) { 1530 $p = strtr( rawurlencode( $p ), array( '%40' => '@' ) ); 1531 } 1532 1533 $pp = implode( '/', $pp ); 1534 $out .= $pp; 1535 } 1536 } 1537 1538 if( $this->addPart( $mask, 'query', $parts ) ) { 1539 $out .= '?' . $parts['query']; 1540 } 1541 1542 if( $this->addPart( $mask, 'fragment', $parts ) ) { 1543 $out .= '#' . $parts['fragment']; 1544 } 1545 1546 return $out; 1547 } 1548 1549 // ------------------------------------------------------------- 1550 function links($text) 1551 { 1552 return preg_replace_callback('/ 1553 (^|(?<=[\s>.\(])|[{[]) # $pre 1554 " # start 1555 (' . $this->c . ') # $atts 1556 ([^"]+?) # $text 1557 (?:\(([^)]+?)\)(?="))? # $title 1558 ": 1559 ('.$this->urlch.'+?) # $url 1560 (\/)? # $slash 1561 ([^'.$this->regex_snippets['wrd'].'\/;]*?) # $post 1562 ([\]}]|(?=\s|$|\))) # $tail 1563 /x'.$this->regex_snippets['mod'], array(&$this, "fLink"), $text); 1564 } 1565 1566 // ------------------------------------------------------------- 1567 function fLink($m) 1568 { 1569 list(, $pre, $atts, $text, $title, $url, $slash, $post, $tail) = $m; 1570 1571 $uri_parts = array(); 1572 $this->parseURI( $url, $uri_parts ); 1573 1574 $scheme = $uri_parts['scheme']; 1575 $scheme_in_list = in_array( $scheme, $this->url_schemes ); 1576 $scheme_ok = '' === $scheme || $scheme_in_list; 1577 1578 if( !$scheme_ok ) 1579 return $m[0]; 1580 1581 if( '$' === $text ) { 1582 if( $scheme_in_list ) 1583 $text = ltrim( $this->rebuildURI( $uri_parts, 'authority,path,query,fragment', false ), '/' ); 1584 else 1585 $text = $url; 1586 } 1587 1588 $atts = $this->pba($atts); 1589 $atts .= ($title != '') ? ' title="' . $this->encode_html($title) . '"' : ''; 1590 1591 if (!$this->noimage) 1592 $text = $this->image($text); 1593 1594 $text = $this->span($text); 1595 $text = $this->glyphs($text); 1596 $url = $this->shelveURL( $this->rebuildURI( $uri_parts ) . $slash ); 1597 1598 $opentag = '<a href="' . $url . '"' . $atts . $this->rel . '>'; 1599 $closetag = '</a>'; 1600 $tags = $this->storeTags($opentag, $closetag); 1601 $out = $tags['open'].trim($text).$tags['close']; 1602 1603 if (($pre and !$tail) or ($tail and !$pre)) 1604 { 1605 $out = $pre.$out.$post.$tail; 1606 $post = ''; 1607 } 1608 1609 return $this->shelve($out).$post; 1610 } 1611 1612 // ------------------------------------------------------------- 1613 function getRefs($text) 1614 { 1615 if( $this->restricted ) 1616 $pattern = "/^\[(.+)\]((?:http:\/\/|https:\/\/|\/)\S+)(?=\s|$)/Um"; 1617 else 1618 $pattern = "/^\[(.+)\]((?:http:\/\/|https:\/\/|tel:|file:|ftp:\/\/|sftp:\/\/|mailto:|callto:|\/)\S+)(?=\s|$)/Um"; 1619 return preg_replace_callback( $pattern, array(&$this, "refs"), $text); 1620 } 1621 1622 // ------------------------------------------------------------- 1623 function refs($m) 1624 { 1625 list(, $flag, $url) = $m; 1626 $uri_parts = array(); 1627 $this->parseURI( $url, $uri_parts ); 1628 $url = ltrim( $this->rebuildURI( $uri_parts ) ); // encodes URL if needed. 1629 $this->urlrefs[$flag] = $url; 1630 return ''; 1631 } 1632 1633 // ------------------------------------------------------------- 1634 function shelveURL($text) 1635 { 1636 if ('' === $text) return ''; 1637 $ref = md5($text); 1638 $this->urlshelf[$ref] = $text; 1639 return 'urlref:'.$ref; 1640 } 1641 1642 // ------------------------------------------------------------- 1643 function retrieveURLs($text) 1644 { 1645 return preg_replace_callback('/urlref:(\w{32})/', 1646 array(&$this, "retrieveURL"), $text); 1647 } 1648 1649 // ------------------------------------------------------------- 1650 function retrieveURL($m) 1651 { 1652 $ref = $m[1]; 1653 if (!isset($this->urlshelf[$ref])) 1654 return $ref; 1655 $url = $this->urlshelf[$ref]; 1656 if (isset($this->urlrefs[$url])) 1657 $url = $this->urlrefs[$url]; 1658 return $this->r_encode_html($this->relURL($url)); 1659 } 1660 1661 // ------------------------------------------------------------- 1662 function relURL($url) 1663 { 1664 $parts = @parse_url(urldecode($url)); 1665 if ((empty($parts['scheme']) or @$parts['scheme'] == 'http') and 1666 empty($parts['host']) and 1667 preg_match('/^\w/', @$parts['path'])) 1668 $url = $this->hu.$url; 1669 if ($this->restricted and !empty($parts['scheme']) and 1670 !in_array($parts['scheme'], $this->url_schemes)) 1671 return '#'; 1672 return $url; 1673 } 1674 1675 // ------------------------------------------------------------- 1676 function isRelURL($url) 1677 { 1678 $parts = @parse_url($url); 1679 return (empty($parts['scheme']) and empty($parts['host'])); 1680 } 1681 1682 // ------------------------------------------------------------- 1683 function image($text) 1684 { 1685 return preg_replace_callback("/ 1686 (?:[[{])? # pre 1687 \! # opening ! 1688 (\<|\=|\>)? # optional alignment atts 1689 ($this->c) # optional style,class atts 1690 (?:\. )? # optional dot-space 1691 ([^\s(!]+) # presume this is the src 1692 \s? # optional space 1693 (?:\(([^\)]+)\))? # optional title 1694 \! # closing 1695 (?::(\S+))? # optional href 1696 (?:[\]}]|(?=\s|$|\))) # lookahead: space or end of string 1697 /x", array(&$this, "fImage"), $text); 1698 } 1699 1700 // ------------------------------------------------------------- 1701 function fImage($m) 1702 { 1703 list(, $algn, $atts, $url) = $m; 1704 $url = htmlspecialchars($url); 1705 1706 $extras = $align = ''; 1707 if( '' !== $algn ) { 1708 $vals = array( 1709 '<' => 'left', 1710 '=' => 'center', 1711 '>' => 'right'); 1712 if ( isset($vals[$algn]) ) { 1713 if( 'html5' === $this->doctype ) 1714 $extras = "align-{$vals[$algn]}"; 1715 else 1716 $align = " align=\"{$vals[$algn]}\""; 1717 } 1718 } 1719 $atts = $this->pba($atts , '' , 1 , $extras) . $align; 1720 1721 if(isset($m[4])) 1722 { 1723 $m[4] = htmlspecialchars($m[4]); 1724 $atts .= ' title="' . $m[4] . '" alt="' . $m[4] . '"'; 1725 } 1726 else 1727 $atts .= ' alt=""'; 1728 1729 $size = false; 1730 if ($this->isRelUrl($url)) 1731 $size = @getimagesize(realpath($this->doc_root.ltrim($url, $this->ds))); 1732 if ($size) $atts .= " $size[3]"; 1733 1734 $href = (isset($m[5])) ? $this->shelveURL($m[5]) : ''; 1735 $url = $this->shelveURL($url); 1736 1737 $out = array( 1738 ($href) ? '<a href="' . $href . '"' . $this->rel .'>' : '', 1739 '<img src="' . $url . '"' . $atts . ' />', 1740 ($href) ? '</a>' : '' 1741 ); 1742 1743 return $this->shelve(join('',$out)); 1744 } 1745 1746 // ------------------------------------------------------------- 1747 function code($text) 1748 { 1749 $text = $this->doSpecial($text, '<code>', '</code>', 'fCode'); 1750 $text = $this->doSpecial($text, '@', '@', 'fCode'); 1751 $text = $this->doSpecial($text, '<pre>', '</pre>', 'fPre'); 1752 return $text; 1753 } 1754 1755 // ------------------------------------------------------------- 1756 function fCode($m) 1757 { 1758 @list(, $before, $text, $after) = $m; 1759 return $before.$this->shelve('<code>'.$this->r_encode_html($text).'</code>').$after; 1760 } 1761 1762 // ------------------------------------------------------------- 1763 function fPre($m) 1764 { 1765 @list(, $before, $text, $after) = $m; 1766 return $before.'<pre>'.$this->shelve($this->r_encode_html($text)).'</pre>'.$after; 1767 } 1768 1769 // ------------------------------------------------------------- 1770 function shelve($val) 1771 { 1772 $i = uniqid(rand()); 1773 $this->shelf[$i] = $val; 1774 return $i; 1775 } 1776 1777 // ------------------------------------------------------------- 1778 function retrieve($text) 1779 { 1780 if (is_array($this->shelf)) 1781 do { 1782 $old = $text; 1783 $text = strtr($text, $this->shelf); 1784 } while ($text != $old); 1785 1786 return $text; 1787 } 1788 1789 // ------------------------------------------------------------- 1790 // NOTE: deprecated 1791 function incomingEntities($text) 1792 { 1793 return preg_replace("/&(?![#a-z0-9]+;)/i", "x%x%", $text); 1794 } 1795 1796 // ------------------------------------------------------------- 1797 // NOTE: deprecated 1798 function encodeEntities($text) 1799 { 1800 return (function_exists('mb_encode_numericentity')) 1801 ? $this->encode_high($text) 1802 : htmlentities($text, ENT_NOQUOTES, "utf-8"); 1803 } 1804 1805 // ------------------------------------------------------------- 1806 // NOTE: deprecated 1807 function fixEntities($text) 1808 { 1809 /* de-entify any remaining angle brackets or ampersands */ 1810 return str_replace(array(">", "<", "&"), 1811 array(">", "<", "&"), $text); 1812 } 1813 1814 // ------------------------------------------------------------- 1815 function cleanWhiteSpace($text) 1816 { 1817 $out = preg_replace("/^\xEF\xBB\xBF|\x1A/", '', $text); # Byte order mark (if present) 1818 $out = preg_replace("/\r\n?/", "\n", $out); # DOS and MAC line endings to *NIX style endings 1819 $out = preg_replace("/^[ \t]*\n/m", "\n", $out); # lines containing only whitespace 1820 $out = preg_replace("/\n{3,}/", "\n\n", $out); # 3 or more line ends 1821 $out = preg_replace("/^\n*/", "", $out); # leading blank lines 1822 return $out; 1823 } 1824 1825 // ------------------------------------------------------------- 1826 function doSpecial($text, $start, $end, $method='fSpecial') 1827 { 1828 return preg_replace_callback('/(^|\s|[[({>])'.preg_quote($start, '/').'(.*?)'.preg_quote($end, '/').'(\s|$|[\])}])?/ms', 1829 array(&$this, $method), $text); 1830 } 1831 1832 // ------------------------------------------------------------- 1833 function fSpecial($m) 1834 { 1835 // A special block like notextile or code 1836 @list(, $before, $text, $after) = $m; 1837 return $before.$this->shelve($this->encode_html($text)).$after; 1838 } 1839 1840 // ------------------------------------------------------------- 1841 function noTextile($text) 1842 { 1843 $text = $this->doSpecial($text, '<notextile>', '</notextile>', 'fTextile'); 1844 return $this->doSpecial($text, '==', '==', 'fTextile'); 1845 1846 } 1847 1848 // ------------------------------------------------------------- 1849 function fTextile($m) 1850 { 1851 @list(, $before, $notextile, $after) = $m; 1852 #$notextile = str_replace(array_keys($modifiers), array_values($modifiers), $notextile); 1853 return $before.$this->shelve($notextile).$after; 1854 } 1855 1856 // ------------------------------------------------------------- 1857 function footnoteRef($text) 1858 { 1859 return preg_replace('/(?<=\S)\[([0-9]+)([\!]?)\](\s)?/Ue', 1860 '$this->footnoteID(\'\1\',\'\2\',\'\3\')', $text); 1861 } 1862 1863 // ------------------------------------------------------------- 1864 function footnoteID($id, $nolink, $t) 1865 { 1866 $backref = ' '; 1867 if (empty($this->fn[$id])) { 1868 $this->fn[$id] = $a = uniqid(rand()); 1869 $backref = ' id="fnrev'.$a.'" '; 1870 } 1871 1872 $fnid = $this->fn[$id]; 1873 1874 $footref = ( '!' == $nolink ) ? $id : '<a href="#fn'.$fnid.'">'.$id.'</a>'; 1875 $backref .= 'class="footnote"'; 1876 1877 $footref = $this->formatFootnote( $footref, $backref, false ); 1878 1879 return $footref; 1880 } 1881 1882 // ------------------------------------------------------------- 1883 function glyphs($text) 1884 { 1885 // fix: hackish -- adds a space if final char of text is a double quote. 1886 $text = preg_replace('/"\z/', "\" ", $text); 1887 1888 $text = preg_split("@(<[\w/!?].*>)@Us", $text, -1, PREG_SPLIT_DELIM_CAPTURE); 1889 $i = 0; 1890 foreach($text as $line) { 1891 // text tag text tag text ... 1892 if (++$i % 2) { 1893 // raw < > & chars are already entity encoded in restricted mode 1894 if (!$this->restricted) { 1895 $line = $this->encode_raw_amp($line); 1896 $line = $this->encode_lt_gt($line); 1897 } 1898 $line = preg_replace($this->glyph_search, $this->glyph_replace, $line); 1899 } 1900 $glyph_out[] = $line; 1901 } 1902 return join('', $glyph_out); 1903 } 1904 1905 // ------------------------------------------------------------- 1906 function replaceGlyphs($text) 1907 { 1908 return preg_replace('/glyph:([^<]+)/','$1',$text); 1909 } 1910 1911 // ------------------------------------------------------------- 1912 function hAlign($in) 1913 { 1914 $vals = array( 1915 '<' => 'left', 1916 '=' => 'center', 1917 '>' => 'right', 1918 '<>' => 'justify'); 1919 return (isset($vals[$in])) ? $vals[$in] : ''; 1920 } 1921 1922 // ------------------------------------------------------------- 1923 function vAlign($in) 1924 { 1925 $vals = array( 1926 '^' => 'top', 1927 '-' => 'middle', 1928 '~' => 'bottom'); 1929 return (isset($vals[$in])) ? $vals[$in] : ''; 1930 } 1931 1932 // ------------------------------------------------------------- 1933 // NOTE: used in notelists 1934 function encode_high($text, $charset = "UTF-8") 1935 { 1936 return mb_encode_numericentity($text, $this->cmap(), $charset); 1937 } 1938 1939 // ------------------------------------------------------------- 1940 // NOTE: used in notelists 1941 function decode_high($text, $charset = "UTF-8") 1942 { 1943 return mb_decode_numericentity($text, $this->cmap(), $charset); 1944 } 1945 1946 // ------------------------------------------------------------- 1947 function cmap() 1948 { 1949 $f = 0xffff; 1950 $cmap = array( 1951 0x0080, 0xffff, 0, $f); 1952 return $cmap; 1953 } 1954 1955 // ------------------------------------------------------------- 1956 function encode_raw_amp($text) 1957 { 1958 return preg_replace('/&(?!#?[a-z0-9]+;)/i', '&', $text); 1959 } 1960 1961 // ------------------------------------------------------------- 1962 function encode_lt_gt($text) 1963 { 1964 return strtr($text, array('<' => '<', '>' => '>')); 1965 } 1966 1967 // ------------------------------------------------------------- 1968 function encode_quot($text) 1969 { 1970 return str_replace('"', '"', $text); 1971 } 1972 1973 // ------------------------------------------------------------- 1974 function encode_html($str, $quotes=1) 1975 { 1976 $a = array( 1977 '&' => '&', 1978 '<' => '<', 1979 '>' => '>', 1980 ); 1981 if ($quotes) $a = $a + array( 1982 "'" => ''', // numeric, as in htmlspecialchars 1983 '"' => '"', 1984 ); 1985 1986 return strtr($str, $a); 1987 } 1988 1989 // ------------------------------------------------------------- 1990 function r_encode_html($str, $quotes=1) 1991 { 1992 // in restricted mode, all input but quotes has already been escaped 1993 if ($this->restricted) 1994 return $this->encode_quot($str); 1995 return $this->encode_html($str, $quotes); 1996 } 1997 1998 // ------------------------------------------------------------- 1999 function textile_popup_help($name, $helpvar, $windowW, $windowH) 2000 { 2001 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 />'; 2002 } 2003 2004 // ------------------------------------------------------------- 2005 // NOTE: deprecated 2006 function txtgps($thing) 2007 { 2008 if (isset($_POST[$thing])) { 2009 if (get_magic_quotes_gpc()) { 2010 return stripslashes($_POST[$thing]); 2011 } 2012 else { 2013 return $_POST[$thing]; 2014 } 2015 } 2016 else { 2017 return ''; 2018 } 2019 } 2020 2021 // ------------------------------------------------------------- 2022 // NOTE: deprecated 2023 function dump() 2024 { 2025 static $bool = array( 0=>'false', 1=>'true' ); 2026 foreach (func_get_args() as $a) 2027 echo "\n<pre>",(is_array($a)) ? print_r($a) : ((is_bool($a)) ? $bool[(int)$a] : $a), "</pre>\n"; 2028 return $this; 2029 } 2030 2031 // ------------------------------------------------------------- 2032 2033 function blockLite($text) 2034 { 2035 $this->btag = array('bq', 'p'); 2036 return $this->block($text."\n\n"); 2037 } 2038 2039 2040 } // end class 2041
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title