1<?php
2
3/**
4 * Plugin RefNotes: Renderer
5 *
6 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
7 * @author     Mykola Ostrovskyy <dwpforge@gmail.com>
8 */
9
10////////////////////////////////////////////////////////////////////////////////////////////////////
11class refnotes_renderer_mock {
12
13    /**
14     *
15     */
16    public function renderReference($reference) {
17        return '';
18    }
19}
20
21////////////////////////////////////////////////////////////////////////////////////////////////////
22abstract class refnotes_renderer_base {
23
24    protected $namespace;
25
26    /**
27     * Constructor
28     */
29    public function __construct($namespace) {
30        $this->namespace = $namespace;
31    }
32
33    /**
34     *
35     */
36    protected function getStyle($name) {
37        return $this->namespace->getStyle($name);
38    }
39
40    /**
41     * Returns an array of keys for data that is shared between references and notes.
42     */
43    abstract public function getReferenceSharedDataSet();
44
45    /**
46     * Returns an array of keys for data that is specific to references.
47     */
48    abstract public function getReferencePrivateDataSet();
49
50    /**
51     *
52     */
53    abstract public function renderReference($mode, $reference);
54
55    /**
56     *
57     */
58    abstract public function renderNoteText($note);
59
60    /**
61     *
62     */
63    abstract public function renderNote($mode, $note, $reference);
64}
65
66////////////////////////////////////////////////////////////////////////////////////////////////////
67class refnotes_renderer extends refnotes_renderer_base {
68
69    private $referenceRenderer;
70    private $noteRenderer;
71
72    /**
73     * Constructor
74     */
75    public function __construct($namespace) {
76        parent::__construct($namespace);
77
78        $this->referenceRenderer = $this->createRenderer($this->getStyle('reference-render'));
79        $this->noteRenderer = $this->createRenderer($this->getStyle('note-render'));
80    }
81
82    /**
83     *
84     */
85    private function createRenderer($style) {
86        switch ($style) {
87            case 'harvard':
88                $renderer = new refnotes_harvard_renderer($this->namespace);
89                break;
90
91            default:
92                $renderer = new refnotes_basic_renderer($this->namespace);
93                break;
94        }
95
96        return $renderer;
97    }
98
99    /**
100     *
101     */
102    public function getReferenceSharedDataSet() {
103        return $this->referenceRenderer->getReferenceSharedDataSet();
104    }
105
106    /**
107     *
108     */
109    public function getReferencePrivateDataSet() {
110        return $this->referenceRenderer->getReferencePrivateDataSet();
111    }
112
113    /**
114     *
115     */
116    public function renderReference($mode, $reference) {
117        return $this->referenceRenderer->renderReference($mode, $reference);
118    }
119
120    /**
121     *
122     */
123    public function renderNotesSeparator() {
124        $html = '';
125        $style = $this->getStyle('notes-separator');
126        if ($style != 'none') {
127            if ($style != '') {
128                $style = ' style="width: '. $style . '"';
129            }
130            $html = '<hr' . $style . '>' . DOKU_LF;
131        }
132
133        return $html;
134    }
135
136    /**
137     *
138     */
139    public function renderNoteText($note) {
140        return $this->noteRenderer->renderNoteText($note);
141    }
142
143    /**
144     *
145     */
146    public function renderNote($mode, $note, $reference) {
147        return $this->noteRenderer->renderNote($mode, $note, $reference);
148    }
149}
150
151////////////////////////////////////////////////////////////////////////////////////////////////////
152class refnotes_renderer_data {
153
154    private $data;
155
156    /**
157     * Constructor
158     */
159    public function __construct($data) {
160        $this->data = $data;
161    }
162
163    /**
164     *
165     */
166    public function has($key) {
167        if (func_num_args() > 1) {
168            $result = false;
169
170            foreach (func_get_args() as $key) {
171                if (array_key_exists($key, $this->data)) {
172                    $result = true;
173                    break;
174                }
175            }
176
177            return $result;
178        }
179        else {
180            return array_key_exists($key, $this->data);
181        }
182    }
183
184    /**
185     *
186     */
187    public function get($key) {
188        if (func_num_args() > 1) {
189            $result = '';
190
191            foreach (func_get_args() as $key) {
192                if (array_key_exists($key, $this->data)) {
193                    $result = $this->data[$key];
194                    break;
195                }
196            }
197
198            return $result;
199        }
200        else {
201            return array_key_exists($key, $this->data) ? $this->data[$key] : '';
202        }
203    }
204
205    /**
206     *
207     */
208    public function getLongest() {
209        $result = '';
210
211        if (func_num_args() > 0) {
212            foreach (func_get_args() as $key) {
213                if (array_key_exists($key, $this->data) && (strlen($result) < strlen($this->data[$key]))) {
214                    $result = $this->data[$key];
215                }
216            }
217        }
218        else {
219            foreach ($this->data as $value) {
220                if (strlen($result) < strlen($value)) {
221                    $result = $value;
222                }
223            }
224        }
225
226        return $result;
227    }
228
229    /**
230     *
231     */
232    public function isPositive($key)
233    {
234        static $lookup = array('y', 'yes', 'on', 'true', '1');
235
236        return in_array(strtolower($this->get($key)), $lookup);
237    }
238}
239
240////////////////////////////////////////////////////////////////////////////////////////////////////
241class refnotes_basic_renderer extends refnotes_renderer_base {
242
243    protected $renderedNoteId = array();
244
245    /**
246     *
247     */
248    public function getReferenceSharedDataSet() {
249        return array();
250    }
251
252    /**
253     *
254     */
255    public function getReferencePrivateDataSet() {
256        return array();
257    }
258
259    /**
260     *
261     */
262    public function renderReference($mode, $reference) {
263        if ($reference->isInline()) {
264            $doc = $this->renderInlineReference($reference);
265        }
266        else {
267            $doc = $this->renderRegularReference($mode, $reference);
268        }
269
270        return $doc;
271    }
272
273    /**
274     *
275     */
276    public function renderNoteText($note) {
277        $data = new refnotes_renderer_data($note->getData());
278
279        $text = $data->get('note-text', 'title');
280
281        if ($text == '') {
282            $text = $data->getLongest();
283        }
284
285        if ($url = $data->get('url')) {
286            $text = '[[' . $url . '|' . $text . ']]';
287        }
288
289        return $text;
290    }
291
292    /**
293     *
294     */
295    public function renderNote($mode, $note, $reference) {
296        $doc = '';
297
298        switch ($mode) {
299            case 'xhtml':
300                $doc = $this->renderNoteXhtml($note, $reference);
301                break;
302
303            case 'odt':
304                $doc = $this->renderNoteOdt($note, $reference);
305                break;
306        }
307
308        return $doc;
309    }
310
311    /**
312     *
313     */
314    protected function renderNoteXhtml($note, $reference) {
315        $html = '<div class="' . $this->renderNoteClass() . '">' . DOKU_LF;
316        $html .= $this->renderBackReferences($note, $reference);
317        $html .= '<span id="' . $note->getAnchorName() . ':text">' . DOKU_LF;
318        $html .= $note->getText() . DOKU_LF;
319        $html .= '</span></div>' . DOKU_LF;
320
321        $this->rendered = true;
322
323        return $html;
324    }
325
326    /**
327     *
328     */
329    protected function renderNoteOdt($note, $reference) {
330        $this->rendered = true;
331
332        return '';
333    }
334
335    /**
336     *
337     */
338    protected function getInlineReferenceStyle($reference, $name, $default) {
339        return ($reference->getAttribute('use-' . $name) === false) ? $default : $this->getStyle($name);
340    }
341
342    /**
343     *
344     */
345    protected function renderInlineReference($reference) {
346        $baseStyle = $this->getInlineReferenceStyle($reference, 'reference-base', 'text');
347        $fontWeightStyle = $this->getInlineReferenceStyle($reference, 'reference-font-weight', 'normal');
348        $fontStyle = $this->getInlineReferenceStyle($reference, 'reference-font-style', 'normal');
349        $formatStyle = $this->getInlineReferenceStyle($reference, 'reference-format', 'none');
350
351        list($baseOpen, $baseClose) = $this->renderBase($baseStyle);
352        list($fontOpen, $fontClose) = $this->renderFont($fontWeightStyle, 'normal', $fontStyle);
353        list($formatOpen, $formatClose) = $this->renderFormat($formatStyle);
354
355        $html = $baseOpen . $fontOpen . $formatOpen;
356        $html .= $reference->getNote()->getText();
357        $html .= $formatClose . $fontClose . $baseClose;
358
359        return $html;
360    }
361
362    /**
363     *
364     */
365    protected function renderRegularReference($mode, $reference) {
366        $doc = '';
367
368        switch ($mode) {
369            case 'xhtml':
370                $doc = $this->renderRegularReferenceXhtml($reference);
371                break;
372
373            case 'odt':
374                $doc = $this->renderRegularReferenceOdt($reference);
375                break;
376        }
377
378        return $doc;
379    }
380
381    /**
382     *
383     */
384    protected function renderRegularReferenceXhtml($reference) {
385        $noteName = $reference->getNote()->getAnchorName();
386        $referenceName = $reference->getAnchorName();
387        $class = $this->renderReferenceClass();
388
389        list($baseOpen, $baseClose) = $this->renderReferenceBase();
390        list($fontOpen, $fontClose) = $this->renderReferenceFont();
391        list($formatOpen, $formatClose) = $this->renderReferenceFormat($reference);
392
393        $html = $baseOpen . $fontOpen;
394        $html .= '<a href="#' . $noteName . '" name="' . $referenceName . '" class="' . $class . '">';
395        $html .= $formatOpen . $this->renderReferenceId($reference) . $formatClose;
396        $html .= '</a>';
397        $html .= $fontClose . $baseClose;
398
399        return $html;
400    }
401
402    /**
403     *
404     */
405    protected function renderRegularReferenceOdt($reference) {
406        $xmlOdt = '';
407        $note = $reference->getNote();
408        $noteId = $note->getId();
409        $refId = $reference->getId();
410
411        // Check to see if this note has been seen before
412
413        if (array_search($noteId, $this->renderedNoteId) === false) {
414            // new note, add it to the $renderedNoteId array
415            $this->renderedNoteId[] = $noteId;
416
417            $xmlOdt .= '<text:note text:id="refnote' . $refId . '" text:note-class="footnote">';
418            $xmlOdt .= '<text:note-citation text:label="' . $refId . '">' . $refId . '</text:note-citation>';
419            $xmlOdt .= '<text:note-body>';
420            $xmlOdt .= '<text:p>' . $note->getText();
421            $xmlOdt .= '</text:p>';
422            $xmlOdt .= '</text:note-body>';
423            $xmlOdt .= '</text:note>';
424        }
425        else {
426            // Seen this one before - just reference it FIXME: style isn't correct
427            $xmlOdt = '<text:note-ref text:note-class="footnote" text:ref-name="refnote' . $noteId . '">';
428            $xmlOdt .= $refId;
429            $xmlOdt .= '</text:note-ref>';
430        }
431
432        return $xmlOdt;
433    }
434
435    /**
436     *
437     */
438    protected function renderBackReferences($note, $reference) {
439        $references = count($reference);
440        $singleReference = ($references == 1);
441        $nameAttribute = ' name="' . $note->getAnchorName() .'"';
442        $backRefFormat = $this->getStyle('back-ref-format');
443        $backRefCaret = '';
444        $html = '';
445
446        list($formatOpen, $formatClose) = $this->renderNoteIdFormat();
447
448        if (($backRefFormat != 'note') && ($backRefFormat != '')) {
449            list($baseOpen, $baseClose) = $this->renderNoteIdBase();
450            list($fontOpen, $fontClose) = $this->renderNoteIdFont();
451
452            $html .= $baseOpen . $fontOpen;
453            $html .= '<a' . $nameAttribute .' class="nolink">';
454            $html .= $formatOpen . $this->renderNoteId($note) . $formatClose;
455            $html .= '</a>';
456            $html .= $fontClose . $baseClose . DOKU_LF;
457
458            $nameAttribute = '';
459            $formatOpen = '';
460            $formatClose = '';
461            $backRefCaret = $this->renderBackRefCaret($singleReference);
462        }
463
464        if ($backRefFormat != 'none') {
465            $separator = $this->renderBackRefSeparator();
466
467            list($baseOpen, $baseClose) = $this->renderBackRefBase();
468            list($fontOpen, $fontClose) = $this->renderBackRefFont();
469
470            $html .= $baseOpen . $backRefCaret;
471
472            for ($r = 0; $r < $references; $r++) {
473                $referenceName = $reference[$r]->getAnchorName();
474
475                if ($r > 0) {
476                    $html .= $separator . DOKU_LF;
477                }
478
479                $html .= $fontOpen;
480                $html .= '<a href="#' . $referenceName . '"' . $nameAttribute .' class="backref">';
481                $html .= $formatOpen . $this->renderBackRefId($reference[$r], $r, $singleReference) . $formatClose;
482                $html .= '</a>';
483                $html .= $fontClose;
484
485                $nameAttribute = '';
486            }
487
488            $html .= $baseClose . DOKU_LF;
489        }
490
491        return $html;
492    }
493
494    /**
495     *
496     */
497    protected function renderReferenceClass() {
498        switch ($this->getStyle('note-preview')) {
499            case 'tooltip':
500                $result = 'refnotes-ref note-tooltip';
501                break;
502
503            case 'none':
504                $result = 'refnotes-ref';
505                break;
506
507            default:
508                $result = 'refnotes-ref note-popup';
509                break;
510        }
511
512        return $result;
513    }
514
515    /**
516     *
517     */
518    protected function renderReferenceBase() {
519        return $this->renderBase($this->getStyle('reference-base'));
520    }
521
522    /**
523     *
524     */
525    protected function renderReferenceFont() {
526        return $this->renderFont('reference-font-weight', 'normal', 'reference-font-style');
527    }
528
529    /**
530     *
531     */
532    protected function renderReferenceFormat($reference) {
533        $result = $this->renderFormat($this->getStyle('reference-format'));
534
535        if ($this->getReferenceGrouping($reference)) {
536            switch ($reference->getAttribute('group')) {
537                case 'open':
538                    $result = array($result[0], $this->renderReferenceGroupSeparator());
539                    break;
540
541                case 'hold':
542                    $result = array('', $this->renderReferenceGroupSeparator());
543                    break;
544
545                case 'close':
546                    $result = array('', $result[1]);
547                    break;
548            }
549        }
550
551        return $result;
552    }
553
554    /**
555     *
556     */
557    protected function getReferenceGrouping($reference) {
558        $group = $reference->getAttribute('group');
559        return !empty($group) && in_array($group, array('open', 'hold', 'close')) &&
560                in_array($this->getStyle('reference-group'), array(',', 's'));
561    }
562
563    /**
564     *
565     */
566    protected function renderReferenceGroupSeparator() {
567        $style = $this->getStyle('reference-group');
568        switch ($style) {
569            case ',':
570                $result = ',';
571                break;
572
573            case 's':
574                $result = ';';
575                break;
576
577            default:
578                $result = '';
579                break;
580        }
581
582        return $result;
583    }
584
585    /**
586     *
587     */
588    protected function renderReferenceId($reference) {
589        $idStyle = $this->getStyle('refnote-id');
590        if ($idStyle == 'name') {
591            $html = $reference->getNote()->getName();
592        }
593        else {
594            switch ($this->getStyle('multi-ref-id')) {
595                case 'note':
596                    $id = $reference->getNote()->getId();
597                    break;
598
599                default:
600                    $id = $reference->getId();
601                    break;
602            }
603
604            $html = $this->convertToStyle($id, $idStyle);
605        }
606
607        return $html;
608    }
609
610    /**
611     *
612     */
613    protected function renderNoteClass() {
614        $result = 'note';
615
616        switch ($this->getStyle('note-font-size')) {
617            case 'small':
618                $result .= ' small';
619                break;
620        }
621
622        switch ($this->getStyle('note-text-align')) {
623            case 'left':
624                $result .= ' left';
625                break;
626
627            default:
628                $result .= ' justify';
629                break;
630        }
631
632        return $result;
633    }
634
635    /**
636     *
637     */
638    protected function renderNoteIdBase() {
639        return $this->renderBase($this->getStyle('note-id-base'));
640    }
641
642    /**
643     *
644     */
645    protected function renderNoteIdFont() {
646        return $this->renderFont('note-id-font-weight', 'normal', 'note-id-font-style');
647    }
648
649    /**
650     *
651     */
652    protected function renderNoteIdFormat() {
653        $style = $this->getStyle('note-id-format');
654        switch ($style) {
655            case '.':
656                $result = array('', '.');
657                break;
658
659            default:
660                $result = $this->renderFormat($style);
661                break;
662        }
663
664        return $result;
665    }
666
667    /**
668     *
669     */
670    protected function renderNoteId($note) {
671        $idStyle = $this->getStyle('refnote-id');
672        if ($idStyle == 'name') {
673            $html = $note->getName();
674        }
675        else {
676            $html = $this->convertToStyle($note->getId(), $idStyle);
677        }
678
679        return $html;
680    }
681
682    /**
683     *
684     */
685    protected function renderBackRefCaret($singleReference) {
686        switch ($this->getStyle('back-ref-caret')) {
687            case 'prefix':
688                $result = '^ ';
689                break;
690
691            case 'merge':
692                $result = $singleReference ? '' : '^ ';
693                break;
694
695            default:
696                $result = '';
697                break;
698        }
699
700        return $result;
701    }
702
703    /**
704     *
705     */
706    protected function renderBackRefBase() {
707        return $this->renderBase($this->getStyle('back-ref-base'));
708    }
709
710    /**
711     *
712     */
713    protected function renderBackRefFont() {
714        return $this->renderFont('back-ref-font-weight', 'bold', 'back-ref-font-style');
715    }
716
717    /**
718     *
719     */
720    protected function renderBackRefSeparator() {
721        static $html = array('' => ',', 'none' => '');
722
723        $style = $this->getStyle('back-ref-separator');
724        if (!array_key_exists($style, $html)) {
725            $style = '';
726        }
727
728        return $html[$style];
729    }
730
731    /**
732     *
733     */
734    protected function renderBackRefId($reference, $index, $singleReference) {
735        $style = $this->getStyle('back-ref-format');
736        switch ($style) {
737            case 'a':
738                $result = $this->convertToLatin($index + 1, $style);
739                break;
740
741            case '1':
742                $result = $index + 1;
743                break;
744
745            case 'caret':
746                $result = '^';
747                break;
748
749            case 'arrow':
750                $result = '&uarr;';
751                break;
752
753            default:
754                $result = $this->renderReferenceId($reference);
755                break;
756        }
757
758        if ($singleReference && ($this->getStyle('back-ref-caret') == 'merge')) {
759            $result = '^';
760        }
761
762        return $result;
763    }
764
765    /**
766     *
767     */
768    protected function renderBase($style) {
769        static $html = array(
770            '' => array('<sup>', '</sup>'),
771            'text' => array('', '')
772        );
773
774        if (!array_key_exists($style, $html)) {
775            $style = '';
776        }
777
778        return $html[$style];
779    }
780
781    /**
782     *
783     */
784    protected function renderFont($weight, $defaultWeight, $style) {
785        list($weightOpen, $weightClose) = $this->renderFontWeight($this->getStyle($weight), $defaultWeight);
786        list($styleOpen, $styleClose) = $this->renderFontStyle($this->getStyle($style));
787
788        return array($weightOpen . $styleOpen, $styleClose . $weightClose);
789    }
790
791    /**
792     *
793     */
794    protected function renderFontWeight($style, $default) {
795        static $html = array(
796            'normal' => array('', ''),
797            'bold' => array('<b>', '</b>')
798        );
799
800        if (!array_key_exists($style, $html)) {
801            $style = $default;
802        }
803
804        return $html[$style];
805    }
806
807    /**
808     *
809     */
810    protected function renderFontStyle($style) {
811        static $html = array(
812            '' => array('', ''),
813            'italic' => array('<i>', '</i>')
814        );
815
816        if (!array_key_exists($style, $html)) {
817            $style = '';
818        }
819
820        return $html[$style];
821    }
822
823    /**
824     *
825     */
826    protected function renderFormat($style) {
827        static $html = array(
828            '' => array('', ')'),
829            '()' => array('(', ')'),
830            ']' => array('', ']'),
831            '[]' => array('[', ']'),
832            'none' => array('', '')
833        );
834
835        if (!array_key_exists($style, $html)) {
836            $style = '';
837        }
838
839        return $html[$style];
840    }
841
842    /**
843     *
844     */
845    protected function convertToStyle($id, $style) {
846        switch ($style) {
847            case 'a':
848            case 'A':
849                $result = $this->convertToLatin($id, $style);
850                break;
851
852            case 'i':
853            case 'I':
854                $result = $this->convertToRoman($id, $style);
855                break;
856
857            case '*':
858                $result = str_repeat('*', $id);
859                break;
860
861            default:
862                $result = $id;
863                break;
864        }
865
866        return $result;
867    }
868
869    /**
870     *
871     */
872    protected function convertToLatin($number, $case) {
873        static $alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
874
875        $result = '';
876
877        while ($number > 0) {
878            --$number;
879            $digit = $number % 26;
880            $result = $alpha[$digit] . $result;
881            $number = intval($number / 26);
882        }
883
884        if ($case == 'a') {
885            $result = strtolower($result);
886        }
887
888        return $result;
889    }
890
891    /**
892     *
893     */
894    protected function convertToRoman($number, $case) {
895        static $lookup = array(
896            'M' => 1000, 'CM' => 900, 'D' => 500, 'CD' => 400,
897            'C' => 100, 'XC' => 90, 'L' => 50, 'XL' => 40,
898            'X' => 10, 'IX' => 9, 'V' => 5, 'IV' => 4, 'I' => 1
899        );
900
901        $result = '';
902
903        foreach ($lookup as $roman => $value) {
904            $matches = intval($number / $value);
905
906            if ($matches > 0) {
907                $result .= str_repeat($roman, $matches);
908                $number = $number % $value;
909            }
910        }
911
912        if ($case == 'i') {
913            $result = strtolower($result);
914        }
915
916        return $result;
917    }
918}
919
920////////////////////////////////////////////////////////////////////////////////////////////////////
921class refnotes_harvard_renderer extends refnotes_basic_renderer {
922
923    /**
924     * Constructor
925     */
926    public function __construct($namespace) {
927        parent::__construct($namespace);
928    }
929
930    /**
931     *
932     */
933    public function getReferenceSharedDataSet() {
934        static $key = array('ref-authors', 'ref-author', 'authors', 'author', 'published', 'month', 'year');
935
936        return $key;
937    }
938
939    /**
940     *
941     */
942    public function getReferencePrivateDataSet() {
943        static $key = array('direct', 'pages', 'page');
944
945        return $key;
946    }
947
948    /**
949     *
950     */
951    public function renderNoteText($note) {
952        $data = new refnotes_renderer_data($note->getData());
953
954        if (!$data->has('title')) {
955            return parent::renderNoteText($note);
956        }
957
958        // authors, published. //[[url|title.]]// edition. publisher, pages, isbn.
959        // authors, published. chapter In //[[url|title.]]// edition. publisher, pages, isbn.
960        // authors, published. [[url|title.]] //journal//, volume, publisher, pages, issn.
961        // authors, published. [[url|title.]] //booktitle//, publisher, pages, issn.
962
963        $title = $this->renderTitle($data);
964
965        // authors, published. //$title// edition. publisher, pages, isbn.
966        // authors, published. chapter In //$title// edition. publisher, pages, isbn.
967        // authors, published. $title //journal//, volume, publisher, pages, issn.
968        // authors, published. $title //booktitle//, publisher, pages, issn.
969
970        $authors = $this->renderAuthors($data);
971
972        // $authors? //$title// edition. publisher, pages, isbn.
973        // $authors? chapter In //$title// edition. publisher, pages, isbn.
974        // $authors? $title //journal//, volume, publisher, pages, issn.
975        // $authors? $title //booktitle//, publisher, pages, issn.
976
977        $publication = $this->renderPublication($data, $authors != '');
978
979        if ($data->has('journal')) {
980            // $authors? $title //journal//, volume, $publication?
981
982            $subtitle = $this->renderJournal($data);
983        }
984        elseif ($data->has('booktitle')) {
985            // $authors? $title //booktitle//, $publication?
986
987            $subtitle = $this->renderBookTitle($data);
988        }
989
990        if (!empty($subtitle)) {
991            // $authors? $title $subtitle?, $publication?
992
993            $text = $title . ' ' . $subtitle;
994
995            // $authors? $text, $publication?
996
997            $text .= ($publication != '') ? ',' : '.';
998        }
999        else {
1000            // $authors? //$title// edition. $publication?
1001            // $authors? chapter In //$title// edition. $publication?
1002
1003            $text = $this->renderBook($data, $title);
1004        }
1005
1006        // $authors? $text $publication?
1007
1008        if ($authors != '') {
1009            $text = $authors . ' ' . $text;
1010        }
1011
1012        if ($publication != '') {
1013            $text .= ' ' . $publication;
1014        }
1015
1016        return $text;
1017    }
1018
1019    /**
1020     *
1021     */
1022    protected function renderTitle($data) {
1023        $text = $data->get('title') . '.';
1024
1025        if ($url = $data->get('url')) {
1026            $text = '[[' . $url . '|' . $text . ']]';
1027        }
1028
1029        return $text;
1030    }
1031
1032    /**
1033     *
1034     */
1035    protected function renderAuthors($data) {
1036        $text = $data->get('authors', 'author');
1037
1038        if ($text != '') {
1039            if ($published = $this->renderPublished($data)) {
1040                $text .= ', ' . $published;
1041            }
1042
1043            $text .= '.';
1044        }
1045
1046        return $text;
1047    }
1048
1049    /**
1050     *
1051     */
1052    protected function renderPublished($data, $useMonth = true) {
1053        $text = $data->get('published');
1054
1055        if ($text == '') {
1056            if ($text = $data->get('year')) {
1057                if ($useMonth && $month = $data->get('month')) {
1058                    $text = $month . ' ' . $text;
1059                }
1060            }
1061        }
1062
1063        return $text;
1064    }
1065
1066    /**
1067     *
1068     */
1069    protected function renderPublication($data, $authors) {
1070        $part = array();
1071
1072        $address = $data->get('address');
1073        $publisher = $data->get('publisher');
1074
1075        if ($address && $publisher) {
1076            $part[] = $address . ': ' . $publisher;
1077        }
1078        else {
1079            if ($address || $publisher) {
1080                $part[] = $address . $publisher;
1081            }
1082        }
1083
1084        if (!$authors && ($published = $this->renderPublished($data))) {
1085            $part[] = $published;
1086        }
1087
1088        if ($pages = $this->renderPages($data, array('note-pages', 'note-page', 'pages', 'page'))) {
1089            $part[] = $pages;
1090        }
1091
1092        if ($isbn = $data->get('isbn')) {
1093            $part[] = 'ISBN ' . $isbn;
1094        }
1095        elseif ($issn = $data->get('issn')) {
1096            $part[] = 'ISSN ' . $issn;
1097        }
1098
1099        $text = implode(', ', $part);
1100
1101        if ($text != '') {
1102            $text = rtrim($text, '.') . '.';
1103        }
1104
1105        return $text;
1106    }
1107
1108    /**
1109     *
1110     */
1111    protected function renderPages($data, $key) {
1112        $text = '';
1113
1114        foreach ($key as $k) {
1115            if ($text = $data->get($k)) {
1116                if (preg_match("/^[0-9]/", $text)) {
1117                    $abbr_key = (substr($k, -1) == 's') ? 'txt_pages_abbr' : 'txt_page_abbr';
1118                    $text = refnotes_localization::getInstance()->getLang($abbr_key) . $text;
1119                }
1120                break;
1121            }
1122        }
1123
1124        return $text;
1125    }
1126
1127    /**
1128     *
1129     */
1130    protected function renderJournal($data) {
1131        $text = '//' . $data->get('journal') . '//';
1132
1133        if ($volume = $data->get('volume')) {
1134            $text .= ', ' . $volume;
1135        }
1136
1137        return $text;
1138    }
1139
1140    /**
1141     *
1142     */
1143    protected function renderBook($data, $title) {
1144        $text = '//' . $title . '//';
1145
1146        if ($chapter = $data->get('chapter')) {
1147            $text = $chapter . '. ' . refnotes_localization::getInstance()->getLang('txt_in_cap') . ' ' . $text;
1148        }
1149
1150        if ($edition = $data->get('edition')) {
1151            $text .= ' ' . $edition . '.';
1152        }
1153
1154        return $text;
1155    }
1156
1157    /**
1158     *
1159     */
1160    protected function renderBookTitle($data) {
1161        return '//' . $data->get('booktitle') . '//';
1162    }
1163
1164    /**
1165     *
1166     */
1167    protected function renderReferenceId($reference) {
1168        $data = new refnotes_renderer_data($reference->getData());
1169
1170        if (!$this->checkReferenceData($data)) {
1171            return $this->renderBasicReferenceId($reference);
1172        }
1173
1174        $authors = $data->get('ref-authors', 'ref-author', 'authors', 'author');
1175        $html = $this->renderReferenceExtra($data);
1176
1177        list($formatOpen, $formatClose) = $this->renderReferenceParentheses();
1178
1179        if ($data->isPositive('direct')) {
1180            $html = $authors . ' ' . $formatOpen . $html . $formatClose;
1181
1182            if ($this->getReferenceGrouping($reference)) {
1183                switch ($reference->getAttribute('group')) {
1184                    case 'open':
1185                    case 'hold':
1186                        $html .= $this->renderReferenceGroupSeparator();
1187                        break;
1188                }
1189            }
1190        }
1191        else {
1192            if ($this->getReferenceGrouping($reference)) {
1193                switch ($reference->getAttribute('group')) {
1194                    case 'open':
1195                        $formatClose = $this->renderReferenceGroupSeparator();
1196                        break;
1197
1198                    case 'hold':
1199                        $formatOpen = '';
1200                        $formatClose = $this->renderReferenceGroupSeparator();
1201                        break;
1202
1203                    case 'close':
1204                        $formatOpen = '';
1205                        break;
1206                }
1207            }
1208
1209            $html = $formatOpen . $authors . ', ' . $html . $formatClose;
1210        }
1211
1212        return htmlspecialchars($html);
1213    }
1214
1215    /**
1216     *
1217     */
1218    protected function renderBasicReferenceId($reference) {
1219        list($formatOpen, $formatClose) = parent::renderReferenceFormat($reference);
1220
1221        return $formatOpen . parent::renderReferenceId($reference) . $formatClose;
1222    }
1223
1224    /**
1225     *
1226     */
1227    protected function renderReferenceExtra($data) {
1228        $html = '';
1229
1230        if ($published = $this->renderPublished($data, false)) {
1231            $html .= $published;
1232        }
1233
1234        if ($pages = $this->renderPages($data, array('page', 'pages'))) {
1235            if ($html != '') {
1236                $html .= ', ';
1237            }
1238
1239            $html .= $pages;
1240        }
1241
1242        return $html;
1243    }
1244
1245    /**
1246     *
1247     */
1248    protected function renderReferenceParentheses() {
1249        $style = $this->getStyle('reference-format');
1250        $style = (($style == '[]') || ($style == ']')) ? '[]' : '()';
1251
1252        return $this->renderFormat($style);
1253    }
1254
1255    /**
1256     *
1257     */
1258    protected function renderReferenceFormat($reference) {
1259        return array('', '');
1260    }
1261
1262    /**
1263     *
1264     */
1265    protected function checkReferenceData($data) {
1266        $authors = $data->has('ref-authors', 'ref-author', 'authors', 'author');
1267        $year = $data->has('published', 'year');
1268        $page = $data->has('page', 'pages');
1269
1270        return $authors && ($year || $page);
1271    }
1272}
1273