Chris@0
|
1 <?php
|
Chris@0
|
2 namespace Masterminds\HTML5\Parser;
|
Chris@0
|
3
|
Chris@0
|
4 /**
|
Chris@0
|
5 * Handles special-case rules for the DOM tree builder.
|
Chris@0
|
6 *
|
Chris@0
|
7 * Many tags have special rules that need to be accomodated on an
|
Chris@0
|
8 * individual basis. This class handles those rules.
|
Chris@0
|
9 *
|
Chris@0
|
10 * See section 8.1.2.4 of the spec.
|
Chris@0
|
11 *
|
Chris@0
|
12 * @todo - colgroup and col special behaviors
|
Chris@0
|
13 * - body and head special behaviors
|
Chris@0
|
14 */
|
Chris@0
|
15 class TreeBuildingRules
|
Chris@0
|
16 {
|
Chris@0
|
17
|
Chris@0
|
18 protected static $tags = array(
|
Chris@0
|
19 'li' => 1,
|
Chris@0
|
20 'dd' => 1,
|
Chris@0
|
21 'dt' => 1,
|
Chris@0
|
22 'rt' => 1,
|
Chris@0
|
23 'rp' => 1,
|
Chris@0
|
24 'tr' => 1,
|
Chris@0
|
25 'th' => 1,
|
Chris@0
|
26 'td' => 1,
|
Chris@0
|
27 'thead' => 1,
|
Chris@0
|
28 'tfoot' => 1,
|
Chris@0
|
29 'tbody' => 1,
|
Chris@0
|
30 'table' => 1,
|
Chris@0
|
31 'optgroup' => 1,
|
Chris@0
|
32 'option' => 1
|
Chris@0
|
33 );
|
Chris@0
|
34
|
Chris@0
|
35 /**
|
Chris@0
|
36 * Build a new rules engine.
|
Chris@0
|
37 *
|
Chris@0
|
38 * @param \DOMDocument $doc
|
Chris@0
|
39 * The DOM document to use for evaluation and modification.
|
Chris@0
|
40 */
|
Chris@0
|
41 public function __construct($doc)
|
Chris@0
|
42 {
|
Chris@0
|
43 $this->doc = $doc;
|
Chris@0
|
44 }
|
Chris@0
|
45
|
Chris@0
|
46 /**
|
Chris@0
|
47 * Returns true if the given tagname has special processing rules.
|
Chris@0
|
48 */
|
Chris@0
|
49 public function hasRules($tagname)
|
Chris@0
|
50 {
|
Chris@0
|
51 return isset(static::$tags[$tagname]);
|
Chris@0
|
52 }
|
Chris@0
|
53
|
Chris@0
|
54 /**
|
Chris@0
|
55 * Evaluate the rule for the current tag name.
|
Chris@0
|
56 *
|
Chris@0
|
57 * This may modify the existing DOM.
|
Chris@0
|
58 *
|
Chris@0
|
59 * @return \DOMElement The new Current DOM element.
|
Chris@0
|
60 */
|
Chris@0
|
61 public function evaluate($new, $current)
|
Chris@0
|
62 {
|
Chris@0
|
63 switch ($new->tagName) {
|
Chris@0
|
64 case 'li':
|
Chris@0
|
65 return $this->handleLI($new, $current);
|
Chris@0
|
66 case 'dt':
|
Chris@0
|
67 case 'dd':
|
Chris@0
|
68 return $this->handleDT($new, $current);
|
Chris@0
|
69 case 'rt':
|
Chris@0
|
70 case 'rp':
|
Chris@0
|
71 return $this->handleRT($new, $current);
|
Chris@0
|
72 case 'optgroup':
|
Chris@0
|
73 return $this->closeIfCurrentMatches($new, $current, array(
|
Chris@0
|
74 'optgroup'
|
Chris@0
|
75 ));
|
Chris@0
|
76 case 'option':
|
Chris@0
|
77 return $this->closeIfCurrentMatches($new, $current, array(
|
Chris@0
|
78 'option',
|
Chris@0
|
79 ));
|
Chris@0
|
80 case 'tr':
|
Chris@0
|
81 return $this->closeIfCurrentMatches($new, $current, array(
|
Chris@0
|
82 'tr'
|
Chris@0
|
83 ));
|
Chris@0
|
84 case 'td':
|
Chris@0
|
85 case 'th':
|
Chris@0
|
86 return $this->closeIfCurrentMatches($new, $current, array(
|
Chris@0
|
87 'th',
|
Chris@0
|
88 'td'
|
Chris@0
|
89 ));
|
Chris@0
|
90 case 'tbody':
|
Chris@0
|
91 case 'thead':
|
Chris@0
|
92 case 'tfoot':
|
Chris@0
|
93 case 'table': // Spec isn't explicit about this, but it's necessary.
|
Chris@0
|
94
|
Chris@0
|
95 return $this->closeIfCurrentMatches($new, $current, array(
|
Chris@0
|
96 'thead',
|
Chris@0
|
97 'tfoot',
|
Chris@0
|
98 'tbody'
|
Chris@0
|
99 ));
|
Chris@0
|
100 }
|
Chris@0
|
101
|
Chris@0
|
102 return $current;
|
Chris@0
|
103 }
|
Chris@0
|
104
|
Chris@0
|
105 protected function handleLI($ele, $current)
|
Chris@0
|
106 {
|
Chris@0
|
107 return $this->closeIfCurrentMatches($ele, $current, array(
|
Chris@0
|
108 'li'
|
Chris@0
|
109 ));
|
Chris@0
|
110 }
|
Chris@0
|
111
|
Chris@0
|
112 protected function handleDT($ele, $current)
|
Chris@0
|
113 {
|
Chris@0
|
114 return $this->closeIfCurrentMatches($ele, $current, array(
|
Chris@0
|
115 'dt',
|
Chris@0
|
116 'dd'
|
Chris@0
|
117 ));
|
Chris@0
|
118 }
|
Chris@0
|
119
|
Chris@0
|
120 protected function handleRT($ele, $current)
|
Chris@0
|
121 {
|
Chris@0
|
122 return $this->closeIfCurrentMatches($ele, $current, array(
|
Chris@0
|
123 'rt',
|
Chris@0
|
124 'rp'
|
Chris@0
|
125 ));
|
Chris@0
|
126 }
|
Chris@0
|
127
|
Chris@0
|
128 protected function closeIfCurrentMatches($ele, $current, $match)
|
Chris@0
|
129 {
|
Chris@0
|
130 $tname = $current->tagName;
|
Chris@0
|
131 if (in_array($current->tagName, $match)) {
|
Chris@0
|
132 $current->parentNode->appendChild($ele);
|
Chris@0
|
133 } else {
|
Chris@0
|
134 $current->appendChild($ele);
|
Chris@0
|
135 }
|
Chris@0
|
136
|
Chris@0
|
137 return $ele;
|
Chris@0
|
138 }
|
Chris@0
|
139 }
|