Subversion Repositories Applications.papyrus

Rev

Rev 1371 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
248 jpm 1
<?php
2
 
3
/**
4
* The baseline abstract parser class.
5
*/
6
 
401 jpm 7
require_once 'Wiki/Parse.php';
248 jpm 8
 
9
/**
10
* The baseline abstract render class.
11
*/
12
 
401 jpm 13
require_once 'Wiki/Render.php';
248 jpm 14
 
15
/**
16
*
17
* Parse structured wiki text and render into arbitrary formats such as XHTML.
18
*
19
* This is the "master" class for handling the management and convenience
20
* functions to transform Wiki-formatted text.
21
*
401 jpm 22
* $Id: Wiki.php,v 1.2 2005-06-24 10:47:09 jpm Exp $
248 jpm 23
*
24
* @author Paul M. Jones <pmjones@ciaweb.net>
25
*
26
* @package Text_Wiki
27
*
28
* @version 0.23.1
29
*
30
* @license LGPL
31
*
32
*/
33
 
34
class Text_Wiki {
35
 
36
    /**
37
    *
38
    * The default list of rules, in order, to apply to the source text.
39
    *
40
    * @access public
41
    *
42
    * @var array
43
    *
44
    */
45
 
46
    var $rules = array(
47
        'Prefilter',
48
        'Delimiter',
49
        'Code',
50
        'Function',
51
        'Html',
52
        'Raw',
53
        'Include',
54
        'Embed',
55
        'Anchor',
56
        'Heading',
57
        'Toc',
58
        'Horiz',
59
        'Break',
60
        'Blockquote',
61
        'List',
62
        'Deflist',
63
        'Table',
64
        'Image',
65
        'Phplookup',
66
        'Center',
67
        'Newline',
68
        'Paragraph',
69
        'Url',
70
        'Freelink',
71
        'Interwiki',
72
        'Wikilink',
73
        'Colortext',
74
        'Strong',
75
        'Bold',
76
        'Emphasis',
77
        'Italic',
78
        'Tt',
79
        'Superscript',
80
        'Revise',
81
        'Tighten'
82
    );
83
 
84
 
85
    /**
86
    *
87
    * The list of rules to not-apply to the source text.
88
    *
89
    * @access public
90
    *
91
    * @var array
92
    *
93
    */
94
 
95
    var $disable = array(
96
        'Html',
97
        'Include',
98
        'Embed'
99
    );
100
 
101
 
102
    /**
103
    *
104
    * Custom configuration for rules at the parsing stage.
105
    *
106
    * In this array, the key is the parsing rule name, and the value is
107
    * an array of key-value configuration pairs corresponding to the $conf
108
    * property in the target parsing rule.
109
    *
110
    * For example:
111
    *
112
    * <code>
113
    * $parseConf = array(
114
    *     'Include' => array(
115
    *         'base' => '/path/to/scripts/'
116
    *     )
117
    * );
118
    * </code>
119
    *
120
    * Note that most default rules do not need any parsing configuration.
121
    *
122
    * @access public
123
    *
124
    * @var array
125
    *
126
    */
127
 
128
    var $parseConf = array();
129
 
130
 
131
    /**
132
    *
133
    * Custom configuration for rules at the rendering stage.
134
    *
135
    * Because rendering may be different for each target format, the
136
    * first-level element in this array is always a format name (e.g.,
137
    * 'Xhtml').
138
    *
139
    * Within that first level element, the subsequent elements match the
140
    * $parseConf format. That is, the sub-key is the rendering rule name,
141
    * and the sub-value is an array of key-value configuration pairs
142
    * corresponding to the $conf property in the target rendering rule.
143
    *
144
    * @access public
145
    *
146
    * @var array
147
    *
148
    */
149
 
150
    var $renderConf = array(
151
        'Docbook' => array(),
152
        'Latex' => array(),
153
        'Pdf' => array(),
154
        'Plain' => array(),
155
        'Rtf' => array(),
156
        'Xhtml' => array()
157
    );
158
 
159
 
160
    /**
161
    *
162
    * Custom configuration for the output format itself.
163
    *
164
    * Even though Text_Wiki will render the tokens from parsed text,
165
    * the format itself may require some configuration.  For example,
166
    * RTF needs to know font names and sizes, PDF requires page layout
167
    * information, and DocBook needs a section hierarchy.  This array
168
    * matches the $conf property of the the format-level renderer
169
    * (e.g., Text_Wiki_Render_Xhtml).
170
    *
171
    * In this array, the key is the rendering format name, and the value is
172
    * an array of key-value configuration pairs corresponding to the $conf
173
    * property in the rendering format rule.
174
    *
175
    * @access public
176
    *
177
    * @var array
178
    *
179
    */
180
 
181
    var $formatConf = array(
182
        'Docbook' => array(),
183
        'Latex' => array(),
184
        'Pdf' => array(),
185
        'Plain' => array(),
186
        'Rtf' => array(),
187
        'Xhtml' => array()
188
    );
189
 
190
 
191
    /**
192
    *
193
    * The delimiter for token numbers of parsed elements in source text.
194
    *
195
    * @access public
196
    *
197
    * @var string
198
    *
199
    */
200
 
201
    var $delim = "\xFF";
202
 
203
 
204
    /**
205
    *
206
    * The tokens generated by rules as the source text is parsed.
207
    *
208
    * As Text_Wiki applies rule classes to the source text, it will
209
    * replace portions of the text with a delimited token number.  This
210
    * is the array of those tokens, representing the replaced text and
211
    * any options set by the parser for that replaced text.
212
    *
213
    * The tokens array is sequential; each element is itself a sequential
214
    * array where element 0 is the name of the rule that generated the
215
    * token, and element 1 is an associative array where the key is an
216
    * option name and the value is an option value.
217
    *
218
    * @access private
219
    *
220
    * @var array
221
    *
222
    */
223
 
224
    var $tokens = array();
225
 
226
 
227
    /**
228
    *
229
    * The source text to which rules will be applied.
230
    *
231
    * This text will be transformed in-place, which means that it will
232
    * change as the rules are applied.
233
    *
234
    * @access private
235
    *
236
    * @var string
237
    *
238
    */
239
 
240
    var $source = '';
241
 
242
 
243
    /**
244
    *
245
    * Array of rule parsers.
246
    *
247
    * Text_Wiki creates one instance of every rule that is applied to
248
    * the source text; this array holds those instances.  The array key
249
    * is the rule name, and the array value is an instance of the rule
250
    * class.
251
    *
252
    * @access private
253
    *
254
    * @var array
255
    *
256
    */
257
 
258
    var $parseObj = array();
259
 
260
 
261
    /**
262
    *
263
    * Array of rule renderers.
264
    *
265
    * Text_Wiki creates one instance of every rule that is applied to
266
    * the source text; this array holds those instances.  The array key
267
    * is the rule name, and the array value is an instance of the rule
268
    * class.
269
    *
270
    * @access private
271
    *
272
    * @var array
273
    *
274
    */
275
 
276
    var $renderObj = array();
277
 
278
 
279
    /**
280
    *
281
    * Array of format renderers.
282
    *
283
    * @access private
284
    *
285
    * @var array
286
    *
287
    */
288
 
289
    var $formatObj = array();
290
 
291
 
292
    /**
293
    *
294
    * Array of paths to search, in order, for parsing and rendering rules.
295
    *
296
    * @access private
297
    *
298
    * @var array
299
    *
300
    */
301
 
302
    var $path = array(
303
        'parse' => array(),
304
        'render' => array()
305
    );
306
 
307
 
308
 
309
    /**
310
    *
311
    * The directory separator character.
312
    *
313
    * @access private
314
    *
315
    * @var string
316
    *
317
    */
318
 
319
    var $_dirSep = DIRECTORY_SEPARATOR;
320
 
321
 
322
    /**
323
    *
324
    * Constructor.
325
    *
326
    * @access public
327
    *
328
    * @param array $rules The set of rules to load for this object.
329
    *
330
    */
331
 
332
    function Text_Wiki($rules = null)
333
    {
334
        if (is_array($rules)) {
335
            $this->rules = $rules;
336
        }
337
 
338
        $this->addPath(
339
            'parse',
340
            $this->fixPath(dirname(__FILE__)) . 'Wiki/Parse/'
341
        );
342
 
343
        $this->addPath(
344
            'render',
345
            $this->fixPath(dirname(__FILE__)) . 'Wiki/Render/'
346
        );
347
 
348
    }
349
 
350
 
351
    /**
352
    *
353
    * Set parser configuration for a specific rule and key.
354
    *
355
    * @access public
356
    *
357
    * @param string $rule The parse rule to set config for.
358
    *
359
    * @param array|string $arg1 The full config array to use for the
360
    * parse rule, or a conf key in that array.
361
    *
362
    * @param string $arg2 The config value for the key.
363
    *
364
    * @return void
365
    *
366
    */
367
 
368
    function setParseConf($rule, $arg1, $arg2 = null)
369
    {
370
        $rule = ucwords(strtolower($rule));
371
 
372
        if (! isset($this->parseConf[$rule])) {
373
            $this->parseConf[$rule] = array();
374
        }
375
 
376
        // if first arg is an array, use it as the entire
377
        // conf array for the rule.  otherwise, treat arg1
378
        // as a key and arg2 as a value for the rule conf.
379
        if (is_array($arg1)) {
380
            $this->parseConf[$rule] = $arg1;
381
        } else {
382
            $this->parseConf[$rule][$arg1] = $arg2;
383
        }
384
    }
385
 
386
 
387
    /**
388
    *
389
    * Get parser configuration for a specific rule and key.
390
    *
391
    * @access public
392
    *
393
    * @param string $rule The parse rule to get config for.
394
    *
395
    * @param string $key A key in the conf array; if null,
396
    * returns the entire conf array.
397
    *
398
    * @return mixed The whole conf array if no key is specified,
399
    * or the specific conf key value.
400
    *
401
    */
402
 
403
    function getParseConf($rule, $key = null)
404
    {
405
        $rule = ucwords(strtolower($rule));
406
 
407
        // the rule does not exist
408
        if (! isset($this->parseConf[$rule])) {
409
            return null;
410
        }
411
 
412
        // no key requested, return the whole array
413
        if (is_null($key)) {
414
            return $this->parseConf[$rule];
415
        }
416
 
417
        // does the requested key exist?
418
        if (isset($this->parseConf[$rule][$key])) {
419
            // yes, return that value
420
            return $this->parseConf[$rule][$key];
421
        } else {
422
            // no
423
            return null;
424
        }
425
    }
426
 
427
 
428
    /**
429
    *
430
    * Set renderer configuration for a specific format, rule, and key.
431
    *
432
    * @access public
433
    *
434
    * @param string $format The render format to set config for.
435
    *
436
    * @param string $rule The render rule to set config for in the format.
437
    *
438
    * @param array|string $arg1 The config array, or the config key
439
    * within the render rule.
440
    *
441
    * @param string $arg2 The config value for the key.
442
    *
443
    * @return void
444
    *
445
    */
446
 
447
    function setRenderConf($format, $rule, $arg1, $arg2 = null)
448
    {
449
        $format = ucwords(strtolower($format));
450
        $rule = ucwords(strtolower($rule));
451
 
452
        if (! isset($this->renderConf[$format])) {
453
            $this->renderConf[$format] = array();
454
        }
455
 
456
        if (! isset($this->renderConf[$format][$rule])) {
457
            $this->renderConf[$format][$rule] = array();
458
        }
459
 
460
        // if first arg is an array, use it as the entire
461
        // conf array for the render rule.  otherwise, treat arg1
462
        // as a key and arg2 as a value for the render rule conf.
463
        if (is_array($arg1)) {
464
            $this->renderConf[$format][$rule] = $arg1;
465
        } else {
466
            $this->renderConf[$format][$rule][$arg1] = $arg2;
467
        }
468
    }
469
 
470
 
471
    /**
472
    *
473
    * Get renderer configuration for a specific format, rule, and key.
474
    *
475
    * @access public
476
    *
477
    * @param string $format The render format to get config for.
478
    *
479
    * @param string $rule The render format rule to get config for.
480
    *
481
    * @param string $key A key in the conf array; if null,
482
    * returns the entire conf array.
483
    *
484
    * @return mixed The whole conf array if no key is specified,
485
    * or the specific conf key value.
486
    *
487
    */
488
 
489
    function getRenderConf($format, $rule, $key = null)
490
    {
491
        $format = ucwords(strtolower($format));
492
        $rule = ucwords(strtolower($rule));
493
 
494
        if (! isset($this->renderConf[$format]) ||
495
            ! isset($this->renderConf[$format][$rule])) {
496
            return null;
497
        }
498
 
499
        // no key requested, return the whole array
500
        if (is_null($key)) {
501
            return $this->renderConf[$format][$rule];
502
        }
503
 
504
        // does the requested key exist?
505
        if (isset($this->renderConf[$format][$rule][$key])) {
506
            // yes, return that value
507
            return $this->renderConf[$format][$rule][$key];
508
        } else {
509
            // no
510
            return null;
511
        }
512
 
513
    }
514
 
515
    /**
516
    *
517
    * Set format configuration for a specific rule and key.
518
    *
519
    * @access public
520
    *
521
    * @param string $format The format to set config for.
522
    *
523
    * @param string $key The config key within the format.
524
    *
525
    * @param string $val The config value for the key.
526
    *
527
    * @return void
528
    *
529
    */
530
 
531
    function setFormatConf($format, $arg1, $arg2 = null)
532
    {
533
        if (! is_array($this->formatConf[$format])) {
534
            $this->formatConf[$format] = array();
535
        }
536
 
537
        // if first arg is an array, use it as the entire
538
        // conf array for the format.  otherwise, treat arg1
539
        // as a key and arg2 as a value for the format conf.
540
        if (is_array($arg1)) {
541
            $this->formatConf[$format] = $arg1;
542
        } else {
543
            $this->formatConf[$format][$arg1] = $arg2;
544
        }
545
    }
546
 
547
 
548
 
549
    /**
550
    *
551
    * Get configuration for a specific format and key.
552
    *
553
    * @access public
554
    *
555
    * @param string $format The format to get config for.
556
    *
557
    * @param mixed $key A key in the conf array; if null,
558
    * returns the entire conf array.
559
    *
560
    * @return mixed The whole conf array if no key is specified,
561
    * or the specific conf key value.
562
    *
563
    */
564
 
565
    function getFormatConf($format, $key = null)
566
    {
567
        // the format does not exist
568
        if (! isset($this->formatConf[$format])) {
569
            return null;
570
        }
571
 
572
        // no key requested, return the whole array
573
        if (is_null($key)) {
574
            return $this->formatConf[$format];
575
        }
576
 
577
        // does the requested key exist?
578
        if (isset($this->formatConf[$format][$key])) {
579
            // yes, return that value
580
            return $this->formatConf[$format][$key];
581
        } else {
582
            // no
583
            return null;
584
        }
585
    }
586
 
587
 
588
    /**
589
    *
590
    * Inserts a rule into to the rule set.
591
    *
592
    * @access public
593
    *
594
    * @param string $name The name of the rule.  Should be different from
595
    * all other keys in the rule set.
596
    *
597
    * @param string $tgt The rule after which to insert this new rule.  By
598
    * default (null) the rule is inserted at the end; if set to '', inserts
599
    * at the beginning.
600
    *
601
    * @return void
602
    *
603
    */
604
 
605
    function insertRule($name, $tgt = null)
606
    {
607
        $name = ucwords(strtolower($name));
608
        if (! is_null($tgt)) {
609
            $tgt = ucwords(strtolower($tgt));
610
        }
611
 
612
        // does the rule name to be inserted already exist?
613
        if (in_array($name, $this->rules)) {
614
            // yes, return
615
            return null;
616
        }
617
 
618
        // the target name is not null, and not '', but does not exist
619
        // in the list of rules. this means we're trying to insert after
620
        // a target key, but the target key isn't there.
621
        if (! is_null($tgt) && $tgt != '' &&
622
            ! in_array($tgt, $this->rules)) {
623
            return false;
624
        }
625
 
626
        // if $tgt is null, insert at the end.  We know this is at the
627
        // end (instead of resetting an existing rule) becuase we exited
628
        // at the top of this method if the rule was already in place.
629
        if (is_null($tgt)) {
630
            $this->rules[] = $name;
631
            return true;
632
        }
633
 
634
        // save a copy of the current rules, then reset the rule set
635
        // so we can insert in the proper place later.
636
        // where to insert the rule?
637
        if ($tgt == '') {
638
            // insert at the beginning
639
            array_unshift($this->rules, $name);
640
            return true;
641
        }
642
 
643
        // insert after the named rule
644
        $tmp = $this->rules;
645
        $this->rules = array();
646
 
647
        foreach ($tmp as $val) {
648
            $this->rules[] = $val;
649
            if ($val == $tgt) {
650
                $this->rules[] = $name;
651
            }
652
        }
653
 
654
        return true;
655
 
656
    }
657
 
658
 
659
    /**
660
    *
661
    * Delete (remove or unset) a rule from the $rules property.
662
    *
663
    * @access public
664
    *
665
    * @param string $rule The name of the rule to remove.
666
    *
667
    * @return void
668
    *
669
    */
670
 
671
    function deleteRule($name)
672
    {
673
        $name = ucwords(strtolower($name));
674
        $key = array_search($name, $this->rules);
675
        if ($key !== false) {
676
            unset($this->rules[$key]);
677
        }
678
    }
679
 
680
 
681
    /**
682
    *
683
    * Change from one rule to another in-place.
684
    *
685
    * @access public
686
    *
687
    * @param string $old The name of the rule to change from.
688
    *
689
    * @param string $new The name of the rule to change to.
690
    *
691
    * @return void
692
    *
693
    */
694
 
695
    function changeRule($old, $new)
696
    {
697
        $old = ucwords(strtolower($old));
698
        $new = ucwords(strtolower($new));
699
        $key = array_search($old, $this->rules);
700
        if ($key !== false) {
701
            $this->rules[$old] = $new;
702
        }
703
    }
704
 
705
 
706
    /**
707
    *
708
    * Enables a rule so that it is applied when parsing.
709
    *
710
    * @access public
711
    *
712
    * @param string $rule The name of the rule to enable.
713
    *
714
    * @return void
715
    *
716
    */
717
 
718
    function enableRule($name)
719
    {
720
        $name = ucwords(strtolower($name));
721
        $key = array_search($name, $this->disable);
722
        if ($key !== false) {
723
            unset($this->disable[$key]);
724
        }
725
    }
726
 
727
 
728
    /**
729
    *
730
    * Disables a rule so that it is not applied when parsing.
731
    *
732
    * @access public
733
    *
734
    * @param string $rule The name of the rule to disable.
735
    *
736
    * @return void
737
    *
738
    */
739
 
740
    function disableRule($name)
741
    {
742
        $name = ucwords(strtolower($name));
743
        $key = array_search($name, $this->disable);
744
        if ($key === false) {
745
            $this->disable[] = $name;
746
        }
747
    }
748
 
749
 
750
    /**
751
    *
752
    * Parses and renders the text passed to it, and returns the results.
753
    *
754
    * First, the method parses the source text, applying rules to the
755
    * text as it goes.  These rules will modify the source text
756
    * in-place, replacing some text with delimited tokens (and
757
    * populating the $this->tokens array as it goes).
758
    *
759
    * Next, the method renders the in-place tokens into the requested
760
    * output format.
761
    *
762
    * Finally, the method returns the transformed text.  Note that the
763
    * source text is transformed in place; once it is transformed, it is
764
    * no longer the same as the original source text.
765
    *
766
    * @access public
767
    *
768
    * @param string $text The source text to which wiki rules should be
769
    * applied, both for parsing and for rendering.
770
    *
771
    * @param string $format The target output format, typically 'xhtml'.
772
    *  If a rule does not support a given format, the output from that
773
    * rule is rule-specific.
774
    *
775
    * @return string The transformed wiki text.
776
    *
777
    */
778
 
779
    function transform($text, $format = 'Xhtml')
780
    {
781
        $this->parse($text);
782
        return $this->render($format);
783
    }
784
 
785
 
786
    /**
787
    *
788
    * Sets the $_source text property, then parses it in place and
789
    * retains tokens in the $_tokens array property.
790
    *
791
    * @access public
792
    *
793
    * @param string $text The source text to which wiki rules should be
794
    * applied, both for parsing and for rendering.
795
    *
796
    * @return void
797
    *
798
    */
799
 
800
    function parse($text)
801
    {
802
        // set the object property for the source text
803
        $this->source = $text;
804
 
805
        // reset the tokens.
806
        $this->tokens = array();
807
 
808
        // apply the parse() method of each requested rule to the source
809
        // text.
810
        foreach ($this->rules as $name) {
811
            // do not parse the rules listed in $disable
812
            if (! in_array($name, $this->disable)) {
813
 
814
                // load the parsing object
815
                $this->loadParseObj($name);
816
 
817
                // load may have failed; only parse if
818
                // an object is in the array now
819
                if (is_object($this->parseObj[$name])) {
820
                    $this->parseObj[$name]->parse();
821
                }
822
            }
823
        }
824
    }
825
 
826
 
827
    /**
828
    *
829
    * Renders tokens back into the source text, based on the requested format.
830
    *
831
    * @access public
832
    *
833
    * @param string $format The target output format, typically 'xhtml'.
834
    * If a rule does not support a given format, the output from that
835
    * rule is rule-specific.
836
    *
837
    * @return string The transformed wiki text.
838
    *
839
    */
840
 
841
    function render($format = 'Xhtml')
842
    {
843
        // the rendering method we're going to use from each rule
844
        $format = ucwords(strtolower($format));
845
 
846
        // the eventual output text
847
        $output = '';
848
 
849
        // when passing through the parsed source text, keep track of when
850
        // we are in a delimited section
851
        $in_delim = false;
852
 
853
        // when in a delimited section, capture the token key number
854
        $key = '';
855
 
856
        // load the format object
857
        $this->loadFormatObj($format);
858
 
859
        // pre-rendering activity
860
        if (is_object($this->formatObj[$format])) {
861
            $output .= $this->formatObj[$format]->pre();
862
        }
863
 
864
        // load the render objects
865
        foreach (array_keys($this->parseObj) as $rule) {
866
            $this->loadRenderObj($format, $rule);
867
        }
868
 
869
        // pass through the parsed source text character by character
870
        $k = strlen($this->source);
871
        for ($i = 0; $i < $k; $i++) {
872
 
873
            // the current character
874
            $char = $this->source{$i};
875
 
876
            // are alredy in a delimited section?
877
            if ($in_delim) {
878
 
879
                // yes; are we ending the section?
880
                if ($char == $this->delim) {
881
 
882
                    // yes, get the replacement text for the delimited
883
                    // token number and unset the flag.
884
                    $key = (int)$key;
885
                    $rule = $this->tokens[$key][0];
886
                    $opts = $this->tokens[$key][1];
887
                    $output .= $this->renderObj[$rule]->token($opts);
888
                    $in_delim = false;
889
 
890
                } else {
891
 
892
                    // no, add to the dlimited token key number
893
                    $key .= $char;
894
 
895
                }
896
 
897
            } else {
898
 
899
                // not currently in a delimited section.
900
                // are we starting into a delimited section?
901
                if ($char == $this->delim) {
902
                    // yes, reset the previous key and
903
                    // set the flag.
904
                    $key = '';
905
                    $in_delim = true;
906
                } else {
907
                    // no, add to the output as-is
908
                    $output .= $char;
909
                }
910
            }
911
        }
912
 
913
        // post-rendering activity
914
        if (is_object($this->formatObj[$format])) {
915
            $output .= $this->formatObj[$format]->post();
916
        }
917
 
918
        // return the rendered source text.
919
        return $output;
920
    }
921
 
922
 
923
    /**
924
    *
925
    * Returns the parsed source text with delimited token placeholders.
926
    *
927
    * @access public
928
    *
929
    * @return string The parsed source text.
930
    *
931
    */
932
 
933
    function getSource()
934
    {
935
        return $this->source;
936
    }
937
 
938
 
939
    /**
940
    *
941
    * Returns tokens that have been parsed out of the source text.
942
    *
943
    * @access public
944
    *
945
    * @param array $rules If an array of rule names is passed, only return
946
    * tokens matching these rule names.  If no array is passed, return all
947
    * tokens.
948
    *
949
    * @return array An array of tokens.
950
    *
951
    */
952
 
953
    function getTokens($rules = null)
954
    {
955
        if (is_null($rules)) {
956
            return $this->tokens;
957
        } else {
958
            settype($rules, 'array');
959
            $result = array();
960
            foreach ($this->tokens as $key => $val) {
961
                if (in_array($val[0], $rules)) {
962
                    $result[] = $val;
963
                }
964
            }
965
            return $result;
966
        }
967
    }
968
 
969
 
970
    /**
971
    *
972
    * Add a token to the Text_Wiki tokens array, and return a delimited
973
    * token number.
974
    *
975
    * @access public
976
    *
977
    * @param array $options An associative array of options for the new
978
    * token array element.  The keys and values are specific to the
979
    * rule, and may or may not be common to other rule options.  Typical
980
    * options keys are 'text' and 'type' but may include others.
981
    *
982
    * @param boolean $id_only If true, return only the token number, not
983
    * a delimited token string.
984
    *
985
    * @return string|int By default, return the number of the
986
    * newly-created token array element with a delimiter prefix and
987
    * suffix; however, if $id_only is set to true, return only the token
988
    * number (no delimiters).
989
    *
990
    */
991
 
992
    function addToken($rule, $options = array(), $id_only = false)
993
    {
994
        // increment the token ID number.  note that if you parse
995
        // multiple times with the same Text_Wiki object, the ID number
996
        // will not reset to zero.
997
        static $id;
998
        if (! isset($id)) {
999
            $id = 0;
1000
        } else {
1001
            $id ++;
1002
        }
1003
 
1004
        // force the options to be an array
1005
        settype($options, 'array');
1006
 
1007
        // add the token
1008
        $this->tokens[$id] = array(
1009
 
1010
            1 => $options
1011
        );
1012
 
1013
        // return a value
1014
        if ($id_only) {
1015
            // return the last token number
1016
            return $id;
1017
        } else {
1018
            // return the token number with delimiters
1019
            return $this->delim . $id . $this->delim;
1020
        }
1021
    }
1022
 
1023
 
1024
    /**
1025
    *
1026
    * Set or re-set a token with specific information, overwriting any
1027
    * previous rule name and rule options.
1028
    *
1029
    * @access public
1030
    *
1031
    * @param int $id The token number to reset.
1032
    *
1033
    * @param int $rule The rule name to use.
1034
    *
1035
    * @param array $options An associative array of options for the
1036
    * token array element.  The keys and values are specific to the
1037
    * rule, and may or may not be common to other rule options.  Typical
1038
    * options keys are 'text' and 'type' but may include others.
1039
    *
1040
    * @return void
1041
    *
1042
    */
1043
 
1044
    function setToken($id, $rule, $options = array())
1045
    {
1046
        // reset the token
1047
        $this->tokens[$id] = array(
1048
 
1049
            1 => $options
1050
        );
1051
    }
1052
 
1053
 
1054
    /**
1055
    *
1056
    * Load a rule parser class file.
1057
    *
1058
    * @access public
1059
    *
1060
    * @return bool True if loaded, false if not.
1061
    *
1062
    */
1063
 
1064
    function loadParseObj($rule)
1065
    {
1066
        $rule = ucwords(strtolower($rule));
1067
        $file = $rule . '.php';
1068
        $class = "Text_Wiki_Parse_$rule";
1069
 
1070
        if (! class_exists($class)) {
1071
            $loc = $this->findFile('parse', $file);
1072
            if ($loc) {
1073
                // found the class
1074
                include_once $loc;
1075
            } else {
1076
                // can't find the class
1077
                $this->parseObj[$rule] = null;
1078
                return false;
1079
            }
1080
        }
1081
 
1082
        $this->parseObj[$rule] =& new $class($this);
1083
 
1084
    }
1085
 
1086
 
1087
    /**
1088
    *
1089
    * Load a rule-render class file.
1090
    *
1091
    * @access public
1092
    *
1093
    * @return bool True if loaded, false if not.
1094
    *
1095
    */
1096
 
1097
    function loadRenderObj($format, $rule)
1098
    {
1099
        $format = ucwords(strtolower($format));
1100
        $rule = ucwords(strtolower($rule));
1101
        $file = "$format/$rule.php";
1102
        $class = "Text_Wiki_Render_$format" . "_$rule";
1103
 
1104
        if (! class_exists($class)) {
1105
            // load the class
1106
            $loc = $this->findFile('render', $file);
1107
            if ($loc) {
1108
                // found the class
1109
                include_once $loc;
1110
            } else {
1111
                // can't find the class
1112
                return false;
1113
            }
1114
        }
1115
 
1116
        $this->renderObj[$rule] =& new $class($this);
1117
    }
1118
 
1119
 
1120
    /**
1121
    *
1122
    * Load a format-render class file.
1123
    *
1124
    * @access public
1125
    *
1126
    * @return bool True if loaded, false if not.
1127
    *
1128
    */
1129
 
1130
    function loadFormatObj($format)
1131
    {
1132
        $format = ucwords(strtolower($format));
1133
        $file = $format . '.php';
1134
        $class = "Text_Wiki_Render_$format";
1135
 
1136
        if (! class_exists($class)) {
1137
            $loc = $this->findFile('render', $file);
1138
            if ($loc) {
1139
                // found the class
1140
                include_once $loc;
1141
            } else {
1142
                // can't find the class
1143
                return false;
1144
            }
1145
        }
1146
 
1147
        $this->formatObj[$format] =& new $class($this);
1148
    }
1149
 
1150
 
1151
    /**
1152
    *
1153
    * Add a path to a path array.
1154
    *
1155
    * @access public
1156
    *
1157
    * @param string $type The path-type to add (parse or render).
1158
    *
1159
    * @param string $dir The directory to add to the path-type.
1160
    *
1161
    * @return void
1162
    *
1163
    */
1164
 
1165
    function addPath($type, $dir)
1166
    {
1167
        $dir = $this->fixPath($dir);
1168
        if (! isset($this->path[$type])) {
1169
            $this->path[$type] = array($dir);
1170
        } else {
1171
            array_unshift($this->path[$type], $dir);
1172
        }
1173
    }
1174
 
1175
 
1176
    /**
1177
    *
1178
    * Get the current path array for a path-type.
1179
    *
1180
    * @access public
1181
    *
1182
    * @param string $type The path-type to look up (plugin, filter, or
1183
    * template).  If not set, returns all path types.
1184
    *
1185
    * @return array The array of paths for the requested type.
1186
    *
1187
    */
1188
 
1189
    function getPath($type = null)
1190
    {
1191
        if (is_null($type)) {
1192
            return $this->path;
1193
        } elseif (! isset($this->path[$type])) {
1194
            return array();
1195
        } else {
1196
            return $this->path[$type];
1197
        }
1198
    }
1199
 
1200
 
1201
    /**
1202
    *
1203
    * Searches a series of paths for a given file.
1204
    *
1205
    * @param array $type The type of paths to search (template, plugin,
1206
    * or filter).
1207
    *
1208
    * @param string $file The file name to look for.
1209
    *
1210
    * @return string|bool The full path and file name for the target file,
1211
    * or boolean false if the file is not found in any of the paths.
1212
    *
1213
    */
1214
 
1215
    function findFile($type, $file)
1216
    {
1217
        // get the set of paths
1218
        $set = $this->getPath($type);
1219
 
1220
        // start looping through them
1221
        foreach ($set as $path) {
1222
            $fullname = $path . $file;
1223
            if (file_exists($fullname) && is_readable($fullname)) {
1224
                return $fullname;
1225
            }
1226
        }
1227
 
1228
        // could not find the file in the set of paths
1229
        return false;
1230
    }
1231
 
1232
 
1233
    /**
1234
    *
1235
    * Append a trailing '/' to paths, unless the path is empty.
1236
    *
1237
    * @access private
1238
    *
1239
    * @param string $path The file path to fix
1240
    *
1241
    * @return string The fixed file path
1242
    *
1243
    */
1244
 
1245
    function fixPath($path)
1246
    {
1247
        $len = strlen($this->_dirSep);
1248
 
1249
        if (! empty($path) &&
1250
            substr($path, -1 * $len, $len) != $this->_dirSep)    {
1251
            return $path . $this->_dirSep;
1252
        } else {
1253
            return $path;
1254
        }
1255
    }
1256
}
1257
 
1258
?>