Skip to content

Commit d2791d0

Browse files
committed
WIP: Add some stack functions
1 parent 74c9e6b commit d2791d0

2 files changed

Lines changed: 311 additions & 0 deletions

File tree

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
class WP_HTML_Element_Stack_Item {
4+
const IS_CLOSER = 1 << 0;
5+
const HAS_SELF_CLOSING_FLAG = 1 << 1;
6+
7+
/**
8+
* Stores the name of the bookmark pointing to the element at the position of the item.
9+
*
10+
* @var string|null
11+
*/
12+
public $bookmark_name = null;
13+
14+
/**
15+
* Stores the element class name for the element at the position of the item.
16+
*
17+
* This is the name of the PHP class representing the element.
18+
* For example, `WP_HTMLDivElement` from calling `WP_HTMLDivElement::class`.
19+
*
20+
* @var string|null
21+
*/
22+
public $element = null;
23+
24+
/**
25+
* Properties about this item in the stack that are relevant for relating opening and closing tags.
26+
*
27+
* @var int
28+
*/
29+
public $flags = 0;
30+
31+
/**
32+
* Pointer to related item on the stack, if one exists.
33+
* For example, a tag opener that opens the current tag closer.
34+
*
35+
* @var WP_HTML_Element_Stack_Item|null
36+
*/
37+
public $related_item = null;
38+
39+
public function __construct( $bookmark_name, $element, $flags, $related_item = null ) {
40+
$this->bookmark_name = $bookmark_name;
41+
$this->element = $element;
42+
$this->flags = $flags;
43+
$this->related_item = $related_item;
44+
}
45+
}
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
<?php
2+
3+
class WP_HTML_Element_Stack {
4+
/**
5+
* Stack holding HTML tokens, tag openers, tag closers, or plain bookmarks.
6+
*
7+
* @var WP_HTML_Element_Stack_Item[]
8+
*/
9+
public $stack = array();
10+
11+
public function __construct( $bookmark_name, $element, $flags ) {
12+
$this->bookmark_name = $bookmark_name;
13+
$this->element = $element;
14+
$this->flags = $flags;
15+
}
16+
17+
/**
18+
* Add an item to the top of the stack.
19+
*
20+
* @TODO: Do we need to insertion-sort these?
21+
*
22+
* @param $stack_item
23+
* @return void
24+
*/
25+
public function push( $stack_item ) {
26+
$this->stack[] = $stack_item;
27+
}
28+
29+
public function count() {
30+
return count( $this->stack );
31+
}
32+
33+
/**
34+
* Returns the bottom-most node on the stack.
35+
*
36+
* @return WP_HTML_Element_Stack_Item|null
37+
*/
38+
public function current_node() {
39+
$count = $this->count();
40+
41+
return $this->count() > 0
42+
? $this->stack[ $count - 1 ]
43+
: null;
44+
}
45+
46+
/**
47+
* Returns whether the given element is on the stack.
48+
*
49+
* @param string $element the ::class name of the element to check for.
50+
* @return boolean whether the given element is on the stack.
51+
*/
52+
public function has_element( $element ) {
53+
for ( $i = count( $this->stack ) - 1; $i > 0; $i++ ) {
54+
if ( $this->stack[ $i ]->element === $element ) {
55+
return true;
56+
}
57+
}
58+
59+
return false;
60+
}
61+
62+
/**
63+
* Returns whether an element is in a specific scope.
64+
*
65+
* @see https://html.spec.whatwg.org/#has-an-element-in-the-specific-scope
66+
*
67+
* @param string $element The target node.
68+
* @param string[] $termination_list List of elements that terminate the search.
69+
* @return bool
70+
*/
71+
public function has_element_in_specific_scope( $element, $termination_list ) {
72+
$i = $this->count();
73+
if ( $i === 0 ) {
74+
return false;
75+
}
76+
77+
$node = $this->stack[ --$i ];
78+
79+
if ( $node->element === $element ) {
80+
return true;
81+
}
82+
83+
if ( in_array( $element, $termination_list, true ) ) {
84+
return false;
85+
}
86+
87+
while ( $i > 0 && null !== ( $node = $this->stack[ --$i ] ) ) {
88+
if ( $node->element === $element ) {
89+
return true;
90+
}
91+
}
92+
93+
return false;
94+
}
95+
96+
/**
97+
* Returns whether a given element is in a particular scope.
98+
*
99+
* @see https://html.spec.whatwg.org/#has-an-element-in-scope
100+
*
101+
* @param string $element
102+
* @return bool
103+
*/
104+
public function has_element_in_particular_scope( $element ) {
105+
return $this->has_element_in_specific_scope( $element, array(
106+
WP_HTMLAppletElement::class,
107+
WP_HTMLCaptionElement::class,
108+
WP_HTMLHtmlElement::class,
109+
WP_HTMLTableElement::class,
110+
WP_HTMLTdElement::class,
111+
WP_HTMLThElement::class,
112+
WP_HTMLMarqueeElement::class,
113+
WP_HTMLObjectElement::class,
114+
WP_HTMLTemplateElement::class,
115+
WP_MathML_Mi_Element::class,
116+
WP_MathML_Mo_Element::class,
117+
WP_MathML_Mn_Element::class,
118+
WP_MathML_Ms_Element::class,
119+
WP_MathML_Mtext_Element::class,
120+
WP_MathML_Annotation_Xml_Element::class,
121+
WP_SVG_ForeignObject_Element::class,
122+
WP_SVG_Description_Element::class,
123+
WP_SVG_Title_Element::class,
124+
) );
125+
}
126+
127+
/**
128+
* Returns whether a given element is in list item scope.
129+
*
130+
* @see https://html.spec.whatwg.org/#has-an-element-in-list-item-scope
131+
*
132+
* @param $element
133+
* @return void
134+
*/
135+
public function has_element_in_list_item_scope( $element ) {
136+
return $this->has_element_in_specific_scope( $element, array(
137+
WP_HTMLAppletElement::class,
138+
WP_HTMLCaptionElement::class,
139+
WP_HTMLHtmlElement::class,
140+
WP_HTMLTableElement::class,
141+
WP_HTMLTdElement::class,
142+
WP_HTMLThElement::class,
143+
WP_HTMLMarqueeElement::class,
144+
WP_HTMLObjectElement::class,
145+
WP_HTMLTemplateElement::class,
146+
WP_MathML_Mi_Element::class,
147+
WP_MathML_Mo_Element::class,
148+
WP_MathML_Mn_Element::class,
149+
WP_MathML_Ms_Element::class,
150+
WP_MathML_Mtext_Element::class,
151+
WP_MathML_Annotation_Xml_Element::class,
152+
WP_SVG_ForeignObject_Element::class,
153+
WP_SVG_Description_Element::class,
154+
WP_SVG_Title_Element::class,
155+
156+
// Additionally these elements.
157+
WP_HTMLOlElement::class,
158+
WP_HTMLUlElement::class,
159+
) );
160+
}
161+
162+
/**
163+
* Returns whether a given element is in button scope.
164+
*
165+
* @see https://html.spec.whatwg.org/#has-an-element-in-button-scope
166+
*
167+
* @param string $element
168+
* @return boolean
169+
*/
170+
public function has_element_in_button_scope( $element ) {
171+
return $this->has_element_in_specific_scope( $element, array(
172+
WP_HTMLAppletElement::class,
173+
WP_HTMLCaptionElement::class,
174+
WP_HTMLHtmlElement::class,
175+
WP_HTMLTableElement::class,
176+
WP_HTMLTdElement::class,
177+
WP_HTMLThElement::class,
178+
WP_HTMLMarqueeElement::class,
179+
WP_HTMLObjectElement::class,
180+
WP_HTMLTemplateElement::class,
181+
WP_MathML_Mi_Element::class,
182+
WP_MathML_Mo_Element::class,
183+
WP_MathML_Mn_Element::class,
184+
WP_MathML_Ms_Element::class,
185+
WP_MathML_Mtext_Element::class,
186+
WP_MathML_Annotation_Xml_Element::class,
187+
WP_SVG_ForeignObject_Element::class,
188+
WP_SVG_Description_Element::class,
189+
WP_SVG_Title_Element::class,
190+
191+
// Additionally these elements.
192+
WP_HTMLButtonElement::class,
193+
) );
194+
}
195+
196+
/**
197+
* Returns whether the given element is in table scope.
198+
*
199+
* @see https://html.spec.whatwg.org/#has-an-element-in-table-scope
200+
*
201+
* @param string $element
202+
* @return bool
203+
*/
204+
public function has_element_in_table_scope( $element ) {
205+
return $this->has_element_in_specific_scope( $element, array(
206+
WP_HTMLAppletElement::class,
207+
WP_HTMLCaptionElement::class,
208+
WP_HTMLHtmlElement::class,
209+
WP_HTMLTableElement::class,
210+
WP_HTMLTdElement::class,
211+
WP_HTMLThElement::class,
212+
WP_HTMLMarqueeElement::class,
213+
WP_HTMLObjectElement::class,
214+
WP_HTMLTemplateElement::class,
215+
WP_MathML_Mi_Element::class,
216+
WP_MathML_Mo_Element::class,
217+
WP_MathML_Mn_Element::class,
218+
WP_MathML_Ms_Element::class,
219+
WP_MathML_Mtext_Element::class,
220+
WP_MathML_Annotation_Xml_Element::class,
221+
WP_SVG_ForeignObject_Element::class,
222+
WP_SVG_Description_Element::class,
223+
WP_SVG_Title_Element::class,
224+
225+
// Additionally these elements.
226+
WP_HTMLHtmlElement::class,
227+
WP_HTMLTableElement::class,
228+
WP_HTMLTemplateElement::class,
229+
) );
230+
}
231+
232+
/**
233+
* Returns whether a given element is in select scope.
234+
*
235+
* @see https://html.spec.whatwg.org/#has-an-element-in-select-scope
236+
*
237+
* @param string $element
238+
* @return bool
239+
*/
240+
public function has_element_in_select_scope( $element ) {
241+
return $this->has_element_in_specific_scope( $element, array(
242+
WP_HTMLAppletElement::class,
243+
WP_HTMLCaptionElement::class,
244+
WP_HTMLHtmlElement::class,
245+
WP_HTMLTableElement::class,
246+
WP_HTMLTdElement::class,
247+
WP_HTMLThElement::class,
248+
WP_HTMLMarqueeElement::class,
249+
WP_HTMLObjectElement::class,
250+
WP_HTMLTemplateElement::class,
251+
WP_MathML_Mi_Element::class,
252+
WP_MathML_Mo_Element::class,
253+
WP_MathML_Mn_Element::class,
254+
WP_MathML_Ms_Element::class,
255+
WP_MathML_Mtext_Element::class,
256+
WP_MathML_Annotation_Xml_Element::class,
257+
WP_SVG_ForeignObject_Element::class,
258+
WP_SVG_Description_Element::class,
259+
WP_SVG_Title_Element::class,
260+
261+
// Additionally these elements.
262+
WP_HTMLOptgroupElement::class,
263+
WP_HTMLOptionElement::class,
264+
) );
265+
}
266+
}

0 commit comments

Comments
 (0)