colonymech / docs / www / colonyscout / internal / jeditable / js / Textile.php @ f59acf11
History | View | Annotate | Download (31.1 KB)
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://svn.textpattern.com/development/crockery/textpattern/lib/classTextile.php $
|
13 |
$LastChangedRevision: 13 $
|
14 |
*/
|
15 |
|
16 |
/*
|
17 |
|
18 |
_____________
|
19 |
T E X T I L E
|
20 |
|
21 |
A Humane Web Text Generator
|
22 |
|
23 |
Version 2.0 beta
|
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 |
_____________
|
32 |
L I C E N S E
|
33 |
|
34 |
Redistribution and use in source and binary forms, with or without
|
35 |
modification, are permitted provided that the following conditions are met:
|
36 |
|
37 |
* Redistributions of source code must retain the above copyright notice,
|
38 |
this list of conditions and the following disclaimer.
|
39 |
|
40 |
* Redistributions in binary form must reproduce the above copyright notice,
|
41 |
this list of conditions and the following disclaimer in the documentation
|
42 |
and/or other materials provided with the distribution.
|
43 |
|
44 |
* Neither the name Textile nor the names of its contributors may be used to
|
45 |
endorse or promote products derived from this software without specific
|
46 |
prior written permission.
|
47 |
|
48 |
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
49 |
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
50 |
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
51 |
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
52 |
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
53 |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
54 |
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
55 |
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
56 |
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
57 |
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
58 |
POSSIBILITY OF SUCH DAMAGE.
|
59 |
|
60 |
_________
|
61 |
U S A G E
|
62 |
|
63 |
Block modifier syntax:
|
64 |
|
65 |
Header: h(1-6).
|
66 |
Paragraphs beginning with 'hn. ' (where n is 1-6) are wrapped in header tags.
|
67 |
Example: h1. Header... -> <h1>Header...</h1>
|
68 |
|
69 |
Paragraph: p. (also applied by default)
|
70 |
Example: p. Text -> <p>Text</p>
|
71 |
|
72 |
Blockquote: bq.
|
73 |
Example: bq. Block quotation... -> <blockquote>Block quotation...</blockquote>
|
74 |
|
75 |
Blockquote with citation: bq.:http://citation.url
|
76 |
Example: bq.:http://textism.com/ Text...
|
77 |
-> <blockquote cite="http://textism.com">Text...</blockquote>
|
78 |
|
79 |
Footnote: fn(1-100).
|
80 |
Example: fn1. Footnote... -> <p id="fn1">Footnote...</p>
|
81 |
|
82 |
Numeric list: #, ##
|
83 |
Consecutive paragraphs beginning with # are wrapped in ordered list tags.
|
84 |
Example: <ol><li>ordered list</li></ol>
|
85 |
|
86 |
Bulleted list: *, **
|
87 |
Consecutive paragraphs beginning with * are wrapped in unordered list tags.
|
88 |
Example: <ul><li>unordered list</li></ul>
|
89 |
|
90 |
Phrase modifier syntax:
|
91 |
|
92 |
_emphasis_ -> <em>emphasis</em>
|
93 |
__italic__ -> <i>italic</i>
|
94 |
*strong* -> <strong>strong</strong>
|
95 |
**bold** -> <b>bold</b>
|
96 |
??citation?? -> <cite>citation</cite>
|
97 |
-deleted text- -> <del>deleted</del>
|
98 |
+inserted text+ -> <ins>inserted</ins>
|
99 |
^superscript^ -> <sup>superscript</sup>
|
100 |
~subscript~ -> <sub>subscript</sub>
|
101 |
@code@ -> <code>computer code</code>
|
102 |
%(bob)span% -> <span class="bob">span</span>
|
103 |
|
104 |
==notextile== -> leave text alone (do not format)
|
105 |
|
106 |
"linktext":url -> <a href="url">linktext</a>
|
107 |
"linktext(title)":url -> <a href="url" title="title">linktext</a>
|
108 |
|
109 |
!imageurl! -> <img src="imageurl" />
|
110 |
!imageurl(alt text)! -> <img src="imageurl" alt="alt text" />
|
111 |
!imageurl!:linkurl -> <a href="linkurl"><img src="imageurl" /></a>
|
112 |
|
113 |
ABC(Always Be Closing) -> <acronym title="Always Be Closing">ABC</acronym>
|
114 |
|
115 |
|
116 |
Table syntax:
|
117 |
|
118 |
Simple tables:
|
119 |
|
120 |
|a|simple|table|row|
|
121 |
|And|Another|table|row|
|
122 |
|
123 |
|_. A|_. table|_. header|_.row|
|
124 |
|A|simple|table|row|
|
125 |
|
126 |
Tables with attributes:
|
127 |
|
128 |
table{border:1px solid black}.
|
129 |
{background:#ddd;color:red}. |{}| | | |
|
130 |
|
131 |
|
132 |
Applying Attributes:
|
133 |
|
134 |
Most anywhere Textile code is used, attributes such as arbitrary css style,
|
135 |
css classes, and ids can be applied. The syntax is fairly consistent.
|
136 |
|
137 |
The following characters quickly alter the alignment of block elements:
|
138 |
|
139 |
< -> left align ex. p<. left-aligned para
|
140 |
> -> right align h3>. right-aligned header 3
|
141 |
= -> centred h4=. centred header 4
|
142 |
<> -> justified p<>. justified paragraph
|
143 |
|
144 |
These will change vertical alignment in table cells:
|
145 |
|
146 |
^ -> top ex. |^. top-aligned table cell|
|
147 |
- -> middle |-. middle aligned|
|
148 |
~ -> bottom |~. bottom aligned cell|
|
149 |
|
150 |
Plain (parentheses) inserted between block syntax and the closing dot-space
|
151 |
indicate classes and ids:
|
152 |
|
153 |
p(hector). paragraph -> <p class="hector">paragraph</p>
|
154 |
|
155 |
p(#fluid). paragraph -> <p id="fluid">paragraph</p>
|
156 |
|
157 |
(classes and ids can be combined)
|
158 |
p(hector#fluid). paragraph -> <p class="hector" id="fluid">paragraph</p>
|
159 |
|
160 |
Curly {brackets} insert arbitrary css style
|
161 |
|
162 |
p{line-height:18px}. paragraph -> <p style="line-height:18px">paragraph</p>
|
163 |
|
164 |
h3{color:red}. header 3 -> <h3 style="color:red">header 3</h3>
|
165 |
|
166 |
Square [brackets] insert language attributes
|
167 |
|
168 |
p[no]. paragraph -> <p lang="no">paragraph</p>
|
169 |
|
170 |
%[fr]phrase% -> <span lang="fr">phrase</span>
|
171 |
|
172 |
Usually Textile block element syntax requires a dot and space before the block
|
173 |
begins, but since lists don't, they can be styled just using braces
|
174 |
|
175 |
#{color:blue} one -> <ol style="color:blue">
|
176 |
# big <li>one</li>
|
177 |
# list <li>big</li>
|
178 |
<li>list</li>
|
179 |
</ol>
|
180 |
|
181 |
Using the span tag to style a phrase
|
182 |
|
183 |
It goes like this, %{color:red}the fourth the fifth%
|
184 |
-> It goes like this, <span style="color:red">the fourth the fifth</span>
|
185 |
|
186 |
*/
|
187 |
|
188 |
class Textile |
189 |
{ |
190 |
var $hlgn; |
191 |
var $vlgn; |
192 |
var $clas; |
193 |
var $lnge; |
194 |
var $styl; |
195 |
var $cspn; |
196 |
var $rspn; |
197 |
var $a; |
198 |
var $s; |
199 |
var $c; |
200 |
var $pnct; |
201 |
var $rel; |
202 |
var $fn; |
203 |
|
204 |
// -------------------------------------------------------------
|
205 |
function Textile() |
206 |
{ |
207 |
$this->hlgn = "(?:\<(?!>)|(?<!<)\>|\<\>|\=|[()]+)"; |
208 |
$this->vlgn = "[\-^~]"; |
209 |
$this->clas = "(?:\([^)]+\))"; |
210 |
$this->lnge = "(?:\[[^]]+\])"; |
211 |
$this->styl = "(?:\{[^}]+\})"; |
212 |
$this->cspn = "(?:\\\\\d+)"; |
213 |
$this->rspn = "(?:\/\d+)"; |
214 |
$this->a = "(?:{$this->hlgn}?{$this->vlgn}?|{$this->vlgn}?{$this->hlgn}?)"; |
215 |
$this->s = "(?:{$this->cspn}?{$this->rspn}?|{$this->rspn}?{$this->cspn}?)"; |
216 |
$this->c = "(?:{$this->clas}?{$this->styl}?{$this->lnge}?|{$this->styl}?{$this->lnge}?{$this->clas}?|{$this->lnge}?{$this->styl}?{$this->clas}?)"; |
217 |
$this->pnct = '[\!"#\$%&\'()\*\+,\-\./:;<=>\?@\[\\\]\^_`{\|}\~]'; |
218 |
|
219 |
} |
220 |
|
221 |
// -------------------------------------------------------------
|
222 |
function TextileThis($text, $lite='', $encode='', $noimage='', $strict='', $rel='') |
223 |
{ |
224 |
if ($rel) |
225 |
$this->rel = ' rel="'.$rel.'" '; |
226 |
|
227 |
$text = $this->incomingEntities($text); |
228 |
|
229 |
if ($encode) { |
230 |
$text = str_replace("x%x%", "&", $text); |
231 |
return $text; |
232 |
} else {
|
233 |
|
234 |
if(!$strict) { |
235 |
$text = $this->fixEntities($text); |
236 |
$text = $this->cleanWhiteSpace($text); |
237 |
} |
238 |
|
239 |
$text = $this->getRefs($text); |
240 |
|
241 |
$text = $this->noTextile($text); |
242 |
$text = $this->links($text); |
243 |
if (!$noimage) { |
244 |
$text = $this->image($text); |
245 |
} |
246 |
$text = $this->code($text); |
247 |
$text = $this->span($text); |
248 |
$text = $this->superscript($text); |
249 |
$text = $this->footnoteRef($text); |
250 |
$text = $this->glyphs($text); |
251 |
$text = $this->retrieve($text); |
252 |
|
253 |
if (!$lite) { |
254 |
$text = $this->lists($text); |
255 |
$text = $this->table($text); |
256 |
$text = $this->block($text); |
257 |
} |
258 |
|
259 |
// clean up <notextile>
|
260 |
$text = preg_replace('/<\/?notextile>/', "", $text); |
261 |
|
262 |
// turn the temp char back to an ampersand entity
|
263 |
$text = str_replace("x%x%", "&", $text); |
264 |
|
265 |
// just to be tidy
|
266 |
$text = str_replace("<br />", "<br />\n", $text); |
267 |
|
268 |
return $text; |
269 |
} |
270 |
} |
271 |
|
272 |
// -------------------------------------------------------------
|
273 |
function pba($in, $element = "") // "parse block attributes" |
274 |
{ |
275 |
$style = ''; |
276 |
$class = ''; |
277 |
$lang = ''; |
278 |
$colspan = ''; |
279 |
$rowspan = ''; |
280 |
$id = ''; |
281 |
$atts = ''; |
282 |
|
283 |
if (!empty($in)) { |
284 |
$matched = $in; |
285 |
if ($element == 'td') { |
286 |
if (preg_match("/\\\\(\d+)/", $matched, $csp)) $colspan = $csp[1]; |
287 |
if (preg_match("/\/(\d+)/", $matched, $rsp)) $rowspan = $rsp[1]; |
288 |
|
289 |
if (preg_match("/($this->vlgn)/", $matched, $vert)) |
290 |
$style[] = "vertical-align:" . $this->vAlign($vert[1]) . ";"; |
291 |
} |
292 |
|
293 |
if (preg_match("/\{([^}]*)\}/", $matched, $sty)) { |
294 |
$style[] = $sty[1] . ';'; |
295 |
$matched = str_replace($sty[0], '', $matched); |
296 |
} |
297 |
|
298 |
if (preg_match("/\[([^)]+)\]/U", $matched, $lng)) { |
299 |
$lang = $lng[1]; |
300 |
$matched = str_replace($lng[0], '', $matched); |
301 |
} |
302 |
|
303 |
if (preg_match("/\(([^()]+)\)/U", $matched, $cls)) { |
304 |
$class = $cls[1]; |
305 |
$matched = str_replace($cls[0], '', $matched); |
306 |
} |
307 |
|
308 |
if (preg_match("/([(]+)/", $matched, $pl)) { |
309 |
$style[] = "padding-left:" . strlen($pl[1]) . "em;"; |
310 |
$matched = str_replace($pl[0], '', $matched); |
311 |
} |
312 |
|
313 |
if (preg_match("/([)]+)/", $matched, $pr)) { |
314 |
// $this->dump($pr);
|
315 |
$style[] = "padding-right:" . strlen($pr[1]) . "em;"; |
316 |
$matched = str_replace($pr[0], '', $matched); |
317 |
} |
318 |
|
319 |
if (preg_match("/($this->hlgn)/", $matched, $horiz)) |
320 |
$style[] = "text-align:" . $this->hAlign($horiz[1]) . ";"; |
321 |
|
322 |
if (preg_match("/^(.*)#(.*)$/", $class, $ids)) { |
323 |
$id = $ids[2]; |
324 |
$class = $ids[1]; |
325 |
} |
326 |
|
327 |
return join('',array( |
328 |
($style) ? ' style="' . join("", $style) .'"':'', |
329 |
($class) ? ' class="' . $class .'"':'', |
330 |
($lang) ? ' lang="' . $lang .'"':'', |
331 |
($id) ? ' id="' . $id .'"':'', |
332 |
($colspan) ? ' colspan="' . $colspan .'"':'', |
333 |
($rowspan) ? ' rowspan="' . $rowspan .'"':'' |
334 |
)); |
335 |
} |
336 |
return ''; |
337 |
} |
338 |
|
339 |
// -------------------------------------------------------------
|
340 |
function table($text) |
341 |
{ |
342 |
$text = $text . "\n\n"; |
343 |
return preg_replace_callback("/^(?:table(_?{$this->s}{$this->a}{$this->c})\. ?\n)?^({$this->a}{$this->c}\.? ?\|.*\|)\n\n/smU", |
344 |
array(&$this, "fTable"), $text); |
345 |
} |
346 |
|
347 |
// -------------------------------------------------------------
|
348 |
function fTable($matches) |
349 |
{ |
350 |
$tatts = $this->pba($matches[1], 'table'); |
351 |
|
352 |
foreach(preg_split("/\|$/m", $matches[2], -1, PREG_SPLIT_NO_EMPTY) as $row) { |
353 |
if (preg_match("/^($this->a$this->c\. )(.*)/m", $row, $rmtch)) { |
354 |
$ratts = $this->pba($rmtch[1], 'tr'); |
355 |
$row = $rmtch[2]; |
356 |
} else $ratts = ''; |
357 |
|
358 |
foreach(explode("|", $row) as $cell) { |
359 |
$ctyp = "d"; |
360 |
if (preg_match("/^_/", $cell)) $ctyp = "h"; |
361 |
if (preg_match("/^(_?$this->s$this->a$this->c\. )(.*)/", $cell, $cmtch)) { |
362 |
$catts = $this->pba($cmtch[1], 'td'); |
363 |
$cell = $cmtch[2]; |
364 |
} else $catts = ''; |
365 |
|
366 |
if (trim($cell) != '') |
367 |
$cells[] = "\t\t\t<t$ctyp$catts>$cell</t$ctyp>"; |
368 |
} |
369 |
$rows[] = "\t\t<tr$ratts>\n" . join("\n", $cells) . "\n\t\t</tr>"; |
370 |
unset($cells, $catts); |
371 |
} |
372 |
return "\t<table$tatts>\n" . join("\n", $rows) . "\n\t</table>\n\n"; |
373 |
} |
374 |
|
375 |
// -------------------------------------------------------------
|
376 |
function lists($text) |
377 |
{ |
378 |
return preg_replace_callback("/^([#*]+$this->c .*)$(?![^#*])/smU", array(&$this, "fList"), $text); |
379 |
} |
380 |
|
381 |
// -------------------------------------------------------------
|
382 |
function fList($m) |
383 |
{ |
384 |
$text = explode("\n", $m[0]); |
385 |
foreach($text as $line) { |
386 |
$nextline = next($text); |
387 |
if (preg_match("/^([#*]+)($this->a$this->c) (.*)$/s", $line, $m)) { |
388 |
list(, $tl, $atts, $content) = $m; |
389 |
$nl = preg_replace("/^([#*]+)\s.*/", "$1", $nextline); |
390 |
if (!isset($lists[$tl])) { |
391 |
$lists[$tl] = true; |
392 |
$atts = $this->pba($atts); |
393 |
$line = "\t<" . $this->lT($tl) . "l$atts>\n\t\t<li>" . $content; |
394 |
} else {
|
395 |
$line = "\t\t<li>" . $content; |
396 |
} |
397 |
|
398 |
if(strlen($nl) <= strlen($tl)) $line .= "</li>"; |
399 |
foreach(array_reverse($lists) as $k => $v) { |
400 |
if(strlen($k) > strlen($nl)) { |
401 |
$line .= "\n\t</" . $this->lT($k) . "l>"; |
402 |
if(strlen($k) > 1) |
403 |
$line .= "</li>"; |
404 |
unset($lists[$k]); |
405 |
} |
406 |
} |
407 |
} |
408 |
$out[] = $line; |
409 |
} |
410 |
return join("\n", $out); |
411 |
} |
412 |
|
413 |
// -------------------------------------------------------------
|
414 |
function lT($in) |
415 |
{ |
416 |
return preg_match("/^#+/", $in) ? 'o' : 'u'; |
417 |
} |
418 |
|
419 |
// -------------------------------------------------------------
|
420 |
function block($text) |
421 |
{ |
422 |
$pre = $php = $txp = false; |
423 |
$find = array('bq', 'h[1-6]', 'fn\d+', 'p'); |
424 |
|
425 |
$text = preg_replace("/(.+)\n(?![#*\s|])/", |
426 |
"$1<br />", $text); |
427 |
|
428 |
$text = explode("\n", $text); |
429 |
array_push($text, " "); |
430 |
|
431 |
foreach($text as $line) { |
432 |
if (preg_match('/<pre>/i', $line)) { |
433 |
$pre = true; |
434 |
} |
435 |
elseif (preg_match('/<txp:php>/i', $line)) { |
436 |
$php = true; |
437 |
} |
438 |
elseif (preg_match('/^\s*<txp:/i', $line)) { |
439 |
$txp = true; |
440 |
} |
441 |
|
442 |
|
443 |
foreach($find as $tag) { |
444 |
$line = ($pre == false and $php == false and $txp == false) |
445 |
? preg_replace_callback("/^($tag)($this->a$this->c)\.(?::(\S+))? (.*)$/", |
446 |
array(&$this, "fBlock"), $line) |
447 |
: $line;
|
448 |
} |
449 |
|
450 |
$line = (!$php and !$txp) ? preg_replace('/^(?!\t|<\/?pre|<\/?code|$| )(.*)/', "\t<p>$1</p>", $line) : $line; |
451 |
|
452 |
$line = ($pre or $php) ? str_replace("<br />", "\n", $line):$line; |
453 |
if (preg_match('/<\/pre>/i', $line)) { |
454 |
$pre = false; |
455 |
} |
456 |
elseif (preg_match('/<\/txp:php>/i', $line)) { |
457 |
$php = false; |
458 |
} |
459 |
if ($txp == true) $txp = false; |
460 |
$out[] = $line; |
461 |
} |
462 |
return join("\n", $out); |
463 |
} |
464 |
|
465 |
// -------------------------------------------------------------
|
466 |
function fBlock($m) |
467 |
{ |
468 |
// $this->dump($m);
|
469 |
list(, $tag, $atts, $cite, $content) = $m; |
470 |
|
471 |
$atts = $this->pba($atts); |
472 |
|
473 |
if (preg_match("/fn(\d+)/", $tag, $fns)) { |
474 |
$tag = 'p'; |
475 |
$fnid = empty($this->fn[$fns[1]]) ? $fns[1] : $this->fn[$fns[1]]; |
476 |
$atts .= ' id="fn' . $fnid . '"'; |
477 |
$content = '<sup>' . $fns[1] . '</sup> ' . $content; |
478 |
} |
479 |
|
480 |
$start = "\t<$tag"; |
481 |
$end = "</$tag>"; |
482 |
|
483 |
if ($tag == "bq") { |
484 |
$cite = $this->checkRefs($cite); |
485 |
$cite = ($cite != '') ? ' cite="' . $cite . '"' : ''; |
486 |
$start = "\t<blockquote$cite>\n\t\t<p"; |
487 |
$end = "</p>\n\t</blockquote>"; |
488 |
} |
489 |
|
490 |
return "$start$atts>$content$end"; |
491 |
} |
492 |
|
493 |
// -------------------------------------------------------------
|
494 |
function span($text) |
495 |
{ |
496 |
$qtags = array('\*\*','\*','\?\?','-','__','_','%','\+','~'); |
497 |
|
498 |
foreach($qtags as $f) { |
499 |
$text = preg_replace_callback("/ |
500 |
(?<=^|\s|[[:punct:]]|[{([])
|
501 |
($f)
|
502 |
($this->c)
|
503 |
(?::(\S+))?
|
504 |
([\w<&].*)
|
505 |
([[:punct:];]*)
|
506 |
$f
|
507 |
(?=[])}]|[[:punct:]]+|\s|$)
|
508 |
/xmU", array(&$this, "fSpan"), $text); |
509 |
} |
510 |
return $text; |
511 |
} |
512 |
|
513 |
// -------------------------------------------------------------
|
514 |
function fSpan($m) |
515 |
{ |
516 |
$qtags = array( |
517 |
'*' => 'strong', |
518 |
'**' => 'b', |
519 |
'??' => 'cite', |
520 |
'_' => 'em', |
521 |
'__' => 'i', |
522 |
'-' => 'del', |
523 |
'%' => 'span', |
524 |
'+' => 'ins', |
525 |
'~' => 'sub' |
526 |
); |
527 |
|
528 |
list(, $tag, $atts, $cite, $content, $end) = $m; |
529 |
$tag = $qtags[$tag]; |
530 |
$atts = $this->pba($atts); |
531 |
$atts .= ($cite != '') ? 'cite="' . $cite . '"' : ''; |
532 |
|
533 |
$out = "<$tag$atts>$content$end</$tag>"; |
534 |
|
535 |
// $this->dump($out);
|
536 |
|
537 |
return $out; |
538 |
|
539 |
} |
540 |
|
541 |
// -------------------------------------------------------------
|
542 |
function links($text) |
543 |
{ |
544 |
return preg_replace_callback('/ |
545 |
([\s[{(]|[[:punct:]])? # $pre
|
546 |
" # start
|
547 |
(' . $this->c . ') # $atts |
548 |
([^"]+) # $text
|
549 |
\s?
|
550 |
(?:\(([^)]+)\)(?="))? # $title
|
551 |
":
|
552 |
(\S+\b) # $url
|
553 |
(\/)? # $slash
|
554 |
([^\w\/;]*) # $post
|
555 |
(?=\s|$)
|
556 |
/Ux', array(&$this, "fLink"), $text); |
557 |
} |
558 |
|
559 |
// -------------------------------------------------------------
|
560 |
function fLink($m) |
561 |
{ |
562 |
list(, $pre, $atts, $text, $title, $url, $slash, $post) = $m; |
563 |
|
564 |
$url = $this->checkRefs($url); |
565 |
|
566 |
$atts = $this->pba($atts); |
567 |
$atts .= ($title != '') ? 'title="' . $title . '"' : ''; |
568 |
|
569 |
$atts = ($atts) ? $this->shelve($atts) : ''; |
570 |
|
571 |
$parts = parse_url($url); |
572 |
if (empty($parts['host']) and preg_match('/^\w/', @$parts['path'])) |
573 |
$url = hu.$url; |
574 |
|
575 |
$out = $pre . '<a href="' . $url . $slash . '"' . $atts . $this->rel . '>' . $text . '</a>' . $post; |
576 |
|
577 |
// $this->dump($out);
|
578 |
return $out; |
579 |
|
580 |
} |
581 |
|
582 |
// -------------------------------------------------------------
|
583 |
function getRefs($text) |
584 |
{ |
585 |
return preg_replace_callback("/(?<=^|\s)\[(.+)\]((?:http:\/\/|\/)\S+)(?=\s|$)/U", |
586 |
array(&$this, "refs"), $text); |
587 |
} |
588 |
// -------------------------------------------------------------
|
589 |
|
590 |
function refs($m) |
591 |
{ |
592 |
list(, $flag, $url) = $m; |
593 |
$this->urlrefs[$flag] = $url; |
594 |
return ''; |
595 |
} |
596 |
|
597 |
// -------------------------------------------------------------
|
598 |
function checkRefs($text) |
599 |
{ |
600 |
return (isset($this->urlrefs[$text])) ? $this->urlrefs[$text] : $text; |
601 |
} |
602 |
|
603 |
// -------------------------------------------------------------
|
604 |
function image($text) |
605 |
{ |
606 |
return preg_replace_callback("/ |
607 |
\! # opening !
|
608 |
(\<|\=|\>)? # optional alignment atts
|
609 |
($this->c) # optional style,class atts
|
610 |
(?:\. )? # optional dot-space
|
611 |
([^\s(!]+) # presume this is the src
|
612 |
\s? # optional space
|
613 |
(?:\(([^\)]+)\))? # optional title
|
614 |
\! # closing
|
615 |
(?::(\S+))? # optional href
|
616 |
(?=\s|$) # lookahead: space or end of string
|
617 |
/Ux", array(&$this, "fImage"), $text); |
618 |
} |
619 |
|
620 |
// -------------------------------------------------------------
|
621 |
function fImage($m) |
622 |
{ |
623 |
list(, $algn, $atts, $url) = $m; |
624 |
$atts = $this->pba($atts); |
625 |
$atts .= ($algn != '') ? ' align="' . $this->iAlign($algn) . '"' : ''; |
626 |
$atts .= (isset($m[4])) ? ' title="' . $m[4] . '"' : ''; |
627 |
$atts .= (isset($m[4])) ? ' alt="' . $m[4] . '"' : ' alt=""'; |
628 |
$size = @getimagesize($url); |
629 |
if ($size) $atts .= " $size[3]"; |
630 |
|
631 |
$href = (isset($m[5])) ? $this->checkRefs($m[5]) : ''; |
632 |
$url = $this->checkRefs($url); |
633 |
|
634 |
$parts = parse_url($url); |
635 |
if (empty($parts['host']) and preg_match('/^\w/', @$parts['path'])) |
636 |
$url = hu.$url; |
637 |
|
638 |
$out = array( |
639 |
($href) ? '<a href="' . $href . '">' : '', |
640 |
'<img src="' . $url . '"' . $atts . ' />', |
641 |
($href) ? '</a>' : '' |
642 |
); |
643 |
|
644 |
return join('',$out); |
645 |
} |
646 |
|
647 |
// -------------------------------------------------------------
|
648 |
function code($text) |
649 |
{ |
650 |
return preg_replace_callback("/ |
651 |
(?:^|(?<=[\s\(])|([[{])) # before
|
652 |
@
|
653 |
(?:\|(\w+)\|)? # lang
|
654 |
(.+) # code
|
655 |
@
|
656 |
(?:$|([\]}])|
|
657 |
(?=[[:punct:]]{1,2}|
|
658 |
\s|$)) # after
|
659 |
/Ux", array(&$this, "fCode"), $text); |
660 |
} |
661 |
|
662 |
// -------------------------------------------------------------
|
663 |
function fCode($m) |
664 |
{ |
665 |
@list(, $before, $lang, $code, $after) = $m; |
666 |
$lang = ($lang) ? ' language="' . $lang . '"' : ''; |
667 |
return $before . '<code' . $lang . '>' . $code . '</code>' . $after; |
668 |
} |
669 |
|
670 |
// -------------------------------------------------------------
|
671 |
function shelve($val) |
672 |
{ |
673 |
$this->shelf[] = $val; |
674 |
return ' <' . count($this->shelf) . '>'; |
675 |
} |
676 |
|
677 |
// -------------------------------------------------------------
|
678 |
function retrieve($text) |
679 |
{ |
680 |
$i = 0; |
681 |
if (isset($this->shelf) && is_array($this->shelf)) { |
682 |
foreach($this->shelf as $r) { |
683 |
$i++;
|
684 |
$text = str_replace("<$i>", $r, $text); |
685 |
} |
686 |
} |
687 |
return $text; |
688 |
} |
689 |
|
690 |
// -------------------------------------------------------------
|
691 |
function incomingEntities($text) |
692 |
{ |
693 |
return preg_replace("/&(?![#a-z0-9]+;)/i", "x%x%", $text); |
694 |
} |
695 |
|
696 |
// -------------------------------------------------------------
|
697 |
function encodeEntities($text) |
698 |
{ |
699 |
return (function_exists('mb_encode_numericentity')) |
700 |
? $this->encode_high($text) |
701 |
: htmlentities($text, ENT_NOQUOTES, "utf-8"); |
702 |
} |
703 |
|
704 |
// -------------------------------------------------------------
|
705 |
function fixEntities($text) |
706 |
{ |
707 |
/* de-entify any remaining angle brackets or ampersands */
|
708 |
return str_replace(array(">", "<", "&"), |
709 |
array(">", "<", "&"), $text); |
710 |
} |
711 |
|
712 |
// -------------------------------------------------------------
|
713 |
function cleanWhiteSpace($text) |
714 |
{ |
715 |
$out = str_replace(array("\r\n", "\t"), array("\n", ''), $text); |
716 |
$out = preg_replace("/\n{3,}/", "\n\n", $out); |
717 |
$out = preg_replace("/\n *\n/", "\n\n", $out); |
718 |
$out = preg_replace('/"$/', "\" ", $out); |
719 |
return $out; |
720 |
} |
721 |
|
722 |
// -------------------------------------------------------------
|
723 |
function noTextile($text) |
724 |
{ |
725 |
$text = preg_replace_callback('/(^|\s)<notextile>(.*)<\/notextile>(\s|$)?/msU', |
726 |
array(&$this, "fTextile"), $text); |
727 |
return preg_replace_callback('/(^|\s)==(.*)==(\s|$)?/msU', |
728 |
array(&$this, "fTextile"), $text); |
729 |
} |
730 |
|
731 |
// -------------------------------------------------------------
|
732 |
function fTextile($m) |
733 |
{ |
734 |
$modifiers = array( |
735 |
'"' => '"', |
736 |
'%' => '%', |
737 |
'*' => '*', |
738 |
'+' => '+', |
739 |
'-' => '-', |
740 |
'<' => '<', |
741 |
'=' => '=', |
742 |
'>' => '>', |
743 |
'?' => '?', |
744 |
'^' => '^', |
745 |
'_' => '_', |
746 |
'~' => '~', |
747 |
); |
748 |
|
749 |
@list(, $before, $notextile, $after) = $m; |
750 |
$notextile = str_replace(array_keys($modifiers), array_values($modifiers), $notextile); |
751 |
return $before . '<notextile>' . $notextile . '</notextile>' . $after; |
752 |
} |
753 |
|
754 |
// -------------------------------------------------------------
|
755 |
function superscript($text) |
756 |
{ |
757 |
return preg_replace('/\^(.*)\^/mU', '<sup>$1</sup>', $text); |
758 |
} |
759 |
|
760 |
// -------------------------------------------------------------
|
761 |
function footnoteRef($text) |
762 |
{ |
763 |
return preg_replace('/\b\[([0-9]+)\](\s)?/Ue', |
764 |
'$this->footnoteID(\'\1\',\'\2\')', $text); |
765 |
} |
766 |
|
767 |
// -------------------------------------------------------------
|
768 |
function footnoteID($id, $t) |
769 |
{ |
770 |
if (empty($this->fn[$id])) |
771 |
$this->fn[$id] = uniqid(rand()); |
772 |
$fnid = $this->fn[$id]; |
773 |
return '<sup><a href="#fn'.$fnid.'">'.$id.'</a></sup>'.$t; |
774 |
} |
775 |
|
776 |
// -------------------------------------------------------------
|
777 |
function glyphs($text) |
778 |
{ |
779 |
// fix: hackish
|
780 |
$text = preg_replace('/"\z/', "\" ", $text); |
781 |
$pnc = '[[:punct:]]'; |
782 |
|
783 |
$glyph_search = array( |
784 |
'/([^\s[{(>_*])?\'(?(1)|(?=\s|s\b|'.$pnc.'))/', // single closing |
785 |
'/\'/', // single opening |
786 |
'/([^\s[{(>_*])?"(?(1)|(?=\s|'.$pnc.'))/', // double closing |
787 |
'/"/', // double opening |
788 |
'/\b( )?\.{3}/', // ellipsis |
789 |
'/\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])/', // 3+ uppercase acronym |
790 |
'/\s?--\s?/', // em dash |
791 |
'/\s-\s/', // en dash |
792 |
'/(\d+) ?x ?(\d+)/', // dimension sign |
793 |
'/\b ?[([]TM[])]/i', // trademark |
794 |
'/\b ?[([]R[])]/i', // registered |
795 |
'/\b ?[([]C[])]/i'); // copyright |
796 |
|
797 |
$glyph_replace = array('$1’$2', // single closing |
798 |
'‘', // single opening |
799 |
'$1”', // double closing |
800 |
'“', // double opening |
801 |
'$1…', // ellipsis |
802 |
'<acronym title="$2">$1</acronym>', // 3+ uppercase acronym |
803 |
'—', // em dash |
804 |
' – ', // en dash |
805 |
'$1×$2', // dimension sign |
806 |
'™', // trademark |
807 |
'®', // registered |
808 |
'©'); // copyright |
809 |
|
810 |
$codepre = false; |
811 |
/* if no html, do a simple search and replace... */
|
812 |
if (!preg_match("/<.*>/", $text)) { |
813 |
$text = preg_replace($glyph_search, $glyph_replace, $text); |
814 |
return $text; |
815 |
} |
816 |
else {
|
817 |
$text = preg_split("/(<.*>)/U", $text, -1, PREG_SPLIT_DELIM_CAPTURE); |
818 |
foreach($text as $line) { |
819 |
$offtags = ('code|pre|kbd|notextile|txp:php'); |
820 |
|
821 |
/* matches are off if we're between <code>, <pre> etc. */
|
822 |
if (preg_match('/<(' . $offtags . ')>/i', $line)) $codepre = true; |
823 |
if (preg_match('/<\/(' . $offtags . ')>/i', $line)) $codepre = false; |
824 |
|
825 |
if (!preg_match("/<.*>/", $line) && $codepre == false) { |
826 |
$line = preg_replace($glyph_search, $glyph_replace, $line); |
827 |
} |
828 |
|
829 |
/* do htmlspecial if between <code> */
|
830 |
if ($codepre == true) { |
831 |
$line = htmlspecialchars($line, ENT_NOQUOTES, "UTF-8"); |
832 |
$line = preg_replace('/<(\/?' . $offtags . ')>/', "<$1>", $line); |
833 |
$line = str_replace("&#","&#",$line); |
834 |
} |
835 |
|
836 |
$glyph_out[] = $line; |
837 |
} |
838 |
return join('', $glyph_out); |
839 |
} |
840 |
} |
841 |
|
842 |
// -------------------------------------------------------------
|
843 |
function iAlign($in) |
844 |
{ |
845 |
$vals = array( |
846 |
'<' => 'left', |
847 |
'=' => 'center', |
848 |
'>' => 'right'); |
849 |
return (isset($vals[$in])) ? $vals[$in] : ''; |
850 |
} |
851 |
|
852 |
// -------------------------------------------------------------
|
853 |
function hAlign($in) |
854 |
{ |
855 |
$vals = array( |
856 |
'<' => 'left', |
857 |
'=' => 'center', |
858 |
'>' => 'right', |
859 |
'<>' => 'justify'); |
860 |
return (isset($vals[$in])) ? $vals[$in] : ''; |
861 |
} |
862 |
|
863 |
// -------------------------------------------------------------
|
864 |
function vAlign($in) |
865 |
{ |
866 |
$vals = array( |
867 |
'^' => 'top', |
868 |
'-' => 'middle', |
869 |
'~' => 'bottom'); |
870 |
return (isset($vals[$in])) ? $vals[$in] : ''; |
871 |
} |
872 |
|
873 |
// -------------------------------------------------------------
|
874 |
function encode_high($text, $charset = "UTF-8") |
875 |
{ |
876 |
return mb_encode_numericentity($text, $this->cmap(), $charset); |
877 |
} |
878 |
|
879 |
// -------------------------------------------------------------
|
880 |
function decode_high($text, $charset = "UTF-8") |
881 |
{ |
882 |
return mb_decode_numericentity($text, $this->cmap(), $charset); |
883 |
} |
884 |
|
885 |
// -------------------------------------------------------------
|
886 |
function cmap() |
887 |
{ |
888 |
$f = 0xffff; |
889 |
$cmap = array( |
890 |
0x0080, 0xffff, 0, $f); |
891 |
return $cmap; |
892 |
} |
893 |
|
894 |
// -------------------------------------------------------------
|
895 |
function textile_popup_help($name, $helpvar, $windowW, $windowH) |
896 |
{ |
897 |
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 />'; |
898 |
|
899 |
return $out; |
900 |
} |
901 |
|
902 |
// -------------------------------------------------------------
|
903 |
function txtgps($thing) |
904 |
{ |
905 |
if (isset($_POST[$thing])) { |
906 |
if (get_magic_quotes_gpc()) { |
907 |
return stripslashes($_POST[$thing]); |
908 |
} |
909 |
else {
|
910 |
return $_POST[$thing]; |
911 |
} |
912 |
} |
913 |
else {
|
914 |
return ''; |
915 |
} |
916 |
} |
917 |
|
918 |
// -------------------------------------------------------------
|
919 |
function dump() |
920 |
{ |
921 |
foreach (func_get_args() as $a) |
922 |
echo "\n<pre>",(is_array($a)) ? print_r($a) : $a, "</pre>\n"; |
923 |
} |
924 |
|
925 |
// -------------------------------------------------------------
|
926 |
function blockLite($text) |
927 |
{ |
928 |
$find = array('bq', 'p'); |
929 |
|
930 |
$text = preg_replace("/(.+)\n(?![#*\s|])/", |
931 |
"$1<br />", $text); |
932 |
|
933 |
$text = explode("\n", $text); |
934 |
array_push($text, " "); |
935 |
|
936 |
foreach($text as $line) { |
937 |
|
938 |
foreach($find as $tag) { |
939 |
$line = preg_replace_callback("/^($tag)($this->a$this->c)\.(?::(\S+))? (.*)$/", |
940 |
array(&$this, "fBlock"), $line); |
941 |
} |
942 |
|
943 |
$line = preg_replace('/^(?!\t|<\/?pre|<\/?code|$| )(.*)/', "\t<p>$1</p>", $line); |
944 |
$out[] = $line; |
945 |
} |
946 |
return join("\n", $out); |
947 |
} |
948 |
|
949 |
|
950 |
} // end class
|
951 |
|
952 |
?>
|