1<?php
2
3namespace dokuwiki\plugin\prosemirror\parser;
4
5class TextNode extends Node implements InlineNodeInterface
6{
7    /** @var  TextNode */
8    public $previous;
9
10    /** @var  Node */
11    protected $parent;
12
13    /** @var Mark[] */
14    protected $marks = [];
15
16    protected $text = '';
17
18    public function __construct($data, Node $parent, Node $previous = null)
19    {
20        $this->parent = &$parent;
21        if ($previous !== false) {
22            $this->previous = &$previous;
23        }
24
25        $this->text = $data['text'] ?? '';
26        if (isset($data['marks'])) {
27            $this->setMarks($data['marks']);
28        }
29    }
30
31    public function getPrefixSyntax()
32    {
33        $doc = '';
34
35        /** @var Mark[] $openingMarks */
36        $openingMarks = [];
37        foreach ($this->marks as $mark) {
38            if ($mark->isOpeningMark()) {
39                $previousOpeningMark = end($openingMarks);
40                if ($previousOpeningMark) {
41                    $mark->setPrevious($previousOpeningMark);
42                    $previousOpeningMark->setNext($mark);
43                }
44                $openingMarks[] = $mark;
45            }
46        }
47
48        if (!empty($openingMarks)) {
49            $mark = $openingMarks[0]->getFirst();
50            $doc .= $mark->getOpeningSyntax();
51            while ($mark = $mark->getNext()) {
52                $doc .= $mark->getOpeningSyntax();
53            }
54
55            foreach ($openingMarks as $mark) {
56                $mark->setNext(null);
57                $mark->setPrevious(null);
58            }
59        }
60        return $doc;
61    }
62
63    public function getPostfixSyntax()
64    {
65        $doc = '';
66        /** @var Mark[] $closingMarks */
67        $closingMarks = [];
68        foreach ($this->marks as $mark) {
69            if ($mark->isClosingMark()) {
70                $previousClosingMark = end($closingMarks);
71                if ($previousClosingMark) {
72                    $mark->setPrevious($previousClosingMark);
73                    $previousClosingMark->setNext($mark);
74                }
75                $closingMarks[] = $mark;
76            }
77        }
78
79        if (!empty($closingMarks)) {
80            $mark = $closingMarks[0]->getLast();
81            $doc .= $mark->getClosingSyntax();
82            while ($mark = $mark->getPrevious()) {
83                $doc .= $mark->getClosingSyntax();
84            }
85
86            foreach ($closingMarks as $mark) {
87                $mark->setNext(null);
88                $mark->setPrevious(null);
89            }
90        }
91
92        return $doc;
93    }
94
95    public function getInnerSyntax()
96    {
97        return $this->text;
98    }
99
100
101    public function toSyntax()
102    {
103        $prefix = $this->getPrefixSyntax();
104        $inner = $this->getInnerSyntax();
105        $postfix = $this->getPostfixSyntax();
106        return $prefix . $inner . $postfix;
107    }
108
109    /**
110     * @param array $marks
111     *
112     * @return $this
113     * @throws \Exception
114     */
115    protected function setMarks(array $marks)
116    {
117        foreach ($marks as $markData) {
118            $currentMark = new Mark($markData, $this);
119            $type = $currentMark->getType();
120            $this->marks[$type] = $currentMark;
121            if ($this->previous !== null) {
122                $this->previous->increaseMark($type);
123            }
124        }
125        return $this;
126    }
127
128    /**
129     * @param string $markType
130     */
131    public function increaseMark($markType)
132    {
133        if (!isset($this->marks[$markType])) {
134            return;
135        }
136
137        $this->marks[$markType]->incrementTail();
138        if ($this->previous !== null) {
139            $this->previous->increaseMark($markType);
140        }
141    }
142
143    public function getStartingNodeMarkScore($markType)
144    {
145        if ($this === $this->previous) {
146            throw new \Exception('circular reference: ' . $this->text);
147        }
148        if (!isset($this->marks[$markType])) {
149            // we don't have that mark
150            return null;
151        }
152        if ($this->previous === null) {
153            // we are the first node
154            return $this->marks[$markType]->getTailLength();
155        }
156
157        $earlierMarkScore = $this->previous->getStartingNodeMarkScore($markType);
158        if ($earlierMarkScore === null) {
159            // the mark begins with us
160            return $this->marks[$markType]->getTailLength();
161        }
162        return $earlierMarkScore;
163    }
164}
165