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