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 (!empty($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 (empty($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') && !empty($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 'normal':
618                break;
619
620            case 'small':
621                $result .= ' small';
622                break;
623
624            default:
625                $result .= ' reduced';
626                break;
627        }
628
629        switch ($this->getStyle('note-text-align')) {
630            case 'left':
631                $result .= ' left';
632                break;
633
634            default:
635                $result .= ' justify';
636                break;
637        }
638
639        return $result;
640    }
641
642    /**
643     *
644     */
645    protected function renderNoteIdBase() {
646        return $this->renderBase($this->getStyle('note-id-base'));
647    }
648
649    /**
650     *
651     */
652    protected function renderNoteIdFont() {
653        return $this->renderFont('note-id-font-weight', 'normal', 'note-id-font-style');
654    }
655
656    /**
657     *
658     */
659    protected function renderNoteIdFormat() {
660        $style = $this->getStyle('note-id-format');
661        switch ($style) {
662            case '.':
663                $result = array('', '.');
664                break;
665
666            default:
667                $result = $this->renderFormat($style);
668                break;
669        }
670
671        return $result;
672    }
673
674    /**
675     *
676     */
677    protected function renderNoteId($note) {
678        $idStyle = $this->getStyle('refnote-id');
679        if ($idStyle == 'name') {
680            $html = $note->getName();
681        }
682        else {
683            $html = $this->convertToStyle($note->getId(), $idStyle);
684        }
685
686        return $html;
687    }
688
689    /**
690     *
691     */
692    protected function renderBackRefCaret($singleReference) {
693        switch ($this->getStyle('back-ref-caret')) {
694            case 'prefix':
695                $result = '^ ';
696                break;
697
698            case 'merge':
699                $result = $singleReference ? '' : '^ ';
700                break;
701
702            default:
703                $result = '';
704                break;
705        }
706
707        return $result;
708    }
709
710    /**
711     *
712     */
713    protected function renderBackRefBase() {
714        return $this->renderBase($this->getStyle('back-ref-base'));
715    }
716
717    /**
718     *
719     */
720    protected function renderBackRefFont() {
721        return $this->renderFont('back-ref-font-weight', 'bold', 'back-ref-font-style');
722    }
723
724    /**
725     *
726     */
727    protected function renderBackRefSeparator() {
728        static $html = array('' => ',', 'none' => '');
729
730        $style = $this->getStyle('back-ref-separator');
731        if (!array_key_exists($style, $html)) {
732            $style = '';
733        }
734
735        return $html[$style];
736    }
737
738    /**
739     *
740     */
741    protected function renderBackRefId($reference, $index, $singleReference) {
742        $style = $this->getStyle('back-ref-format');
743        switch ($style) {
744            case 'a':
745                $result = $this->convertToLatin($index + 1, $style);
746                break;
747
748            case '1':
749                $result = $index + 1;
750                break;
751
752            case 'caret':
753                $result = '^';
754                break;
755
756            case 'arrow':
757                $result = '&uarr;';
758                break;
759
760            default:
761                $result = $this->renderReferenceId($reference);
762                break;
763        }
764
765        if ($singleReference && ($this->getStyle('back-ref-caret') == 'merge')) {
766            $result = '^';
767        }
768
769        return $result;
770    }
771
772    /**
773     *
774     */
775    protected function renderBase($style) {
776        static $html = array(
777            '' => array('<sup>', '</sup>'),
778            'text' => array('', '')
779        );
780
781        if (!array_key_exists($style, $html)) {
782            $style = '';
783        }
784
785        return $html[$style];
786    }
787
788    /**
789     *
790     */
791    protected function renderFont($weight, $defaultWeight, $style) {
792        list($weightOpen, $weightClose) = $this->renderFontWeight($this->getStyle($weight), $defaultWeight);
793        list($styleOpen, $styleClose) = $this->renderFontStyle($this->getStyle($style));
794
795        return array($weightOpen . $styleOpen, $styleClose . $weightClose);
796    }
797
798    /**
799     *
800     */
801    protected function renderFontWeight($style, $default) {
802        static $html = array(
803            'normal' => array('', ''),
804            'bold' => array('<b>', '</b>')
805        );
806
807        if (!array_key_exists($style, $html)) {
808            $style = $default;
809        }
810
811        return $html[$style];
812    }
813
814    /**
815     *
816     */
817    protected function renderFontStyle($style) {
818        static $html = array(
819            '' => array('', ''),
820            'italic' => array('<i>', '</i>')
821        );
822
823        if (!array_key_exists($style, $html)) {
824            $style = '';
825        }
826
827        return $html[$style];
828    }
829
830    /**
831     *
832     */
833    protected function renderFormat($style) {
834        static $html = array(
835            '' => array('', ')'),
836            '()' => array('(', ')'),
837            ']' => array('', ']'),
838            '[]' => array('[', ']'),
839            'none' => array('', '')
840        );
841
842        if (!array_key_exists($style, $html)) {
843            $style = '';
844        }
845
846        return $html[$style];
847    }
848
849    /**
850     *
851     */
852    protected function convertToStyle($id, $style) {
853        switch ($style) {
854            case 'a':
855            case 'A':
856                $result = $this->convertToLatin($id, $style);
857                break;
858
859            case 'i':
860            case 'I':
861                $result = $this->convertToRoman($id, $style);
862                break;
863
864            case '*':
865                $result = str_repeat('*', $id);
866                break;
867
868            default:
869                $result = $id;
870                break;
871        }
872
873        return $result;
874    }
875
876    /**
877     *
878     */
879    protected function convertToLatin($number, $case) {
880        static $alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
881
882        $result = '';
883
884        while ($number > 0) {
885            --$number;
886            $digit = $number % 26;
887            $result = $alpha[$digit] . $result;
888            $number = intval($number / 26);
889        }
890
891        if ($case == 'a') {
892            $result = strtolower($result);
893        }
894
895        return $result;
896    }
897
898    /**
899     *
900     */
901    protected function convertToRoman($number, $case) {
902        static $lookup = array(
903            'M' => 1000, 'CM' => 900, 'D' => 500, 'CD' => 400,
904            'C' => 100, 'XC' => 90, 'L' => 50, 'XL' => 40,
905            'X' => 10, 'IX' => 9, 'V' => 5, 'IV' => 4, 'I' => 1
906        );
907
908        $result = '';
909
910        foreach ($lookup as $roman => $value) {
911            $matches = intval($number / $value);
912
913            if ($matches > 0) {
914                $result .= str_repeat($roman, $matches);
915                $number = $number % $value;
916            }
917        }
918
919        if ($case == 'i') {
920            $result = strtolower($result);
921        }
922
923        return $result;
924    }
925}
926
927////////////////////////////////////////////////////////////////////////////////////////////////////
928class refnotes_harvard_renderer extends refnotes_basic_renderer {
929
930    /**
931     * Constructor
932     */
933    public function __construct($namespace) {
934        parent::__construct($namespace);
935    }
936
937    /**
938     *
939     */
940    public function getReferenceSharedDataSet() {
941        static $key = array('ref-authors', 'ref-author', 'authors', 'author', 'published', 'month', 'year');
942
943        return $key;
944    }
945
946    /**
947     *
948     */
949    public function getReferencePrivateDataSet() {
950        static $key = array('direct', 'pages', 'page');
951
952        return $key;
953    }
954
955    /**
956     *
957     */
958    public function renderNoteText($note) {
959        $data = new refnotes_renderer_data($note->getData());
960
961        if (!$data->has('title')) {
962            return parent::renderNoteText($note);
963        }
964
965        // authors, published. //[[url|title.]]// edition. [format], publisher, pages, isbn. (accessed)
966        // authors, published. chapter In //[[url|title.]]// edition. [format], publisher, pages, isbn. (accessed)
967        // authors, published. [[url|title.]] //journal//, volume, [format], publisher, pages, issn. (accessed)
968        // authors, published. [[url|title.]] //booktitle//, [format], publisher, pages, issn. (accessed)
969
970        $title = $this->renderTitle($data);
971
972        // authors, published. //$title// edition. [format], publisher, pages, isbn. (accessed)
973        // authors, published. chapter In //$title// edition. [format], publisher, pages, isbn. (accessed)
974        // authors, published. $title //journal//, volume, [format], publisher, pages, issn. (accessed)
975        // authors, published. $title //booktitle//, [format], publisher, pages, issn. (accessed)
976
977        $authors = $this->renderAuthors($data);
978
979        // $authors? //$title// edition. [format], publisher, pages, isbn. (accessed)
980        // $authors? chapter In //$title// edition. [format], publisher, pages, isbn. (accessed)
981        // $authors? $title //journal//, volume, [format], publisher, pages, issn. (accessed)
982        // $authors? $title //booktitle//, [format], publisher, pages, issn. (accessed)
983
984        $publication = $this->renderPublication($data, !empty($authors));
985
986        if ($data->has('journal')) {
987            // $authors? $title //journal//, volume, $publication?
988
989            $subtitle = $this->renderJournal($data);
990        }
991        elseif ($data->has('booktitle')) {
992            // $authors? $title //booktitle//, $publication?
993
994            $subtitle = $this->renderBookTitle($data);
995        }
996
997        if (!empty($subtitle)) {
998            // $authors? $title $subtitle?, $publication?
999
1000            $text = $title . ' ' . $subtitle;
1001
1002            // $authors? $text, $publication?
1003
1004            $text .= !empty($publication) ? ',' : '.';
1005        }
1006        else {
1007            // $authors? //$title// edition. $publication?
1008            // $authors? chapter In //$title// edition. $publication?
1009
1010            $text = $this->renderBook($data, $title);
1011        }
1012
1013        // $authors? $text $publication?
1014
1015        if (!empty($authors)) {
1016            $text = $authors . ' ' . $text;
1017        }
1018
1019        if (!empty($publication)) {
1020            $text .= ' ' . $publication;
1021        }
1022
1023        return $text;
1024    }
1025
1026    /**
1027     *
1028     */
1029    protected function renderTitle($data) {
1030        $text = $data->get('title') . '.';
1031
1032        if ($url = $data->get('url')) {
1033            $text = '[[' . $url . '|' . $text . ']]';
1034        }
1035
1036        return $text;
1037    }
1038
1039    /**
1040     *
1041     */
1042    protected function renderAuthors($data) {
1043        $text = $data->get('authors', 'author');
1044
1045        if (!empty($text)) {
1046            if ($published = $this->renderPublished($data)) {
1047                $text .= ', ' . $published;
1048            }
1049
1050            $text .= '.';
1051        }
1052
1053        return $text;
1054    }
1055
1056    /**
1057     *
1058     */
1059    protected function renderPublished($data, $useMonth = true) {
1060        $text = $data->get('published');
1061
1062        if (empty($text)) {
1063            if ($text = $data->get('year')) {
1064                if ($useMonth && $month = $data->get('month')) {
1065                    $text = $month . ' ' . $text;
1066                }
1067            }
1068        }
1069
1070        return $text;
1071    }
1072
1073    /**
1074     *
1075     */
1076    protected function renderPublication($data, $authors) {
1077        $part = array();
1078
1079        if ($format = $data->get('format')) {
1080            $part[] = '[' . $format . ']';
1081        }
1082
1083        $address = $data->get('address');
1084        $publisher = $data->get('publisher');
1085
1086        if ($address && $publisher) {
1087            $part[] = $address . ': ' . $publisher;
1088        }
1089        else {
1090            if ($address || $publisher) {
1091                $part[] = $address . $publisher;
1092            }
1093        }
1094
1095        if (!$authors && ($published = $this->renderPublished($data))) {
1096            $part[] = $published;
1097        }
1098
1099        if ($pages = $this->renderPages($data, array('note-pages', 'note-page', 'pages', 'page'))) {
1100            $part[] = $pages;
1101        }
1102
1103        if ($isbn = $data->get('isbn')) {
1104            $part[] = 'ISBN ' . $isbn;
1105        }
1106        elseif ($issn = $data->get('issn')) {
1107            $part[] = 'ISSN ' . $issn;
1108        }
1109
1110        $text = implode(', ', $part);
1111
1112        if (!empty($text)) {
1113            $text = rtrim($text, '.') . '.';
1114        }
1115
1116        if ($accessed = $data->get('accessed')) {
1117            $accessed = '(' . refnotes_localization::getInstance()->getLang('txt_accessed_cap') . ': ' . $accessed . ')';
1118
1119            if (!empty($text)) {
1120                $text .= ' ' . $accessed;
1121            }
1122            else {
1123                $text = $accessed;
1124            }
1125        }
1126
1127        return $text;
1128    }
1129
1130    /**
1131     *
1132     */
1133    protected function renderPages($data, $key) {
1134        $text = '';
1135
1136        foreach ($key as $k) {
1137            if ($text = $data->get($k)) {
1138                if (preg_match("/^[0-9]/", $text)) {
1139                    $abbr_key = (substr($k, -1) == 's') ? 'txt_pages_abbr' : 'txt_page_abbr';
1140                    $text = refnotes_localization::getInstance()->getLang($abbr_key) . $text;
1141                }
1142                break;
1143            }
1144        }
1145
1146        return $text;
1147    }
1148
1149    /**
1150     *
1151     */
1152    protected function renderJournal($data) {
1153        $text = '//' . $data->get('journal') . '//';
1154
1155        if ($volume = $data->get('volume')) {
1156            $text .= ', ' . $volume;
1157        }
1158
1159        return $text;
1160    }
1161
1162    /**
1163     *
1164     */
1165    protected function renderBook($data, $title) {
1166        $text = '//' . $title . '//';
1167
1168        if ($chapter = $data->get('chapter')) {
1169            $text = $chapter . '. ' . refnotes_localization::getInstance()->getLang('txt_in_cap') . ' ' . $text;
1170        }
1171
1172        if ($edition = $data->get('edition')) {
1173            $text .= ' ' . $edition . '.';
1174        }
1175
1176        return $text;
1177    }
1178
1179    /**
1180     *
1181     */
1182    protected function renderBookTitle($data) {
1183        return '//' . $data->get('booktitle') . '//';
1184    }
1185
1186    /**
1187     *
1188     */
1189    protected function renderReferenceId($reference) {
1190        $data = new refnotes_renderer_data($reference->getData());
1191
1192        if (!$this->checkReferenceData($data)) {
1193            return $this->renderBasicReferenceId($reference);
1194        }
1195
1196        $authors = $data->get('ref-authors', 'ref-author');
1197
1198        if (empty($authors)) {
1199            $authors = $data->get('authors', 'author');
1200            $authorList = explode(',', $authors, 4);
1201
1202            if (count($authorList) > 3) {
1203                $authors = $authorList[0] . refnotes_localization::getInstance()->getLang('txt_et_al');
1204            }
1205        }
1206
1207        $html = $this->renderReferenceExtra($data);
1208
1209        list($formatOpen, $formatClose) = $this->renderReferenceParentheses();
1210
1211        if ($data->isPositive('direct')) {
1212            $html = $authors . ' ' . $formatOpen . $html . $formatClose;
1213
1214            if ($this->getReferenceGrouping($reference)) {
1215                switch ($reference->getAttribute('group')) {
1216                    case 'open':
1217                    case 'hold':
1218                        $html .= $this->renderReferenceGroupSeparator();
1219                        break;
1220                }
1221            }
1222        }
1223        else {
1224            if ($this->getReferenceGrouping($reference)) {
1225                switch ($reference->getAttribute('group')) {
1226                    case 'open':
1227                        $formatClose = $this->renderReferenceGroupSeparator();
1228                        break;
1229
1230                    case 'hold':
1231                        $formatOpen = '';
1232                        $formatClose = $this->renderReferenceGroupSeparator();
1233                        break;
1234
1235                    case 'close':
1236                        $formatOpen = '';
1237                        break;
1238                }
1239            }
1240
1241            $html = $formatOpen . $authors . ', ' . $html . $formatClose;
1242        }
1243
1244        return htmlspecialchars($html);
1245    }
1246
1247    /**
1248     *
1249     */
1250    protected function renderBasicReferenceId($reference) {
1251        list($formatOpen, $formatClose) = parent::renderReferenceFormat($reference);
1252
1253        return $formatOpen . parent::renderReferenceId($reference) . $formatClose;
1254    }
1255
1256    /**
1257     *
1258     */
1259    protected function renderReferenceExtra($data) {
1260        $html = '';
1261
1262        if ($published = $this->renderPublished($data, false)) {
1263            $html .= $published;
1264        }
1265
1266        if ($pages = $this->renderPages($data, array('page', 'pages'))) {
1267            if (!empty($html)) {
1268                $html .= ', ';
1269            }
1270
1271            $html .= $pages;
1272        }
1273
1274        return $html;
1275    }
1276
1277    /**
1278     *
1279     */
1280    protected function renderReferenceParentheses() {
1281        $style = $this->getStyle('reference-format');
1282        $style = (($style == '[]') || ($style == ']')) ? '[]' : '()';
1283
1284        return $this->renderFormat($style);
1285    }
1286
1287    /**
1288     *
1289     */
1290    protected function renderReferenceFormat($reference) {
1291        return array('', '');
1292    }
1293
1294    /**
1295     *
1296     */
1297    protected function checkReferenceData($data) {
1298        $authors = $data->has('ref-authors', 'ref-author', 'authors', 'author');
1299        $year = $data->has('published', 'year');
1300        $page = $data->has('page', 'pages');
1301
1302        return $authors && ($year || $page);
1303    }
1304}
1305