Project

General

Profile

Statistics
| Branch: | Revision:

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%", "&#38;", $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%", "&#38;", $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("&gt;", "&lt;", "&amp;"),
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
            '"' => '&#34;',
736
            '%' => '&#37;',
737
            '*' => '&#42;',
738
            '+' => '&#43;',
739
            '-' => '&#45;',
740
            '<' => '&#60;',
741
            '=' => '&#61;',
742
            '>' => '&#62;',
743
            '?' => '&#63;',     
744
            '^' => '&#94;',
745
            '_' => '&#95;',
746
            '~' => '&#126;',        
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&#8217;$2',   //  single closing
798
            '&#8216;',                          //  single opening
799
            '$1&#8221;',                        //  double closing
800
            '&#8220;',                          //  double opening
801
            '$1&#8230;',                        //  ellipsis
802
            '<acronym title="$2">$1</acronym>', //  3+ uppercase acronym
803
            '&#8212;',                          //  em dash
804
            ' &#8211; ',                        //  en dash
805
            '$1&#215;$2',                       //  dimension sign
806
            '&#8482;',                          //  trademark
807
            '&#174;',                           //  registered
808
            '&#169;');                          //  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('/&lt;(\/?' . $offtags . ')&gt;/', "<$1>", $line);
833
                    $line = str_replace("&amp;#","&#",$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
?>