[ Index ]

PHP Cross Reference of phpwcms V1.5.0 _r431 (28.01.12)

title

Body

[close]

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


Generated: Sun Jan 29 16:31:14 2012 Cross-referenced by PHPXref 0.7.1