[ Index ]

PHP Cross Reference of Textpattern 4.0.8

title

Body

[close]

/textpattern/lib/ -> classTextile.php (source)

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


Generated: Thu May 21 23:03:01 2009 Cross-referenced by PHPXref 0.7