Commit 823bf244a8f7121de6ced72a592d50ec6877f827

Updated Textile to a PHP5-compatible version.
Thanks to Andrea Fiore for suggesting this.
classTextile.php
(901 / 823)
  
33/**
44 * Example: get XHTML from a given Textile-markup string ($string)
55 *
6 * $textile = new Textile;
7 * echo $textile->TextileThis($string);
6 * $textile = new Textile;
7 * echo $textile->TextileThis($string);
88 *
99 */
1010
1111/*
1212$Id: classTextile.php 216 2006-10-17 22:31:53Z zem $
13$LastChangedRevision: 216 $
13$LastChangedRevision$
1414*/
1515
1616/*
6464
6565Block modifier syntax:
6666
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>
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>
7070
71 Paragraph: p. (also applied by default)
72 Example: p. Text -> <p>Text</p>
71 Paragraph: p. (also applied by default)
72 Example: p. Text -> <p>Text</p>
7373
74 Blockquote: bq.
75 Example: bq. Block quotation... -> <blockquote>Block quotation...</blockquote>
74 Blockquote: bq.
75 Example: bq. Block quotation... -> <blockquote>Block quotation...</blockquote>
7676
77 Blockquote with citation: bq.:http://citation.url
78 Example: bq.:http://textism.com/ Text...
79 -> <blockquote cite="http://textism.com">Text...</blockquote>
77 Blockquote with citation: bq.:http://citation.url
78 Example: bq.:http://textism.com/ Text...
79 -> <blockquote cite="http://textism.com">Text...</blockquote>
8080
81 Footnote: fn(1-100).
82 Example: fn1. Footnote... -> <p id="fn1">Footnote...</p>
81 Footnote: fn(1-100).
82 Example: fn1. Footnote... -> <p id="fn1">Footnote...</p>
8383
84 Numeric list: #, ##
85 Consecutive paragraphs beginning with # are wrapped in ordered list tags.
86 Example: <ol><li>ordered list</li></ol>
84 Numeric list: #, ##
85 Consecutive paragraphs beginning with # are wrapped in ordered list tags.
86 Example: <ol><li>ordered list</li></ol>
8787
88 Bulleted list: *, **
89 Consecutive paragraphs beginning with * are wrapped in unordered list tags.
90 Example: <ul><li>unordered list</li></ul>
88 Bulleted list: *, **
89 Consecutive paragraphs beginning with * are wrapped in unordered list tags.
90 Example: <ul><li>unordered list</li></ul>
9191
9292Phrase modifier syntax:
9393
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>
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>
105105
106 ==notextile== -> leave text alone (do not format)
106 ==notextile== -> leave text alone (do not format)
107107
108 "linktext":url -> <a href="url">linktext</a>
109 "linktext(title)":url -> <a href="url" title="title">linktext</a>
108 "linktext":url -> <a href="url">linktext</a>
109 "linktext(title)":url -> <a href="url" title="title">linktext</a>
110110
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>
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>
114114
115ABC(Always Be Closing) -> <acronym title="Always Be Closing">ABC</acronym>
115ABC(Always Be Closing) -> <acronym title="Always Be Closing">ABC</acronym>
116116
117117
118118Table syntax:
119119
120 Simple tables:
120 Simple tables:
121121
122 |a|simple|table|row|
123 |And|Another|table|row|
122 |a|simple|table|row|
123 |And|Another|table|row|
124124
125 |_. A|_. table|_. header|_.row|
126 |A|simple|table|row|
125 |_. A|_. table|_. header|_.row|
126 |A|simple|table|row|
127127
128 Tables with attributes:
128 Tables with attributes:
129129
130 table{border:1px solid black}.
131 {background:#ddd;color:red}. |{}| | | |
130 table{border:1px solid black}.
131 {background:#ddd;color:red}. |{}| | | |
132132
133133
134134Applying Attributes:
135135
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.
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.
138138
139 The following characters quickly alter the alignment of block elements:
139 The following characters quickly alter the alignment of block elements:
140140
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
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
145145
146 These will change vertical alignment in table cells:
146 These will change vertical alignment in table cells:
147147
148 ^ -> top ex. |^. top-aligned table cell|
149 - -> middle |-. middle aligned|
150 ~ -> bottom |~. bottom aligned cell|
148 ^ -> top ex. |^. top-aligned table cell|
149 - -> middle |-. middle aligned|
150 ~ -> bottom |~. bottom aligned cell|
151151
152 Plain (parentheses) inserted between block syntax and the closing dot-space
153 indicate classes and ids:
152 Plain (parentheses) inserted between block syntax and the closing dot-space
153 indicate classes and ids:
154154
155 p(hector). paragraph -> <p class="hector">paragraph</p>
155 p(hector). paragraph -> <p class="hector">paragraph</p>
156156
157 p(#fluid). paragraph -> <p id="fluid">paragraph</p>
157 p(#fluid). paragraph -> <p id="fluid">paragraph</p>
158158
159 (classes and ids can be combined)
160 p(hector#fluid). paragraph -> <p class="hector" id="fluid">paragraph</p>
159 (classes and ids can be combined)
160 p(hector#fluid). paragraph -> <p class="hector" id="fluid">paragraph</p>
161161
162 Curly {brackets} insert arbitrary css style
162 Curly {brackets} insert arbitrary css style
163163
164 p{line-height:18px}. paragraph -> <p style="line-height:18px">paragraph</p>
164 p{line-height:18px}. paragraph -> <p style="line-height:18px">paragraph</p>
165165
166 h3{color:red}. header 3 -> <h3 style="color:red">header 3</h3>
166 h3{color:red}. header 3 -> <h3 style="color:red">header 3</h3>
167167
168 Square [brackets] insert language attributes
168 Square [brackets] insert language attributes
169169
170 p[no]. paragraph -> <p lang="no">paragraph</p>
170 p[no]. paragraph -> <p lang="no">paragraph</p>
171171
172 %[fr]phrase% -> <span lang="fr">phrase</span>
172 %[fr]phrase% -> <span lang="fr">phrase</span>
173173
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
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
176176
177 #{color:blue} one -> <ol style="color:blue">
178 # big <li>one</li>
179 # list <li>big</li>
180 <li>list</li>
181 </ol>
177 #{color:blue} one -> <ol style="color:blue">
178 # big <li>one</li>
179 # list <li>big</li>
180 <li>list</li>
181 </ol>
182182
183 Using the span tag to style a phrase
183 Using the span tag to style a phrase
184184
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>
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>
187187
188188*/
189189
192192@define('txt_quote_single_close', '&#8217;');
193193@define('txt_quote_double_open', '&#8220;');
194194@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;');
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;');
205205
206206class Textile
207207{
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: 216 $';
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$';
232
233 var $doc_root;
232234
233235// -------------------------------------------------------------
234 function Textile()
235 {
236 $this->hlgn = "(?:\<(?!>)|(?<!<)\>|\<\>|\=|[()]+(?! ))";
237 $this->vlgn = "[\-^~]";
238 $this->clas = "(?:\([^)]+\))";
239 $this->lnge = "(?:\[[^]]+\])";
240 $this->styl = "(?:\{[^}]+\})";
241 $this->cspn = "(?:\\\\\d+)";
242 $this->rspn = "(?:\/\d+)";
243 $this->a = "(?:{$this->hlgn}|{$this->vlgn})*";
244 $this->s = "(?:{$this->cspn}|{$this->rspn})*";
245 $this->c = "(?:{$this->clas}|{$this->styl}|{$this->lnge}|{$this->hlgn})*";
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})*";
246248
247 $this->pnct = '[\!"#\$%&\'()\*\+,\-\./:;<=>\?@\[\\\]\^_`{\|}\~]';
248 $this->urlch = '[\w"$\-_.+!*\'(),";\/?:@=&%#{}|\\^~\[\]`]';
249 $this->pnct = '[\!"#\$%&\'()\*\+,\-\./:;<=>\?@\[\\\]\^_`{\|}\~]';
250 $this->urlch = '[\w"$\-_.+!*\'(),";\/?:@=&%#{}|\\^~\[\]`]';
249251
250 $this->url_schemes = array('http','https','ftp','mailto');
252 $this->url_schemes = array('http','https','ftp','mailto');
251253
252 $this->btag = array('bq', 'bc', 'notextile', 'pre', 'h[1-6]', 'fn\d+', 'p');
254 $this->btag = array('bq', 'bc', 'notextile', 'pre', 'h[1-6]', 'fn\d+', 'p');
253255
254 $this->glyph = array(
255 'quote_single_open' => txt_quote_single_open,
256 'quote_single_close' => txt_quote_single_close,
257 'quote_double_open' => txt_quote_double_open,
258 'quote_double_close' => txt_quote_double_close,
259 'apostrophe' => txt_apostrophe,
260 'prime' => txt_prime,
261 'prime_double' => txt_prime_double,
262 'ellipsis' => txt_ellipsis,
263 'emdash' => txt_emdash,
264 'endash' => txt_endash,
265 'dimension' => txt_dimension,
266 'trademark' => txt_trademark,
267 'registered' => txt_registered,
268 'copyright' => txt_copyright,
269 );
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 );
270272
271 if (defined('hu'))
272 $this->hu = hu;
273 if (defined('hu'))
274 $this->hu = hu;
273275
274 }
276 if (defined('DIRECTORY_SEPARATOR'))
277 $this->ds = constant('DIRECTORY_SEPARATOR');
278 else
279 $this->ds = '/';
275280
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
276289// -------------------------------------------------------------
277 function TextileThis($text, $lite='', $encode='', $noimage='', $strict='', $rel='')
278 {
279 if ($rel)
280 $this->rel = ' rel="'.$rel.'" ';
281 $this->lite = $lite;
282 $this->noimage = $noimage;
283290
284 if ($encode) {
285 $text = $this->incomingEntities($text);
286 $text = str_replace("x%x%", "&#38;", $text);
287 return $text;
288 } else {
291 function TextileThis($text, $lite = '', $encode = '', $noimage = '', $strict = '', $rel = '')
292 {
293 $this->rel = ($rel) ? ' rel="'.$rel.'"' : '';
289294
290 if(!$strict) {
291 $text = $this->cleanWhiteSpace($text);
292 }
295 $this->lite = $lite;
296 $this->noimage = $noimage;
293297
294 $text = $this->getRefs($text);
298 if ($encode) {
299 $text = $this->incomingEntities($text);
300 $text = str_replace("x%x%", "&#38;", $text);
301 return $text;
302 } else {
295303
296 if (!$lite) {
297 $text = $this->block($text);
298 }
304 if(!$strict) {
305 $text = $this->cleanWhiteSpace($text);
306 }
299307
300 $text = $this->retrieve($text);
308 if (!$lite) {
309 $text = $this->block($text);
310 }
301311
302 // just to be tidy
303 $text = str_replace("<br />", "<br />\n", $text);
312 $text = $this->retrieve($text);
313 $text = $this->retrieveURLs($text);
304314
305 return $text;
306 }
307 }
315 // just to be tidy
316 $text = str_replace("<br />", "<br />\n", $text);
308317
318 return $text;
319 }
320 }
321
309322// -------------------------------------------------------------
310 function TextileRestricted($text, $lite=1, $noimage=1, $rel='nofollow')
311 {
312 $this->restricted = true;
313 $this->lite = $lite;
314 $this->noimage = $noimage;
315 if ($rel)
316 $this->rel = ' rel="'.$rel.'" ';
317323
318 // escape any raw html
319 $text = $this->encode_html($text, 0);
324 function TextileRestricted($text, $lite = 1, $noimage = 1, $rel = 'nofollow')
325 {
326 $this->restricted = true;
327 $this->lite = $lite;
328 $this->noimage = $noimage;
320329
321 $text = $this->cleanWhiteSpace($text);
322 $text = $this->getRefs($text);
330 $this->rel = ($rel) ? ' rel="'.$rel.'"' : '';
323331
324 if ($lite) {
325 $text = $this->blockLite($text);
326 }
327 else {
328 $text = $this->block($text);
329 }
332 // escape any raw html
333 $text = $this->encode_html($text, 0);
330334
331 $text = $this->retrieve($text);
335 $text = $this->cleanWhiteSpace($text);
332336
333 // just to be tidy
334 $text = str_replace("<br />", "<br />\n", $text);
337 if ($lite) {
338 $text = $this->blockLite($text);
339 }
340 else {
341 $text = $this->block($text);
342 }
335343
336 return $text;
337 }
344 $text = $this->retrieve($text);
345 $text = $this->retrieveURLs($text);
338346
347 // just to be tidy
348 $text = str_replace("<br />", "<br />\n", $text);
349
350 return $text;
351 }
352
339353// -------------------------------------------------------------
340 function pba($in, $element = "") // "parse block attributes"
341 {
342 $style = '';
343 $class = '';
344 $lang = '';
345 $colspan = '';
346 $rowspan = '';
347 $id = '';
348 $atts = '';
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 = '';
349363
350 if (!empty($in)) {
351 $matched = $in;
352 if ($element == 'td') {
353 if (preg_match("/\\\\(\d+)/", $matched, $csp)) $colspan = $csp[1];
354 if (preg_match("/\/(\d+)/", $matched, $rsp)) $rowspan = $rsp[1];
355 }
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 }
356370
357 if ($element == 'td' or $element == 'tr') {
358 if (preg_match("/($this->vlgn)/", $matched, $vert))
359 $style[] = "vertical-align:" . $this->vAlign($vert[1]) . ";";
360 }
371 if ($element == 'td' or $element == 'tr') {
372 if (preg_match("/($this->vlgn)/", $matched, $vert))
373 $style[] = "vertical-align:" . $this->vAlign($vert[1]) . ";";
374 }
361375
362 if (preg_match("/\{([^}]*)\}/", $matched, $sty)) {
363 $style[] = rtrim($sty[1], ';') . ';';
364 $matched = str_replace($sty[0], '', $matched);
365 }
376 if (preg_match("/\{([^}]*)\}/", $matched, $sty)) {
377 $style[] = rtrim($sty[1], ';') . ';';
378 $matched = str_replace($sty[0], '', $matched);
379 }
366380
367 if (preg_match("/\[([^]]+)\]/U", $matched, $lng)) {
368 $lang = $lng[1];
369 $matched = str_replace($lng[0], '', $matched);
370 }
381 if (preg_match("/\[([^]]+)\]/U", $matched, $lng)) {
382 $lang = $lng[1];
383 $matched = str_replace($lng[0], '', $matched);
384 }
371385
372 if (preg_match("/\(([^()]+)\)/U", $matched, $cls)) {
373 $class = $cls[1];
374 $matched = str_replace($cls[0], '', $matched);
375 }
386 if (preg_match("/\(([^()]+)\)/U", $matched, $cls)) {
387 $class = $cls[1];
388 $matched = str_replace($cls[0], '', $matched);
389 }
376390
377 if (preg_match("/([(]+)/", $matched, $pl)) {
378 $style[] = "padding-left:" . strlen($pl[1]) . "em;";
379 $matched = str_replace($pl[0], '', $matched);
380 }
391 if (preg_match("/([(]+)/", $matched, $pl)) {
392 $style[] = "padding-left:" . strlen($pl[1]) . "em;";
393 $matched = str_replace($pl[0], '', $matched);
394 }
381395
382 if (preg_match("/([)]+)/", $matched, $pr)) {
383 // $this->dump($pr);
384 $style[] = "padding-right:" . strlen($pr[1]) . "em;";
385 $matched = str_replace($pr[0], '', $matched);
386 }
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 }
387401
388 if (preg_match("/($this->hlgn)/", $matched, $horiz))
389 $style[] = "text-align:" . $this->hAlign($horiz[1]) . ";";
402 if (preg_match("/($this->hlgn)/", $matched, $horiz))
403 $style[] = "text-align:" . $this->hAlign($horiz[1]) . ";";
390404
391 if (preg_match("/^(.*)#(.*)$/", $class, $ids)) {
392 $id = $ids[2];
393 $class = $ids[1];
394 }
405 if (preg_match("/^(.*)#(.*)$/", $class, $ids)) {
406 $id = $ids[2];
407 $class = $ids[1];
408 }
395409
396 if ($this->restricted)
397 return ($lang) ? ' lang="' . $lang .'"':'';
410 if ($this->restricted)
411 return ($lang) ? ' lang="' . $lang .'"':'';
398412
399 return join('',array(
400 ($style) ? ' style="' . join("", $style) .'"':'',
401 ($class) ? ' class="' . $class .'"':'',
402 ($lang) ? ' lang="' . $lang .'"':'',
403 ($id) ? ' id="' . $id .'"':'',
404 ($colspan) ? ' colspan="' . $colspan .'"':'',
405 ($rowspan) ? ' rowspan="' . $rowspan .'"':''
406 ));
407 }
408 return '';
409 }
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 }
410424
411425// -------------------------------------------------------------
412 function hasRawText($text)
413 {
414 // checks whether the text has text not already enclosed by a block tag
415 $r = trim(preg_replace('@<(p|blockquote|div|form|table|ul|ol|pre|h\d)[^>]*?>.*</\1>@s', '', trim($text)));
416 $r = trim(preg_replace('@<(hr|br)[^>]*?/>@', '', $r));
417 return '' != $r;
418 }
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 }
419433
420434// -------------------------------------------------------------
421 function table($text)
422 {
423 $text = $text . "\n\n";
424 return preg_replace_callback("/^(?:table(_?{$this->s}{$this->a}{$this->c})\. ?\n)?^({$this->a}{$this->c}\.? ?\|.*\|)\n\n/smU",
425 array(&$this, "fTable"), $text);
426 }
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 }
427441
428442// -------------------------------------------------------------
429 function fTable($matches)
430 {
431 $tatts = $this->pba($matches[1], 'table');
443 function fTable($matches)
444 {
445 $tatts = $this->pba($matches[1], 'table');
432446
433 foreach(preg_split("/\|$/m", $matches[2], -1, PREG_SPLIT_NO_EMPTY) as $row) {
434 if (preg_match("/^($this->a$this->c\. )(.*)/m", ltrim($row), $rmtch)) {
435 $ratts = $this->pba($rmtch[1], 'tr');
436 $row = $rmtch[2];
437 } else $ratts = '';
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 = '';
438452
439 $cells = array();
440 foreach(explode("|", $row) as $cell) {
441 $ctyp = "d";
442 if (preg_match("/^_/", $cell)) $ctyp = "h";
443 if (preg_match("/^(_?$this->s$this->a$this->c\. )(.*)/", $cell, $cmtch)) {
444 $catts = $this->pba($cmtch[1], 'td');
445 $cell = $cmtch[2];
446 } else $catts = '';
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 = '';
447461
448 $cell = $this->graf($this->span($cell));
462 $cell = $this->graf($cell);
449463
450 if (trim($cell) != '')
451 $cells[] = "\t\t\t<t$ctyp$catts>$cell</t$ctyp>";
452 }
453 $rows[] = "\t\t<tr$ratts>\n" . join("\n", $cells) . ($cells ? "\n" : "") . "\t\t</tr>";
454 unset($cells, $catts);
455 }
456 return "\t<table$tatts>\n" . join("\n", $rows) . "\n\t</table>\n\n";
457 }
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 }
458472
459473// -------------------------------------------------------------
460 function lists($text)
461 {
462 return preg_replace_callback("/^([#*]+$this->c .*)$(?![^#*])/smU", array(&$this, "fList"), $text);
463 }
474 function lists($text)
475 {
476 return preg_replace_callback("/^([#*]+$this->c .*)$(?![^#*])/smU", array(&$this, "fList"), $text);
477 }
464478
465479// -------------------------------------------------------------
466 function fList($m)
467 {
468 $text = explode("\n", $m[0]);
469 foreach($text as $line) {
470 $nextline = next($text);
471 if (preg_match("/^([#*]+)($this->a$this->c) (.*)$/s", $line, $m)) {
472 list(, $tl, $atts, $content) = $m;
473 $nl = '';
474 if (preg_match("/^([#*]+)\s.*/", $nextline, $nm))
475 $nl = $nm[1];
476 if (!isset($lists[$tl])) {
477 $lists[$tl] = true;
478 $atts = $this->pba($atts);
479 $line = "\t<" . $this->lT($tl) . "l$atts>\n\t\t<li>" . $this->graf($content);
480 } else {
481 $line = "\t\t<li>" . $this->graf($content);
482 }
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 }
483497
484 if(strlen($nl) <= strlen($tl)) $line .= "</li>";
485 foreach(array_reverse($lists) as $k => $v) {
486 if(strlen($k) > strlen($nl)) {
487 $line .= "\n\t</" . $this->lT($k) . "l>";
488 if(strlen($k) > 1)
489 $line .= "</li>";
490 unset($lists[$k]);
491 }
492 }
493 }
494 $out[] = $line;
495 }
496 return join("\n", $out);
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 }
498515
499516// -------------------------------------------------------------
500 function lT($in)
501 {
502 return preg_match("/^#+/", $in) ? 'o' : 'u';
503 }
517 function lT($in)
518 {
519 return preg_match("/^#+/", $in) ? 'o' : 'u';
520 }
504521
505522// -------------------------------------------------------------
506 function doPBr($in)
507 {
508 return preg_replace_callback('@<(p)([^>]*?)>(.*)(</\1>)@s', array(&$this, 'doBr'), $in);
509 }
523 function doTagBr($tag, $in)
524 {
525 return preg_replace_callback('@<('.preg_quote($tag).')([^>]*?)>(.*)(</\1>)@s', array(&$this, 'doBr'), $in);
526 }
510527
528
511529// -------------------------------------------------------------
512 function doBr($m)
513 {
514 $content = preg_replace("@(.+)(?<!<br>|<br />)\n(?![#*\s|])@", '$1<br />', $m[3]);
515 return '<'.$m[1].$m[2].'>'.$content.$m[4];
516 }
530 function doPBr($in)
531 {
532 return $this->doTagBr('p', $in);
533 }
517534
518535// -------------------------------------------------------------
519 function block($text)
520 {
521 $find = $this->btag;
522 $tre = join('|', $find);
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 }
523541
524 $text = explode("\n\n", $text);
542// -------------------------------------------------------------
543 function block($text)
544 {
545 $find = $this->btag;
546 $tre = join('|', $find);
525547
526 $tag = 'p';
527 $atts = $cite = $graf = $ext = '';
548 $text = explode("\n\n", $text);
528549
529 foreach($text as $line) {
530 $anon = 0;
531 if (preg_match("/^($tre)($this->a$this->c)\.(\.?)(?::(\S+))? (.*)$/s", $line, $m)) {
532 // last block was extended, so close it
533 if ($ext)
534 $out[count($out)-1] .= $c1;
535 // new block
536 list(,$tag,$atts,$ext,$cite,$graf) = $m;
537 list($o1, $o2, $content, $c2, $c1) = $this->fBlock(array(0,$tag,$atts,$ext,$cite,$graf));
550 $tag = 'p';
551 $atts = $cite = $graf = $ext = '';
538552
539 // leave off c1 if this block is extended, we'll close it at the start of the next block
540 if ($ext)
541 $line = $o1.$o2.$content.$c2;
542 else
543 $line = $o1.$o2.$content.$c2.$c1;
544 }
545 else {
546 // anonymous block
547 $anon = 1;
548 if ($ext or !preg_match('/^ /', $line)) {
549 list($o1, $o2, $content, $c2, $c1) = $this->fBlock(array(0,$tag,$atts,$ext,$cite,$line));
550 // skip $o1/$c1 because this is part of a continuing extended block
551 if ($tag == 'p' and !$this->hasRawText($content)) {
552 $line = $content;
553 }
554 else {
555 $line = $o2.$content.$c2;
556 }
557 }
558 else {
559 $line = $this->graf($line);
560 }
561 }
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));
562562
563 $line = $this->doPBr($line);
564 $line = preg_replace('/<br>/', '<br />', $line);
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 }
565586
566 if ($ext and $anon)
567 $out[count($out)-1] .= "\n".$line;
568 else
569 $out[] = $line;
587 $line = $this->doPBr($line);
588 $line = preg_replace('/<br>/', '<br />', $line);
570589
571 if (!$ext) {
572 $tag = 'p';
573 $atts = '';
574 $cite = '';
575 $graf = '';
576 }
577 }
578 if ($ext) $out[count($out)-1] .= $c1;
579 return join("\n\n", $out);
580 }
590 if ($ext and $anon)
591 $out[count($out)-1] .= "\n".$line;
592 else
593 $out[] = $line;
581594
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 }
582605
583606
607
584608// -------------------------------------------------------------
585 function fBlock($m)
586 {
587 // $this->dump($m);
588 list(, $tag, $atts, $ext, $cite, $content) = $m;
589 $atts = $this->pba($atts);
609 function fBlock($m)
610 {
611 // $this->dump($m);
612 list(, $tag, $att, $ext, $cite, $content) = $m;
613 $atts = $this->pba($att);
590614
591 $o1 = $o2 = $c2 = $c1 = '';
615 $o1 = $o2 = $c2 = $c1 = '';
592616
593 if (preg_match("/fn(\d+)/", $tag, $fns)) {
594 $tag = 'p';
595 $fnid = empty($this->fn[$fns[1]]) ? $fns[1] : $this->fn[$fns[1]];
596 $atts .= ' id="fn' . $fnid . '"';
597 if (strpos($atts, 'class=') === false)
598 $atts .= ' class="footnote"';
599 $content = '<sup>' . $fns[1] . '</sup> ' . $content;
600 }
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 }
601625
602 if ($tag == "bq") {
603 $cite = $this->checkRefs($cite);
604 $cite = ($cite != '') ? ' cite="' . $cite . '"' : '';
605 $o1 = "\t<blockquote$cite$atts>\n";
606 $o2 = "\t\t<p$atts>";
607 $c2 = "</p>";
608 $c1 = "\n\t</blockquote>";
609 }
610 elseif ($tag == 'bc') {
611 $o1 = "<pre$atts>";
612 $o2 = "<code$atts>";
613 $c2 = "</code>";
614 $c1 = "</pre>";
615 $content = $this->shelve($this->encode_html(rtrim($content, "\n")."\n"));
616 }
617 elseif ($tag == 'notextile') {
618 $content = $this->shelve($content);
619 $o1 = $o2 = '';
620 $c1 = $c2 = '';
621 }
622 elseif ($tag == 'pre') {
623 $content = $this->shelve($this->encode_html(rtrim($content, "\n")."\n"));
624 $o1 = "<pre$atts>";
625 $o2 = $c2 = '';
626 $c1 = "</pre>";
627 }
628 else {
629 $o2 = "\t<$tag$atts>";
630 $c2 = "</$tag>";
631 }
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 }
632656
633 $content = $this->graf($content);
657 $content = $this->graf($content);
634658
635 return array($o1, $o2, $content, $c2, $c1);
636 }
659 return array($o1, $o2, $content, $c2, $c1);
660 }
637661
638662// -------------------------------------------------------------
639 function graf($text)
640 {
641 // handle normal paragraph text
642 if (!$this->lite) {
643 $text = $this->noTextile($text);
644 $text = $this->code($text);
645 }
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 }
646670
647 $text = $this->links($text);
648 if (!$this->noimage)
649 $text = $this->image($text);
671 $text = $this->getRefs($text);
672 $text = $this->links($text);
673 if (!$this->noimage)
674 $text = $this->image($text);
650675
651 if (!$this->lite) {
652 $text = $this->lists($text);
653 $text = $this->table($text);
654 }
676 if (!$this->lite) {
677 $text = $this->table($text);
678 $text = $this->lists($text);
679 }
655680
656 $text = $this->span($text);
657 $text = $this->footnoteRef($text);
658 $text = $this->glyphs($text);
659 return rtrim($text, "\n");
660 }
681 $text = $this->span($text);
682 $text = $this->footnoteRef($text);
683 $text = $this->glyphs($text);
684 return rtrim($text, "\n");
685 }
661686
662687// -------------------------------------------------------------
663 function span($text)
664 {
665 $qtags = array('\*\*','\*','\?\?','-','__','_','%','\+','~','\^');
666 $pnct = ".,\"'?!;:";
688 function span($text)
689 {
690 $qtags = array('\*\*','\*','\?\?','-','__','_','%','\+','~','\^');
691 $pnct = ".,\"'?!;:";
667692
668 foreach($qtags as $f) {
669 $text = preg_replace_callback("/
670 (?:^|(?<=[\s>$pnct])|([{[]))
671 ($f)(?!$f)
672 ({$this->c})
673 (?::(\S+))?
674 ([^\s$f]+|\S[^$f\n]*[^\s$f\n])
675 ([$pnct]*)
676 $f
677 (?:$|([\]}])|(?=[[:punct:]]{1,2}|\s))
678 /x", array(&$this, "fSpan"), $text);
679 }
680 return $text;
681 }
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 }
682707
683708// -------------------------------------------------------------
684 function fSpan($m)
685 {
686 $qtags = array(
687 '*' => 'strong',
688 '**' => 'b',
689 '??' => 'cite',
690 '_' => 'em',
691 '__' => 'i',
692 '-' => 'del',
693 '%' => 'span',
694 '+' => 'ins',
695 '~' => 'sub',
696 '^' => 'sup',
697 );
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 );
698723
699 list(,, $tag, $atts, $cite, $content, $end) = $m;
700 $tag = $qtags[$tag];
701 $atts = $this->pba($atts);
702 $atts .= ($cite != '') ? 'cite="' . $cite . '"' : '';
724 list(, $pre, $tag, $atts, $cite, $content, $end, $tail) = $m;
725 $tag = $qtags[$tag];
726 $atts = $this->pba($atts);
727 $atts .= ($cite != '') ? 'cite="' . $cite . '"' : '';
703728
704 $out = "<$tag$atts>$content$end</$tag>";
729 $out = "<$tag$atts>$content$end</$tag>";
705730
706// $this->dump($out);
731 if (($pre and !$tail) or ($tail and !$pre))
732 $out = $pre.$out.$tail;
707733
708 return $out;
734// $this->dump($out);
709735
710 }
736 return $out;
711737
738 }
739
712740// -------------------------------------------------------------
713 function links($text)
714 {
715 return preg_replace_callback('/
716 (?:^|(?<=[\s>.$pnct\(])|([{[])) # $pre
717 " # start
718 (' . $this->c . ') # $atts
719 ([^"]+) # $text
720 \s?
721 (?:\(([^)]+)\)(?="))? # $title
722 ":
723 ('.$this->urlch.'+) # $url
724 (\/)? # $slash
725 ([^\w\/;]*) # $post
726 (?:([\]}])|(?=\s|$|\)))
727 /Ux', array(&$this, "fLink"), $text);
728 }
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 }
729756
730757// -------------------------------------------------------------
731 function fLink($m)
732 {
733 list(, $pre, $atts, $text, $title, $url, $slash, $post) = $m;
758 function fLink($m)
759 {
760 list(, $pre, $atts, $text, $title, $url, $slash, $post, $tail) = $m;
734761
735 $url = $this->checkRefs($url);
762 $atts = $this->pba($atts);
763 $atts .= ($title != '') ? ' title="' . $this->encode_html($title) . '"' : '';
736764
737 $atts = $this->pba($atts);
738 $atts .= ($title != '') ? ' title="' . $this->encode_html($title) . '"' : '';
765 if (!$this->noimage)
766 $text = $this->image($text);
739767
740 if (!$this->noimage)
741 $text = $this->image($text);
768 $text = $this->span($text);
769 $text = $this->glyphs($text);
742770
743 $text = $this->span($text);
744 $text = $this->glyphs($text);
771 $url = $this->shelveURL($url.$slash);
745772
746 $url = $this->relURL($url);
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;
747777
748 $out = '<a href="' . $this->encode_html($url . $slash) . '"' . $atts . $this->rel . '>' . $text . '</a>' . $post;
778 // $this->dump($out);
779 return $this->shelve($out);
749780
750 // $this->dump($out);
751 return $this->shelve($out);
781 }
752782
753 }
783// -------------------------------------------------------------
784 function getRefs($text)
785 {
786 return preg_replace_callback("/^\[(.+)\]((?:http:\/\/|\/)\S+)(?=\s|$)/Um",
787 array(&$this, "refs"), $text);
788 }
754789
755790// -------------------------------------------------------------
756 function getRefs($text)
757 {
758 return preg_replace_callback("/(?<=^|\s)\[(.+)\]((?:http:\/\/|\/)\S+)(?=\s|$)/U",
759 array(&$this, "refs"), $text);
760 }
791 function refs($m)
792 {
793 list(, $flag, $url) = $m;
794 $this->urlrefs[$flag] = $url;
795 return '';
796 }
761797
762798// -------------------------------------------------------------
763 function refs($m)
764 {
765 list(, $flag, $url) = $m;
766 $this->urlrefs[$flag] = $url;
767 return '';
768 }
799 function shelveURL($text)
800 {
801 if (!$text) return '';
802 $ref = md5($text);
803 $this->urlshelf[$ref] = $text;
804 return 'urlref:'.$ref;
805 }
769806
770807// -------------------------------------------------------------
771 function checkRefs($text)
772 {
773 return (isset($this->urlrefs[$text])) ? $this->urlrefs[$text] : $text;
774 }
808 function retrieveURLs($text)
809 {
810 return preg_replace_callback('/urlref:(\w{32})/',
811 array(&$this, "retrieveURL"), $text);
812 }
775813
776814// -------------------------------------------------------------
777 function relURL($url)
778 {
779 $parts = parse_url($url);
780 if ((empty($parts['scheme']) or @$parts['scheme'] == 'http') and
781 empty($parts['host']) and
782 preg_match('/^\w/', @$parts['path']))
783 $url = $this->hu.$url;
784 if ($this->restricted and !empty($parts['scheme']) and
785 !in_array($parts['scheme'], $this->url_schemes))
786 return '#';
787 return $url;
788 }
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 }
789825
790826// -------------------------------------------------------------
791 function image($text)
792 {
793 return preg_replace_callback("/
794 (?:[[{])? # pre
795 \! # opening !
796 (\<|\=|\>)?? # optional alignment atts
797 ($this->c) # optional style,class atts
798 (?:\. )? # optional dot-space
799 ([^\s(!]+) # presume this is the src
800 \s? # optional space
801 (?:\(([^\)]+)\))? # optional title
802 \! # closing
803 (?::(\S+))? # optional href
804 (?:[\]}]|(?=\s|$)) # lookahead: space or end of string
805 /Ux", array(&$this, "fImage"), $text);
806 }
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 }
807839
808840// -------------------------------------------------------------
809 function fImage($m)
810 {
811 list(, $algn, $atts, $url) = $m;
812 $atts = $this->pba($atts);
813 $atts .= ($algn != '') ? ' align="' . $this->iAlign($algn) . '"' : '';
814 $atts .= (isset($m[4])) ? ' title="' . $m[4] . '"' : '';
815 $atts .= (isset($m[4])) ? ' alt="' . $m[4] . '"' : ' alt=""';
816 $size = @getimagesize($url);
817 if ($size) $atts .= " $size[3]";
841 function isRelURL($url)
842 {
843 $parts = @parse_url($url);
844 return (empty($parts['scheme']) and empty($parts['host']));
845 }
818846
819 $href = (isset($m[5])) ? $this->checkRefs($m[5]) : '';
820 $url = $this->checkRefs($url);
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 }
821864
822 $url = $this->relURL($url);
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]";
823877
824 $out = array(
825 ($href) ? '<a href="' . $href . '">' : '',
826 '<img src="' . $url . '"' . $atts . ' />',
827 ($href) ? '</a>' : ''
828 );
878 $href = (isset($m[5])) ? $this->shelveURL($m[5]) : '';
879 $url = $this->shelveURL($url);
829880
830 return join('',$out);
831 }
881 $out = array(
882 ($href) ? '<a href="' . $href . '">' : '',
883 '<img src="' . $url . '"' . $atts . ' />',
884 ($href) ? '</a>' : ''
885 );
832886
887 return $this->shelve(join('',$out));
888 }
889
833890// -------------------------------------------------------------
834 function code($text)
835 {
836 $text = $this->doSpecial($text, '<code>', '</code>', 'fCode');
837 $text = $this->doSpecial($text, '@', '@', 'fCode');
838 $text = $this->doSpecial($text, '<pre>', '</pre>', 'fPre');
839 return $text;
840 }
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 }
841898
842899// -------------------------------------------------------------
843 function fCode($m)
844 {
845 @list(, $before, $text, $after) = $m;
846 if ($this->restricted)
847 // $text is already escaped
848 return $before.$this->shelve('<code>'.$text.'</code>').$after;
849 else
850 return $before.$this->shelve('<code>'.$this->encode_html($text).'</code>').$after;
851 }
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 }
852905
853906// -------------------------------------------------------------
854 function fPre($m)
855 {
856 @list(, $before, $text, $after) = $m;
857 if ($this->restricted)
858 // $text is already escaped
859 return $before.'<pre>'.$this->shelve($text).'</pre>'.$after;
860 else
861 return $before.'<pre>'.$this->shelve($this->encode_html($text)).'</pre>'.$after;
862 }
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 }
863912// -------------------------------------------------------------
864 function shelve($val)
865 {
866 $i = uniqid(rand());
867 $this->shelf[$i] = $val;
868 return $i;
869 }
913 function shelve($val)
914 {
915 $i = uniqid(rand());
916 $this->shelf[$i] = $val;
917 return $i;
918 }
870919
871920// -------------------------------------------------------------
872 function retrieve($text)
873 {
874 if (is_array($this->shelf))
875 do {
876 $old = $text;
877 $text = strtr($text, $this->shelf);
878 } while ($text != $old);
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);
879928
880 return $text;
881 }
929 return $text;
930 }
882931
883932// -------------------------------------------------------------
884933// NOTE: deprecated
885 function incomingEntities($text)
886 {
887 return preg_replace("/&(?![#a-z0-9]+;)/i", "x%x%", $text);
888 }
934 function incomingEntities($text)
935 {
936 return preg_replace("/&(?![#a-z0-9]+;)/i", "x%x%", $text);
937 }
889938
890939// -------------------------------------------------------------
891940// NOTE: deprecated
892 function encodeEntities($text)
893 {
894 return (function_exists('mb_encode_numericentity'))
895 ? $this->encode_high($text)
896 : htmlentities($text, ENT_NOQUOTES, "utf-8");
897 }
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 }
898947
899948// -------------------------------------------------------------
900949// NOTE: deprecated
901 function fixEntities($text)
902 {
903 /* de-entify any remaining angle brackets or ampersands */
904 return str_replace(array("&gt;", "&lt;", "&amp;"),
905 array(">", "<", "&"), $text);
906 }
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 }
907956
908957// -------------------------------------------------------------
909 function cleanWhiteSpace($text)
910 {
911 $out = str_replace("\r\n", "\n", $text);
912 $out = preg_replace("/\n{3,}/", "\n\n", $out);
913 $out = preg_replace("/\n *\n/", "\n\n", $out);
914 $out = preg_replace('/"$/', "\" ", $out);
915 return $out;
916 }
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 }
917966
918967// -------------------------------------------------------------
919 function doSpecial($text, $start, $end, $method='fSpecial')
920 {
921 return preg_replace_callback('/(^|\s|[[({>])'.preg_quote($start, '/').'(.*?)'.preg_quote($end, '/').'(\s|$|[\])}])?/ms',
922 array(&$this, $method), $text);
923 }
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 }
924973
925974// -------------------------------------------------------------
926 function fSpecial($m)
927 {
928 // A special block like notextile or code
929 @list(, $before, $text, $after) = $m;
930 return $before.$this->shelve($this->encode_html($text)).$after;
931 }
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 }
932981
933982// -------------------------------------------------------------
934 function noTextile($text)
935 {
936 $text = $this->doSpecial($text, '<notextile>', '</notextile>', 'fTextile');
937 return $this->doSpecial($text, '==', '==', 'fTextile');
983 function noTextile($text)
984 {
985 $text = $this->doSpecial($text, '<notextile>', '</notextile>', 'fTextile');
986 return $this->doSpecial($text, '==', '==', 'fTextile');
938987
939 }
988 }
940989
941990// -------------------------------------------------------------
942 function fTextile($m)
943 {
944 @list(, $before, $notextile, $after) = $m;
945 #$notextile = str_replace(array_keys($modifiers), array_values($modifiers), $notextile);
946 return $before.$this->shelve($notextile).$after;
947 }
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 }
948997
949998// -------------------------------------------------------------
950 function footnoteRef($text)
951 {
952 return preg_replace('/\b\[([0-9]+)\](\s)?/Ue',
953 '$this->footnoteID(\'\1\',\'\2\')', $text);
954 }
999 function footnoteRef($text)
1000 {
1001 return preg_replace('/(?<=\S)\[([0-9]+)\](\s)?/Ue',
1002 '$this->footnoteID(\'\1\',\'\2\')', $text);
1003 }
9551004
9561005// -------------------------------------------------------------
957 function footnoteID($id, $t)
958 {
959 if (empty($this->fn[$id]))
960 $this->fn[$id] = uniqid(rand());
961 $fnid = $this->fn[$id];
962 return '<sup class="footnote"><a href="#fn'.$fnid.'">'.$id.'</a></sup>'.$t;
963 }
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 }
9641013
9651014// -------------------------------------------------------------
966 function glyphs($text)
967 {
968 // fix: hackish
969 $text = preg_replace('/"\z/', "\" ", $text);
970 $pnc = '[[:punct:]]';
1015 function glyphs($text)
1016 {
9711017
972 $glyph_search = array(
973 '/(\w)\'(\w)/', // apostrophe's
974 '/(\s)\'(\d+\w?)\b(?!\')/', // back in '88
975 '/(\S)\'(?=\s|'.$pnc.'|<|$)/', // single closing
976 '/\'/', // single opening
977 '/(\S)\"(?=\s|'.$pnc.'|<|$)/', // double closing
978 '/"/', // double opening
979 '/\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])/', // 3+ uppercase acronym
980 '/\b([A-Z][A-Z\'\-]+[A-Z])(?=[\s.,\)>])/', // 3+ uppercase
981 '/\b( )?\.{3}/', // ellipsis
982 '/(\s?)--(\s?)/', // em dash
983 '/\s-(?:\s|$)/', // en dash
984 '/(\d+)( ?)x( ?)(?=\d+)/', // dimension sign
985 '/\b ?[([]TM[])]/i', // trademark
986 '/\b ?[([]R[])]/i', // registered
987 '/\b ?[([]C[])]/i', // copyright
988 );
1018 // fix: hackish
1019 $text = preg_replace('/"\z/', "\" ", $text);
1020 $pnc = '[[:punct:]]';
9891021
990 extract($this->glyph, EXTR_PREFIX_ALL, 'txt');
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 );
9911039
992 $glyph_replace = array(
993 '$1'.$txt_apostrophe.'$2', // apostrophe's
994 '$1'.$txt_apostrophe.'$2', // back in '88
995 '$1'.$txt_quote_single_close, // single closing
996 $txt_quote_single_open, // single opening
997 '$1'.$txt_quote_double_close, // double closing
998 $txt_quote_double_open, // double opening
999 '<acronym title="$2">$1</acronym>', // 3+ uppercase acronym
1000 '<span class="caps">$1</span>', // 3+ uppercase
1001 '$1'.$txt_ellipsis, // ellipsis
1002 '$1'.$txt_emdash.'$2', // em dash
1003 ' '.$txt_endash.' ', // en dash
1004 '$1$2'.$txt_dimension.'$3', // dimension sign
1005 $txt_trademark, // trademark
1006 $txt_registered, // registered
1007 $txt_copyright, // copyright
1008 );
1040 extract($this->glyph, EXTR_PREFIX_ALL, 'txt');
10091041
1010 $text = preg_split("/(<.*>)/U", $text, -1, PREG_SPLIT_DELIM_CAPTURE);
1011 foreach($text as $line) {
1012 if (!preg_match("/<.*>/", $line)) {
1013 $line = preg_replace($glyph_search, $glyph_replace, $line);
1014 }
1015 $glyph_out[] = $line;
1016 }
1017 return join('', $glyph_out);
1018 }
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 );
10191059
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
10201077// -------------------------------------------------------------
1021 function iAlign($in)
1022 {
1023 $vals = array(
1024 '<' => 'left',
1025 '=' => 'center',
1026 '>' => 'right');
1027 return (isset($vals[$in])) ? $vals[$in] : '';
1028 }
1078 function iAlign($in)
1079 {
1080 $vals = array(
1081 '<' => 'left',
1082 '=' => 'center',
1083 '>' => 'right');
1084 return (isset($vals[$in])) ? $vals[$in] : '';
1085 }
10291086
10301087// -------------------------------------------------------------
1031 function hAlign($in)
1032 {
1033 $vals = array(
1034 '<' => 'left',
1035 '=' => 'center',
1036 '>' => 'right',
1037 '<>' => 'justify');
1038 return (isset($vals[$in])) ? $vals[$in] : '';
1039 }
1088 function hAlign($in)
1089 {
1090 $vals = array(
1091 '<' => 'left',
1092 '=' => 'center',
1093 '>' => 'right',
1094 '<>' => 'justify');
1095 return (isset($vals[$in])) ? $vals[$in] : '';
1096 }
10401097
10411098// -------------------------------------------------------------
1042 function vAlign($in)
1043 {
1044 $vals = array(
1045 '^' => 'top',
1046 '-' => 'middle',
1047 '~' => 'bottom');
1048 return (isset($vals[$in])) ? $vals[$in] : '';
1049 }
1099 function vAlign($in)
1100 {
1101 $vals = array(
1102 '^' => 'top',
1103 '-' => 'middle',
1104 '~' => 'bottom');
1105 return (isset($vals[$in])) ? $vals[$in] : '';
1106 }
10501107
10511108// -------------------------------------------------------------
10521109// NOTE: deprecated
1053 function encode_high($text, $charset = "UTF-8")
1054 {
1055 return mb_encode_numericentity($text, $this->cmap(), $charset);
1056 }
1110 function encode_high($text, $charset = "UTF-8")
1111 {
1112 return mb_encode_numericentity($text, $this->cmap(), $charset);
1113 }
10571114
10581115// -------------------------------------------------------------
10591116// NOTE: deprecated
1060 function decode_high($text, $charset = "UTF-8")
1061 {
1062 return mb_decode_numericentity($text, $this->cmap(), $charset);
1063 }
1117 function decode_high($text, $charset = "UTF-8")
1118 {
1119 return mb_decode_numericentity($text, $this->cmap(), $charset);
1120 }
10641121
10651122// -------------------------------------------------------------
10661123// NOTE: deprecated
1067 function cmap()
1068 {
1069 $f = 0xffff;
1070 $cmap = array(
1071 0x0080, 0xffff, 0, $f);
1072 return $cmap;
1073 }
1124 function cmap()
1125 {
1126 $f = 0xffff;
1127 $cmap = array(
1128 0x0080, 0xffff, 0, $f);
1129 return $cmap;
1130 }
10741131
10751132// -------------------------------------------------------------
1076 function encode_html($str, $quotes=1)
1077 {
1078 $a = array(
1079 '&' => '&#38;',
1080 '<' => '&#60;',
1081 '>' => '&#62;',
1082 );
1083 if ($quotes) $a = $a + array(
1084 "'" => '&#39;',
1085 '"' => '&#34;',
1086 );
1133 function encode_raw_amp($text)
1134 {
1135 return preg_replace('/&(?!#?[a-z0-9]+;)/i', '&#38;', $text);
1136 }
10871137
1088 return strtr($str, $a);
1089 }
1138// -------------------------------------------------------------
1139 function encode_lt_gt($text)
1140 {
1141 return strtr($text, array('<' => '&#60;', '>' => '&#62;'));
1142 }
10901143
10911144// -------------------------------------------------------------
1092 function textile_popup_help($name, $helpvar, $windowW, $windowH)
1093 {
1094 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 />';
1145 function encode_html($str, $quotes=1)
1146 {
1147 $a = array(
1148 '&' => '&#38;',
1149 '<' => '&#60;',
1150 '>' => '&#62;',
1151 );
1152 if ($quotes) $a = $a + array(
1153 "'" => '&#39;',
1154 '"' => '&#34;',
1155 );
10951156
1096 return $out;
1097 }
1157 return strtr($str, $a);
1158 }
10981159
10991160// -------------------------------------------------------------
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// -------------------------------------------------------------
11001178// NOTE: deprecated
1101 function txtgps($thing)
1102 {
1103 if (isset($_POST[$thing])) {
1104 if (get_magic_quotes_gpc()) {
1105 return stripslashes($_POST[$thing]);
1106 }
1107 else {
1108 return $_POST[$thing];
1109 }
1110 }
1111 else {
1112 return '';
1113 }
1114 }
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 }
11151193
11161194// -------------------------------------------------------------
11171195// NOTE: deprecated
1118 function dump()
1119 {
1120 foreach (func_get_args() as $a)
1121 echo "\n<pre>",(is_array($a)) ? print_r($a) : $a, "</pre>\n";
1122 }
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 }
11231201
11241202// -------------------------------------------------------------
11251203
1126 function blockLite($text)
1127 {
1128 $this->btag = array('bq', 'p');
1129 return $this->block($text."\n\n");
1130 }
1204 function blockLite($text)
1205 {
1206 $this->btag = array('bq', 'p');
1207 return $this->block($text."\n\n");
1208 }
11311209
11321210
11331211} // end class