1<?php 2 3/* 4 5This is Textile 6A Humane Web Text Generator 7 8Version 2.0 beta 98 July, 2003 10 11Copyright (c) 2003, Dean Allen, www.textism.com 12All rights reserved. 13 14_______ 15LICENSE 16 17Redistribution and use in source and binary forms, with or without 18modification, are permitted provided that the following conditions are met: 19 20* Redistributions of source code must retain the above copyright notice, 21 this list of conditions and the following disclaimer. 22 23* Redistributions in binary form must reproduce the above copyright notice, 24 this list of conditions and the following disclaimer in the documentation 25 and/or other materials provided with the distribution. 26 27* Neither the name Textile nor the names of its contributors may be used to 28 endorse or promote products derived from this software without specific 29 prior written permission. 30 31THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 32AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 33IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 34ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 35LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 36CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 37SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 38INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 39CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 40ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 41POSSIBILITY OF SUCH DAMAGE. 42 43_____________ 44USING TEXTILE 45 46Block modifier syntax: 47 48 Header: h(1-6). 49 Paragraphs beginning with 'hn. ' (where n is 1-6) are wrapped in header tags. 50 Example: h1. Header... -> <h1>Header...</h1> 51 52 Paragraph: p. (also applied by default) 53 Example: p. Text -> <p>Text</p> 54 55 Blockquote: bq. 56 Example: bq. Block quotation... -> <blockquote>Block quotation...</blockquote> 57 58 Blockquote with citation: bq.:http://citation.url 59 Example: bq.:http://textism.com/ Text... 60 -> <blockquote cite="http://textism.com">Text...</blockquote> 61 62 Footnote: fn(1-100). 63 Example: fn1. Footnote... -> <p id="fn1">Footnote...</p> 64 65 Numeric list: #, ## 66 Consecutive paragraphs beginning with # are wrapped in ordered list tags. 67 Example: <ol><li>ordered list</li></ol> 68 69 Bulleted list: *, ** 70 Consecutive paragraphs beginning with * are wrapped in unordered list tags. 71 Example: <ul><li>unordered list</li></ul> 72 73Phrase modifier syntax: 74 75 _emphasis_ -> <em>emphasis</em> 76 __italic__ -> <i>italic</i> 77 *strong* -> <strong>strong</strong> 78 **bold** -> <b>bold</b> 79 ??citation?? -> <cite>citation</cite> 80 -deleted text- -> <del>deleted</del> 81 +inserted text+ -> <ins>inserted</ins> 82 ^superscript^ -> <sup>superscript</sup> 83 ~subscript~ -> <sub>subscript</sub> 84 @code@ -> <code>computer code</code> 85 %(bob)span% -> <span class="bob">span</span> 86 87 ==notextile== -> leave text alone (do not format) 88 89 "linktext":url -> <a href="url">linktext</a> 90 "linktext(title)":url -> <a href="url" title="title">linktext</a> 91 92 !imageurl! -> <img src="imageurl" /> 93 !imageurl(alt text)! -> <img src="imageurl" alt="alt text" /> 94 !imageurl!:linkurl -> <a href="linkurl"><img src="imageurl" /></a> 95 96ABC(Always Be Closing) -> <acronym title="Always Be Closing">ABC</acronym> 97 98 99Table syntax: 100 101 Simple tables: 102 103 |a|simple|table|row| 104 |And|Another|table|row| 105 106 |_. A|_. table|_. header|_.row| 107 |A|simple|table|row| 108 109 Tables with attributes: 110 111 table{border:1px solid black}. 112 {background:#ddd;color:red}. |{}| | | | 113 114 115Applying Attributes: 116 117 Most anywhere Textile code is used, attributes such as arbitrary css style, 118 css classes, and ids can be applied. The syntax is fairly consistent. 119 120 The following characters quickly alter the alignment of block elements: 121 122 < -> left align ex. p<. left-aligned para 123 > -> right align h3>. right-aligned header 3 124 = -> centred h4=. centred header 4 125 <> -> justified p<>. justified paragraph 126 127 These will change vertical alignment in table cells: 128 129 ^ -> top ex. |^. top-aligned table cell| 130 - -> middle |-. middle aligned| 131 ~ -> bottom |~. bottom aligned cell| 132 133 Plain (parentheses) inserted between block syntax and the closing dot-space 134 indicate classes and ids: 135 136 p(hector). paragraph -> <p class="hector">paragraph</p> 137 138 p(#fluid). paragraph -> <p id="fluid">paragraph</p> 139 140 (classes and ids can be combined) 141 p(hector#fluid). paragraph -> <p class="hector" id="fluid">paragraph</p> 142 143 Curly {brackets} insert arbitrary css style 144 145 p{line-height:18px}. paragraph -> <p style="line-height:18px">paragraph</p> 146 147 h3{color:red}. header 3 -> <h3 style="color:red">header 3</h3> 148 149 Square [brackets] insert language attributes 150 151 p[no]. paragraph -> <p lang="no">paragraph</p> 152 153 %[fr]phrase% -> <span lang="fr">phrase</span> 154 155 Usually Textile block element syntax requires a dot and space before the block 156 begins, but since lists don't, they can be styled just using braces 157 158 #{color:blue} one -> <ol style="color:blue"> 159 # big <li>one</li> 160 # list <li>big</li> 161 <li>list</li> 162 </ol> 163 164 Using the span tag to style a phrase 165 166 It goes like this, %{color:red}the fourth the fifth% 167 -> It goes like this, <span style="color:red">the fourth the fifth</span> 168 169*/ 170 171 function myglobals() { 172 global $textile_hlgn, $textile_vlgn, $textile_clas, $textile_lnge, $textile_styl, 173 $textile_cspn, $textile_rspn, $textile_a, $textile_s, $textile_c, $textile_pnct; 174 $textile_hlgn = "(?:\<(?!>)|(?<!<)\>|\<\>|\=|[()]+)"; 175 $textile_vlgn = "[\-^~]"; 176 $textile_clas = "(?:\([^)]+\))"; 177 $textile_lnge = "(?:\[[^]]+\])"; 178 $textile_styl = "(?:\{[^}]+\})"; 179 $textile_cspn = "(?:\\\\\d+)"; 180 $textile_rspn = "(?:\/\d+)"; 181 $textile_a = "(?:$textile_hlgn?$textile_vlgn?|$textile_vlgn?$textile_hlgn?)"; 182 $textile_s = "(?:$textile_cspn?$textile_rspn?|$textile_rspn?$textile_cspn?)"; 183 $textile_c = "(?:$textile_clas?$textile_styl?$textile_lnge?|$textile_styl?$textile_lnge?$textile_clas?|$textile_lnge?$textile_styl?$textile_clas?)"; 184 $textile_pnct = '[\!"#\$%&\'()\*\+,\-\./:;<=>\?@\[\\\]\^_`{\|}\~]'; 185 } 186 187 myglobals(); 188 189 function textile($text,$lite='') { 190 191 if (get_magic_quotes_gpc()==1) 192 $text = stripslashes($text); 193 194 $text = textile_incomingEntities($text); 195 $text = textile_encodeEntities($text); 196 $text = textile_fixEntities($text); 197 $text = textile_cleanWhiteSpace($text); 198 199 $text = textile_getRefs($text); 200 201 $text = textile_noTextile($text); 202 $text = textile_image($text); 203 $text = textile_links($text); 204 $text = textile_code($text); 205 $text = textile_span($text); 206 $text = textile_superscript($text); 207 $text = textile_footnoteRef($text); 208 $text = textile_glyphs($text); 209 $text = textile_retrieve($text); 210 211 if ($lite=='') { 212 $text = textile_lists($text); 213 $text = textile_table($text); 214 $text = textile_block($text); 215 } 216 217 /* clean up <notextile> */ 218 $text = preg_replace('/<\/?notextile>/', "",$text); 219 220 /* turn the temp char back to an ampersand entity */ 221 $text = str_replace("x%x%","&",$text); 222 223 $text = str_replace("<br />","<br />\n",$text); 224 225 return trim($text); 226 } 227 228// ------------------------------------------------------------- 229 function textile_pba($in,$element="") // "parse block attributes" 230 { 231 global $textile_hlgn,$textile_vlgn,$textile_clas,$textile_styl,$textile_cspn,$textile_rspn,$textile_a,$textile_s,$textile_c; 232 233 $textile_style=''; $textile_class=''; $language=''; $textile_colspan=''; $rowspan=''; $id=''; $textile_atts=''; 234 235 if (!empty($in)) { 236 $matched = $in; 237 if($element=='td'){ 238 if(preg_match("/\\\\(\d+)/",$matched,$textile_csp)) $textile_colspan=$textile_csp[1]; 239 if(preg_match("/\/(\d+)/",$matched,$rsp)) $rowspan=$rsp[1]; 240 241 if (preg_match("/($textile_vlgn)/",$matched,$vert)) 242 $textile_style[] = "vertical-align:".textile_vAlign($vert[1]).";"; 243 } 244 245 if(preg_match("/\{([^}]*)\}/",$matched,$textile_sty)) { 246 $textile_style[]=$textile_sty[1].';'; 247 $matched = str_replace($textile_sty[0],'',$matched); 248 } 249 250 if(preg_match("/\[([^)]+)\]/U",$matched,$lng)) { 251 $language=$lng[1]; 252 $matched = str_replace($lng[0],'',$matched); 253 } 254 255 if(preg_match("/\(([^()]+)\)/U",$matched,$textile_cls)) { 256 $textile_class=$textile_cls[1]; 257 $matched = str_replace($textile_cls[0],'',$matched); 258 } 259 260 if(preg_match("/([(]+)/",$matched,$pl)) { 261 $textile_style[] = "padding-left:".strlen($pl[1])."em;"; 262 $matched = str_replace($pl[0],'',$matched); 263 } 264 if(preg_match("/([)]+)/",$matched,$pr)) { 265# dump($pr); 266 $textile_style[] = "padding-right:".strlen($pr[1])."em;"; 267 $matched = str_replace($pr[0],'',$matched); 268 } 269 270 if (preg_match("/($textile_hlgn)/",$matched,$horiz)) 271 $textile_style[] = "text-align:".textile_hAlign($horiz[1]).";"; 272 273 if (preg_match("/^(.*)#(.*)$/",$textile_class,$ids)) { 274 $id = $ids[2]; 275 $textile_class = $ids[1]; 276 } 277 278 if($textile_style) $textile_atts.=' style="'.join("",$textile_style).'"'; 279 if($textile_class) $textile_atts.=' class="'.$textile_class.'"'; 280 if($language) $textile_atts.=' lang="'.$language.'"'; 281 if($id) $textile_atts.=' id="'.$id.'"'; 282 if($textile_colspan) $textile_atts.=' colspan="'.$textile_colspan.'"'; 283 if($rowspan) $textile_atts.=' rowspan="'.$rowspan.'"'; 284 285 return $textile_atts; 286 } else { 287 return ''; 288 } 289 } 290 291// ------------------------------------------------------------- 292 function textile_table($text) 293 { 294 global $textile_a,$textile_c,$textile_s; 295 $text = $text."\n\n"; 296 return preg_replace_callback("/^(?:table(_?$textile_s$textile_a$textile_c)\. ?\n)?^($textile_a$textile_c\.? ?\|.*\|)\n\n/smU", 297 "textile_fTable",$text); 298 } 299 300// ------------------------------------------------------------- 301 function textile_fTable($matches) 302 { 303 global $textile_s,$textile_a,$textile_c; 304 $tatts = textile_pba($matches[1],'table'); 305 306 foreach(preg_split("/\|$/m",$matches[2],-1,PREG_SPLIT_NO_EMPTY) as $row){ 307 if (preg_match("/^($textile_a$textile_c\. )(.*)/m",$row,$rmtch)) { 308 $ratts = textile_pba($rmtch[1],'tr'); 309 $row = $rmtch[2]; 310 } else $ratts = ''; 311 312 foreach(explode("|",$row) as $textile_cell){ 313 $textile_ctyp = "d"; 314 if (preg_match("/^_/",$textile_cell)) $textile_ctyp = "h"; 315 if (preg_match("/^(_?$textile_s$textile_a$textile_c\. )(.*)/",$textile_cell,$textile_cmtch)) { 316 $textile_catts = textile_pba($textile_cmtch[1],'td'); 317 $textile_cell = $textile_cmtch[2]; 318 } else $textile_catts = ''; 319 320 if(trim($textile_cell)!='') 321 $textile_cells[] = "\t\t\t<t$textile_ctyp$textile_catts>$textile_cell</t$textile_ctyp>"; 322 } 323 $rows[] = "\t\t<tr$ratts>\n".join("\n",$textile_cells)."\n\t\t</tr>"; 324 unset($textile_cells,$textile_catts); 325 } 326 return "\t<table$tatts>\n".join("\n",$rows)."\n\t</table>\n\n"; 327 } 328 329 330// ------------------------------------------------------------- 331 function textile_lists($text) 332 { 333 global $textile_a,$textile_c; 334 return preg_replace_callback("/^([#*]+$textile_c .*)$(?![^#*])/smU","textile_fList",$text); 335 } 336 337// ------------------------------------------------------------- 338 function textile_fList($m) 339 { 340 global $textile_a,$textile_c; 341 $text = explode("\n",$m[0]); 342 foreach($text as $nr => $line){ 343 $nextline = isset($text[$nr+1]) ? $text[$nr+1] : false; 344 if(preg_match("/^([#*]+)($textile_a$textile_c) (.*)$/s",$line,$m)) { 345 list(,$tl,$textile_atts,$textile_content) = $m; 346 $nl = preg_replace("/^([#*]+)\s.*/","$1",$nextline); 347 if(!isset($lists[$tl])){ 348 $lists[$tl] = true; 349 $textile_atts = textile_pba($textile_atts); 350 $line = "\t<".textile_lT($tl)."l$textile_atts>\n\t<li>".$textile_content; 351 } else { 352 $line = "\t\t<li>".$textile_content; 353 } 354 355 if ($nl===$tl){ 356 $line .= "</li>"; 357 } elseif($nl=="*" or $nl=="#") { 358 $line .= "</li>\n\t</".textile_lT($tl)."l>\n\t</li>"; 359 unset($lists[$tl]); 360 } 361 if (!$nl) { 362 foreach($lists as $k=>$v){ 363 $line .= "</li>\n\t</".textile_lT($k)."l>"; 364 unset($lists[$k]); 365 } 366 } 367 } 368 $out[] = $line; 369 } 370 return join("\n",$out); 371 } 372 373// ------------------------------------------------------------- 374 function textile_lT($in) 375 { 376 return preg_match("/^#+/",$in) ? 'o' : 'u'; 377 } 378 379// ------------------------------------------------------------- 380 function textile_block($text) 381 { 382 global $textile_a,$textile_c; 383 384 $pre = false; 385 $find = array('bq','h[1-6]','fn\d+','p'); 386 387 $text = preg_replace("/(.+)\n(?![#*\s|])/", 388 "$1<br />", $text); 389 390 $text = explode("\n",$text); 391 array_push($text," "); 392 393 foreach($text as $line) { 394 if (preg_match('/<pre>/i',$line)) { $pre = true; } 395 foreach($find as $tag){ 396 $line = ($pre==false) 397 ? preg_replace_callback("/^($tag)($textile_a$textile_c)\.(?::(\S+))? (.*)$/", 398 "textile_fBlock",$line) 399 : $line; 400 } 401 402 $line = preg_replace('/^(?!\t|<\/?pre|<\/?code|$| )(.*)/',"\t<p>$1</p>",$line); 403 404 $line=($pre==true) ? str_replace("<br />","\n",$line):$line; 405 if (preg_match('/<\/pre>/i',$line)) { $pre = false; } 406 407 $out[] = $line; 408 } 409 return join("\n",$out); 410 } 411 412// ------------------------------------------------------------- 413 function textile_fBlock($m) 414 { 415# dump($m); 416 list(,$tag,$textile_atts,$textile_cite,$textile_content) = $m; 417 418 $textile_atts = textile_pba($textile_atts); 419 420 if(preg_match("/fn(\d+)/",$tag,$fns)){ 421 $tag = 'p'; 422 $textile_atts.= ' id="fn'.$fns[1].'"'; 423 $textile_content = '<sup>'.$fns[1].'</sup> '.$textile_content; 424 } 425 426 $textile_start = "\t<$tag"; 427 $end = "</$tag>"; 428 429 if ($tag=="bq") { 430 $textile_cite = textile_checkRefs($textile_cite); 431 $textile_cite = ($textile_cite!='') ? ' cite="'.$textile_cite.'"' : ''; 432 $textile_start = "\t<blockquote$textile_cite>\n\t\t<p"; 433 $end = "</p>\n\t</blockquote>"; 434 } 435 436 return "$textile_start$textile_atts>$textile_content$end"; 437 } 438 439 440// ------------------------------------------------------------- 441 function textile_span($text) 442 { 443 global $textile_c,$textile_pnct; 444 $qtags = array('\*\*','\*','\?\?','-','__','_','%','\+','~'); 445 446 foreach($qtags as $f) { 447 $text = preg_replace_callback( 448 "/(?<=^|\s|\>|[[:punct:]]|[{(\[]) 449 ($f) 450 ($textile_c) 451 (?::(\S+))? 452 (\w.+\w) 453 ([[:punct:]]*) 454 $f 455 (?=[])}]|[[:punct:]]+|\s|$) 456 /xmU","textile_fSpan",$text); 457 } 458 return $text; 459 } 460 461// ------------------------------------------------------------- 462 function textile_fSpan($m) 463 { 464# dump($m); 465 global $textile_c; 466 $qtags = array( 467 '*' => 'b', 468 '**' => 'strong', 469 '??' => 'cite', 470 '_' => 'em', 471 '__' => 'i', 472 '-' => 'del', 473 '%' => 'span', 474 '+' => 'ins', 475 '~' => 'sub'); 476 477 list(,$tag,$textile_atts,$textile_cite,$textile_content,$end) = $m; 478 $tag = $qtags[$tag]; 479 $textile_atts = textile_pba($textile_atts); 480 $textile_atts.= ($textile_cite!='') ? 'cite="'.$textile_cite.'"' : ''; 481 482 return "<$tag$textile_atts>$textile_content$end</$tag>"; 483 } 484 485// ------------------------------------------------------------- 486 function textile_links($text) 487 { 488 global $textile_c; 489 return preg_replace_callback('/ 490 ([\s[{(]|[[:punct:]])? # $pre 491 ("|") # start 492 ('.$textile_c.') # $textile_atts 493 ([^"]+) # $text 494 \s? 495 (?:\(([^)]+)\)(?="))? # $title 496 ("|"): 497 (\S+\b) # $url 498 (\/)? # $textile_slash 499 ([^\w\/;]*) # $post 500 (?=\s|$) 501 /Ux',"textile_fLink",$text); 502 } 503 504// ------------------------------------------------------------- 505 function textile_fLink($m) 506 { 507 list(,$pre,$textile_atts,$text,$title,$url,$textile_slash,$post) = $m; 508 509 $url = textile_checkRefs($url); 510 511 $textile_atts = textile_pba($textile_atts); 512 $textile_atts.= ($title!='') ? ' title="'.$title.'"' : ''; 513 514 $textile_atts = ($textile_atts!='') ? textile_shelve($textile_atts) : ''; 515 516 if ($text=='_') { 517 $text = $url; 518 } 519 520 if (preg_match('/^http(s?):\/\//i', $url)) { 521 $textile_atts.=' class="external"'; 522 } 523 524 return $pre.'<a href="'.$url.$textile_slash.'"'.$textile_atts.'>'.$text.'</a>'.$post; 525 526 } 527 528// ------------------------------------------------------------- 529 function textile_getRefs($text) 530 { 531 return preg_replace_callback("/(?<=^|\s)\[(.+)\]((?:http:\/\/|\/)\S+)(?=\s|$)/U", 532 "textile_refs",$text); 533 } 534 535// ------------------------------------------------------------- 536 function textile_refs($m) 537 { 538 list(,$flag,$url) = $m; 539 $GLOBALS['urlrefs'][$flag] = $url; 540 return ''; 541 } 542 543// ------------------------------------------------------------- 544 function textile_checkRefs($text) 545 { 546 global $urlrefs; 547 return (isset($urlrefs[$text])) ? $urlrefs[$text] : $text; 548 } 549 550// ------------------------------------------------------------- 551 function textile_image($text) 552 { 553 global $textile_c; 554 return preg_replace_callback("/ 555 \! # opening 556 (\<|\=|\>)? # optional alignment atts 557 ($textile_c) # optional style,class atts 558 (?:\. )? # optional dot-space 559 ([^\s(!]+) # presume this is the src 560 \s? # optional space 561 (?:\(([^\)]+)\))? # optional title 562 \! # closing 563 (?::(\S+))? # optional href 564 (?=\s|$) # lookahead: space or end of string 565 /Ux","textile_fImage",$text); 566 } 567 568// ------------------------------------------------------------- 569 function textile_fImage($m) 570 { 571 list(,$textile_algn,$textile_atts,$url) = $m; 572 $textile_atts = textile_pba($textile_atts); 573 $textile_atts.= ($textile_algn!='') ? ' align="'.textile_iAlign($textile_algn).'"' : ''; 574 $textile_atts.= (isset($m[4])) ? ' title="'.$m[4].'"' : ''; 575 $textile_size = @getimagesize($url); 576 if($textile_size) $textile_atts.= " $textile_size[3]"; 577 578 $href = (isset($m[5])) ? textile_checkRefs($m[5]) : ''; 579 $url = textile_checkRefs($url); 580 581 $out = ''; 582 $out.= ($href!='') ? '<a href="'.$href.'">' : ''; 583 $out.= '<img src="'.$url.'"'.$textile_atts.' />'; 584 $out.= ($href!='') ? '</a>' : ''; 585 586 return $out; 587 } 588 589// ------------------------------------------------------------- 590 function textile_code($text) 591 { 592 global $textile_pnct; 593 return preg_replace_callback("/ 594 (?:^|(?<=[\s\(])|([[{])) # 1 open bracket? 595 @ # opening 596 (?:\|(\w+)\|)? # 2 language 597 (.+) # 3 code 598 @ # closing 599 (?:$|([\]}])| 600 (?=[[:punct:]]{1,2}| 601 \s)) # 4 closing bracket? 602 /Ux","textile_fCode",$text); 603 } 604 605// ------------------------------------------------------------- 606 function textile_fCode($m) 607 { 608 list(,$before,$lang,$textile_code,$textile_after) = $m; 609 $lang = ($lang!='') ? ' language="'.$lang.'"' : ''; 610 return $before.'<code'.$lang.'>'.$textile_code.'</code>'.$textile_after; 611 } 612 613// ------------------------------------------------------------- 614 function textile_shelve($val) 615 { 616 $GLOBALS['shelf'][] = $val; 617 return ' <'.count($GLOBALS['shelf']).'>'; 618 } 619 620// ------------------------------------------------------------- 621 function textile_retrieve($text) 622 { 623 global $textile_shelf; 624 $i = 0; 625 if(is_array($textile_shelf)) { 626 foreach($textile_shelf as $r){ 627 $i++; 628 $text = str_replace("<$i>",$r,$text); 629 } 630 } 631 return $text; 632 } 633 634// ------------------------------------------------------------- 635 function textile_incomingEntities($text) 636 { 637 /* turn any incoming ampersands into a dummy character for now. 638 This uses a negative lookahead for alphanumerics followed by a semicolon, 639 implying an incoming html entity, to be skipped */ 640 641 return preg_replace("/&(?![#a-z0-9]+;)/i","x%x%",$text); 642 } 643 644// ------------------------------------------------------------- 645 function textile_encodeEntities($text) 646 { 647 /* Convert high and low ascii to entities. If multibyte string functions are 648 available (on by default in php 4.3+), we convert using unicode mapping as 649 defined in the function encode_high(). If not, we use php's nasty 650 built-in htmlentities() */ 651 652 return (function_exists('mb_encode_numericentity')) 653 ? textile_encode_high($text) 654 : htmlentities($text,ENT_NOQUOTES,"utf-8"); 655 } 656 657// ------------------------------------------------------------- 658 function textile_fixEntities($text) 659 { 660 /* de-entify any remaining angle brackets or ampersands */ 661 return str_replace(array(">", "<", "&"), 662 array(">", "<", "&"), $text); 663 } 664 665// ------------------------------------------------------------- 666 function textile_cleanWhiteSpace($text) 667 { 668 $out = str_replace(array("\r\n","\t"), array("\n",''), $text); 669 $out = preg_replace("/\n{3,}/","\n\n",$out); 670 $out = preg_replace("/\n *\n/","\n\n",$out); 671 $out = preg_replace('/"$/',"\" ", $out); 672 return $out; 673 } 674 675// ------------------------------------------------------------- 676 function textile_noTextile($text) 677 { 678 return preg_replace('/(^|\s)==(.*)==(\s|$)?/msU', 679 '$1<notextile>$2</notextile>$3',$text); 680 } 681 682// ------------------------------------------------------------- 683 function textile_superscript($text) 684 { 685 return preg_replace('/\^(.*)\^/mU','<sup>$1</sup>',$text); 686 } 687 688// ------------------------------------------------------------- 689 function textile_footnoteRef($text) 690 { 691 return preg_replace('/\b\[([0-9]+)\](\s)?/U', 692 '<sup><a href="#fn$1">$1</a></sup>$2',$text); 693 } 694 695// ------------------------------------------------------------- 696 function textile_glyphs($text) 697 { 698 // fix: hackish 699 $text = preg_replace('/"\z/',"\" ", $text); 700 701 $glyph_search = array( 702 '/([^\s[{(>])?\'(?(1)|(?=\s|s\b|[[:punct:]]))/', // single closing 703 '/\'/', // single opening 704 '/([^\s[{(>])?"(?(1)|(?=\s|[[:punct:]]))/', // double closing 705 '/"/', // double opening 706 '/\b( )?\.{3}/', // ellipsis 707 '/\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])/', // 3+ uppercase acronym 708 '/(^|[^"][>\s])([A-Z][A-Z0-9 ]{2,})([^<a-z0-9]|$)/', // 3+ uppercase caps 709 '/\s?--\s?/', // em dash 710 '/\s-\s/', // en dash 711 '/(\d+) ?x ?(\d+)/', // dimension sign 712 '/\b ?[([]TM[])]/i', // trademark 713 '/\b ?[([]R[])]/i', // registered 714 '/\b ?[([]C[])]/i'); // copyright 715 716 $glyph_replace = array( 717 '$1’$2', // single closing 718 '‘', // single opening 719 '$1”', // double closing 720 '“', // double opening 721 '$1…', // ellipsis 722 '<acronym title="$2">$1</acronym>', // 3+ uppercase acronym 723 '$1<span class="caps">$2</span>$3', // 3+ uppercase caps 724 '—', // em dash 725 ' – ', // en dash 726 '$1×$2', // dimension sign 727 '™', // trademark 728 '®', // registered 729 '©'); // copyright 730 731 732 $textile_codepre = false; 733 /* if no html, do a simple search and replace... */ 734 if (!preg_match("/<.*>/",$text)) { 735 $text = preg_replace($glyph_search,$glyph_replace,$text); 736 return $text; 737 } else { 738 $text = preg_split("/(<.*>)/U",$text,-1,PREG_SPLIT_DELIM_CAPTURE); 739 foreach($text as $line) { 740 $offtags = ('code|pre|kbd|notextile'); 741 742 /* matches are off if we're between <code>, <pre> etc. */ 743 if (preg_match('/<('.$offtags.')>/i',$line)) $textile_codepre = true; 744 if (preg_match('/<\/('.$offtags.')>/i',$line)) $textile_codepre = false; 745 746 if (!preg_match("/<.*>/",$line) && $textile_codepre == false) { 747 $line = preg_replace($glyph_search,$glyph_replace,$line); 748 } 749 750 /* do htmlspecial if between <code> */ 751 if ($textile_codepre == true) { 752 $line = htmlspecialchars($line,ENT_NOQUOTES,"UTF-8"); 753 $line = preg_replace('/<(\/?'.$offtags.')>/',"<$1>",$line); 754 } 755 756 $glyph_out[] = $line; 757 } 758 return join('',$glyph_out); 759 } 760 } 761 762// ------------------------------------------------------------- 763 function textile_iAlign($in) 764 { 765 $vals = array( 766 '<'=>'left', 767 '='=>'center', 768 '>'=>'right'); 769 return (isset($vals[$in])) ? $vals[$in] : ''; 770 } 771 772// ------------------------------------------------------------- 773 function textile_hAlign($in) 774 { 775 $vals = array( 776 '<'=>'left', 777 '='=>'center', 778 '>'=>'right', 779 '<>'=>'justify'); 780 return (isset($vals[$in])) ? $vals[$in] : ''; 781 } 782 783// ------------------------------------------------------------- 784 function textile_vAlign($in) 785 { 786 $vals = array( 787 '^'=>'top', 788 '-'=>'middle', 789 '~'=>'bottom'); 790 return (isset($vals[$in])) ? $vals[$in] : ''; 791 } 792 793// ------------------------------------------------------------- 794 function textile_encode_high($text,$textile_charset="UTF-8") 795 { 796 $textile_cmap = textile_cmap(); 797 return mb_encode_numericentity($text, $textile_cmap, $textile_charset); 798 } 799 800// ------------------------------------------------------------- 801 function textile_decode_high($text,$textile_charset="UTF-8") 802 { 803 $textile_cmap = textile_cmap(); 804 return mb_decode_numericentity($text, $textile_cmap, $textile_charset); 805 } 806 807// ------------------------------------------------------------- 808 function textile_cmap() 809 { 810 $f = 0xffff; 811 $textile_cmap = array( 812 160, 255, 0, $f, 813 402, 402, 0, $f, 814 913, 929, 0, $f, 815 931, 937, 0, $f, 816 945, 969, 0, $f, 817 977, 978, 0, $f, 818 982, 982, 0, $f, 819 8226, 8226, 0, $f, 820 8230, 8230, 0, $f, 821 8242, 8243, 0, $f, 822 8254, 8254, 0, $f, 823 8260, 8260, 0, $f, 824 8465, 8465, 0, $f, 825 8472, 8472, 0, $f, 826 8476, 8476, 0, $f, 827 8482, 8482, 0, $f, 828 8501, 8501, 0, $f, 829 8592, 8596, 0, $f, 830 8629, 8629, 0, $f, 831 8656, 8660, 0, $f, 832 8704, 8704, 0, $f, 833 8706, 8707, 0, $f, 834 8709, 8709, 0, $f, 835 8711, 8713, 0, $f, 836 8715, 8715, 0, $f, 837 8719, 8719, 0, $f, 838 8721, 8722, 0, $f, 839 8727, 8727, 0, $f, 840 8730, 8730, 0, $f, 841 8733, 8734, 0, $f, 842 8736, 8736, 0, $f, 843 8743, 8747, 0, $f, 844 8756, 8756, 0, $f, 845 8764, 8764, 0, $f, 846 8773, 8773, 0, $f, 847 8776, 8776, 0, $f, 848 8800, 8801, 0, $f, 849 8804, 8805, 0, $f, 850 8834, 8836, 0, $f, 851 8838, 8839, 0, $f, 852 8853, 8853, 0, $f, 853 8855, 8855, 0, $f, 854 8869, 8869, 0, $f, 855 8901, 8901, 0, $f, 856 8968, 8971, 0, $f, 857 9001, 9002, 0, $f, 858 9674, 9674, 0, $f, 859 9824, 9824, 0, $f, 860 9827, 9827, 0, $f, 861 9829, 9830, 0, $f, 862 338, 339, 0, $f, 863 352, 353, 0, $f, 864 376, 376, 0, $f, 865 710, 710, 0, $f, 866 732, 732, 0, $f, 867 8194, 8195, 0, $f, 868 8201, 8201, 0, $f, 869 8204, 8207, 0, $f, 870 8211, 8212, 0, $f, 871 8216, 8218, 0, $f, 872 8218, 8218, 0, $f, 873 8220, 8222, 0, $f, 874 8224, 8225, 0, $f, 875 8240, 8240, 0, $f, 876 8249, 8250, 0, $f, 877 8364, 8364, 0, $f); 878 return $textile_cmap; 879 } 880 881 882// ------------------------------------------------------------- 883 function textile_popup_help($name,$helpvar,$windowW,$windowH) { 884 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 />'; 885 } 886 887// ------------------------------------------------------------- 888 function textile_txtgps($thing) 889 { 890 if (isset($_POST[$thing])){ 891 if (get_magic_quotes_gpc()==1){ 892 return stripslashes($_POST[$thing]); 893 } else { 894 return $_POST[$thing]; 895 } 896 } else { 897 return ''; 898 } 899 } 900 901 902// ------------------------------------------------------------- 903// The following functions are used to detextile html, a process 904// still in development. 905 906 907// ------------------------------------------------------------- 908 function textile_detextile($text) { 909 910 $text = preg_replace("/<br \/>\s*/","\n",$text); 911 912 $oktags = array('p','ol','ul','li','i','b','em','strong','span','a','h[1-6]', 913 'table','tr','td','u','del','sup','sub','blockquote'); 914 915 foreach($oktags as $tag){ 916 $text = preg_replace_callback("/\t*<(".$tag.")\s*([^>]*)>(.*)<\/\\1>/Usi", 917 "textile_processTag",$text); 918 } 919 920 $glyphs = array( 921 '’'=>'\'', # single closing 922 '‘'=>'\'', # single opening 923 '”'=>'"', # double closing 924 '“'=>'"', # double opening 925 '—'=>'--', # em dash 926 '–'=>' - ', # en dash 927 '×' =>'x', # dimension sign 928 '™'=>'(TM)', # trademark 929 '®' =>'(R)', # registered 930 '©' =>'(C)', # copyright 931 '…'=>'...' # ellipsis 932 ); 933 934 foreach($glyphs as $f=>$r){ 935 $text = str_replace($f,$r,$text); 936 } 937 938 $list = false; 939 940 $text = preg_split("/(<.*>)/U",$text,-1,PREG_SPLIT_DELIM_CAPTURE); 941 foreach($text as $line){ 942 943 if ($list == false && preg_match('/<ol/',$line)){ 944 $line = ""; 945 $list = "o"; 946 } else if (preg_match('/<\/ol/',$line)){ 947 $line = ""; 948 $list = false; 949 } else if ($list == false && preg_match('/<ul/',$line)){ 950 $line = ""; 951 $list = "u"; 952 } else if (preg_match('/<\/ul/',$line)){ 953 $line = ""; 954 $list = false; 955 } else if ($list == 'o'){ 956 $line = preg_replace('/<li.*>/U','# ', $line); 957 } else if ($list == 'u'){ 958 $line = preg_replace('/<li.*>/U','* ', $line); 959 } 960 $glyph_out[] = $line; 961 } 962 963 $text = implode('',$glyph_out); 964 965 $text = preg_replace('/^\t* *p\. /m','',$text); 966 967 return textile_decode_high($text); 968 } 969 970 971// ------------------------------------------------------------- 972 function textile_processTag($matches) 973 { 974 list($textile_all,$tag,$textile_atts,$textile_content) = $matches; 975 $textile_a = textile_splat($textile_atts); 976# dump($tag); dump($textile_content); dump($textile_a); 977 978 $phr = array( 979 'em'=>'_', 980 'i'=>'__', 981 'b'=>'**', 982 'strong'=>'*', 983 'cite'=>'??', 984 'del'=>'-', 985 'ins'=>'+', 986 'sup'=>'^', 987 'sub'=>'~', 988 'span'=>'%'); 989 990 $blk = array('p','h1','h2','h3','h4','h5','h6'); 991 992 if(isset($phr[$tag])) { 993 return $phr[$tag].textile_sci($textile_a).$textile_content.$phr[$tag]; 994 } elseif($tag=='blockquote') { 995 return 'bq.'.textile_sci($textile_a).' '.$textile_content; 996 } elseif(in_array($tag,$blk)) { 997 return $tag.textile_sci($textile_a).'. '.$textile_content; 998 } elseif ($tag=='a') { 999 $t = textile_filterAtts($textile_a,array('href','title')); 1000 $out = '"'.$textile_content; 1001 $out.= (isset($t['title'])) ? ' ('.$t['title'].')' : ''; 1002 $out.= '":'.$t['href']; 1003 return $out; 1004 } else { 1005 return $textile_all; 1006 } 1007 } 1008 1009// ------------------------------------------------------------- 1010 function textile_filterAtts($textile_atts,$ok) 1011 { 1012 foreach($textile_atts as $textile_a) { 1013 if(in_array($textile_a['name'],$ok)) { 1014 if($textile_a['att']!='') { 1015 $out[$textile_a['name']] = $textile_a['att']; 1016 } 1017 } 1018 } 1019# dump($out); 1020 return $out; 1021 } 1022 1023// ------------------------------------------------------------- 1024 function textile_sci($textile_a) 1025 { 1026 $out = ''; 1027 foreach($textile_a as $t){ 1028 $out.= ($t['name']=='class') ? '(='.$t['att'].')' : ''; 1029 $out.= ($t['name']=='id') ? '[='.$t['att'].']' : ''; 1030 $out.= ($t['name']=='style') ? '{='.$t['att'].'}' : ''; 1031 $out.= ($t['name']=='cite') ? ':'.$t['att'] : ''; 1032 } 1033 return $out; 1034 } 1035 1036// ------------------------------------------------------------- 1037 function textile_splat($textile_attr) // returns attributes as an array 1038 { 1039 $textile_arr = array(); 1040 $textile_atnm = ''; 1041 $mode = 0; 1042 1043 while (strlen($textile_attr) != 0){ 1044 $ok = 0; 1045 switch ($mode) { 1046 case 0: // name 1047 if (preg_match('/^([a-z]+)/i', $textile_attr, $match)) { 1048 $textile_atnm = $match[1]; $ok = $mode = 1; 1049 $textile_attr = preg_replace('/^[a-z]+/i', '', $textile_attr); 1050 } 1051 break; 1052 1053 case 1: // = 1054 if (preg_match('/^\s*=\s*/', $textile_attr)) { 1055 $ok = 1; $mode = 2; 1056 $textile_attr = preg_replace('/^\s*=\s*/', '', $textile_attr); 1057 break; 1058 } 1059 if (preg_match('/^\s+/', $textile_attr)) { 1060 $ok = 1; $mode = 0; 1061 $textile_arr[] = array('name'=>$textile_atnm,'whole'=>$textile_atnm,'att'=>$textile_atnm); 1062 $textile_attr = preg_replace('/^\s+/', '', $textile_attr); 1063 } 1064 break; 1065 1066 case 2: // value 1067 if (preg_match('/^("[^"]*")(\s+|$)/', $textile_attr, $match)) { 1068 $textile_arr[]=array('name' =>$textile_atnm,'whole'=>$textile_atnm.'='.$match[1], 1069 'att'=>str_replace('"','',$match[1])); 1070 $ok = 1; $mode = 0; 1071 $textile_attr = preg_replace('/^"[^"]*"(\s+|$)/', '', $textile_attr); 1072 break; 1073 } 1074 if (preg_match("/^('[^']*')(\s+|$)/", $textile_attr, $match)) { 1075 $textile_arr[]=array('name' =>$textile_atnm,'whole'=>$textile_atnm.'='.$match[1], 1076 'att'=>str_replace("'",'',$match[1])); 1077 $ok = 1; $mode = 0; 1078 $textile_attr = preg_replace("/^'[^']*'(\s+|$)/", '', $textile_attr); 1079 break; 1080 } 1081 if (preg_match("/^(\w+)(\s+|$)/", $textile_attr, $match)) { 1082 $textile_arr[]= 1083 array('name'=>$textile_atnm,'whole'=>$textile_atnm.'="'.$match[1].'"', 1084 'att'=>$match[1]); 1085 $ok = 1; $mode = 0; 1086 $textile_attr = preg_replace("/^\w+(\s+|$)/", '', $textile_attr); 1087 } 1088 break; 1089 } 1090 if ($ok == 0){ 1091 $textile_attr = preg_replace('/^\S*\s*/', '', $textile_attr); 1092 $mode = 0; 1093 } 1094 } 1095 if ($mode == 1) $textile_arr[] = 1096 array ('name'=>$textile_atnm,'whole'=>$textile_atnm.'="'.$textile_atnm.'"','att'=>$textile_atnm); 1097 1098 return $textile_arr; 1099 } 1100?> 1101