1 module gumbo.node;
2 
3 import gumbo.capi;
4 
5 import std.conv : text;
6 import std.stdio;
7 
8 class Node {
9     enum Type {
10         document,
11         element,
12         text,
13         cdata,
14         comment,
15         whitespace
16     };
17 
18 protected:
19     GumboNode * _node;
20     Node[]      _children;
21 
22 public:
23     this(GumboNode * node)
24     {
25         _node = node;
26     }
27 
28     Node parent()
29     {
30         return Node.fromCAPI(_node.parent);
31     }
32 
33     GumboNodeType type()
34     {
35         return _node.type;
36     }
37 
38     size_t indexWithinParent()
39     {
40         return _node.index_within_parent;
41     }
42 
43     GumboParseFlags parseFlags()
44     {
45         return _node.parse_flags;
46     }
47 
48     Node[] children()
49     {
50         return _children;
51     }
52 
53     T findChild(T)(bool delegate(T) predicate)
54     {
55         foreach(child; children) {
56             T c = cast(T)child;
57             if(c && predicate(c))
58                 return c;
59 
60             if(T innerChild = child.findChild!T(predicate))
61                 return innerChild;
62         }
63         return null;
64     }
65 
66     T findChild(T)()
67     {
68         foreach(child; children) {
69             if(T c = cast(T)child)
70                 return c;
71 
72             if(T innerChild = child.findChild!T)
73                 return innerChild;
74         }
75         return null;
76     }
77 
78     T[] findChildren(T)(bool delegate(T) predicate)
79     {
80         T[] found;
81         foreach(child; children) {
82             T c = cast(T)child;
83             if(c && predicate(c))
84                 found ~= c;
85 
86             found ~= child.findChildren!T(predicate);
87         }
88         return found;
89     }
90 
91     T[] findChildren(T)()
92     {
93         T[] found;
94         foreach(child; children) {
95             if(T c = cast(T)child)
96                 found ~= c;
97 
98             found ~= child.findChildren!T;
99         }
100         return found;
101     }
102 
103     T[] mapChildren(T)(T delegate(Node) fun)
104     {
105         T[] vals;
106         foreach(child; children) {
107             vals ~= fun(child);
108         }
109         return vals;
110     }
111 
112     T[] flatMapChildren(T)(T[] delegate(Node) fun)
113     {
114         T[] vals;
115         foreach(child; children) {
116             vals ~= fun(child);
117         }
118         return vals;
119     }
120 
121     static Node fromCAPI(GumboNode * node)
122     {
123         if(!node)
124             return null;
125 
126         switch(cast(Type)node.type) {
127             case Type.document:
128                 return new Document(node);
129             case Type.element:
130                 return new Element(node);
131             case Type.text:
132                 return new Text(node);
133             case Type.cdata:
134                 return new Text(node);
135             case Type.comment:
136                 return new Text(node);
137             case Type.whitespace:
138                 return new Text(node);
139             default:
140                 return null;
141         }
142     }
143 }
144 
145 class Attribute {
146 private:
147     GumboAttribute * _attr;
148 
149 public:
150     this(GumboAttribute * attr)
151     {
152         _attr = attr;
153     }
154 
155     GumboAttributeNamespaceEnum attrNamespace()
156     {
157         return _attr.attr_namespace;
158     }
159 
160     string name()
161     {
162         return text(_attr.name);
163     }
164 
165     string originalName()
166     {
167         return text(_attr.original_name);
168     }
169 
170     string value()
171     {
172         return text(_attr.value);
173     }
174 
175     string originalValue()
176     {
177         return text(_attr.original_value);
178     }
179 
180     GumboSourcePosition nameStart()
181     {
182         return _attr.name_start;
183     }
184 
185     GumboSourcePosition nameEnd()
186     {
187         return _attr.name_end;
188     }
189 
190     GumboSourcePosition valueStart()
191     {
192         return _attr.value_start;
193     }
194 
195     GumboSourcePosition valueEnd()
196     {
197         return _attr.value_end;
198     }
199 }
200 
201 class Document : Node {
202 private:
203     GumboDocument * _doc;
204 
205     string _name;
206     string _publicIdentifier;
207     string _systemIdentifier;
208 
209 public:
210     this(GumboNode * node)
211     {
212         super(node);
213 
214         _doc = &node.v.document;
215 
216         for(uint i = 0; i < _doc.children.length; i++) {
217             _children ~= Node.fromCAPI(cast(GumboNode*)_doc.children.data[i]);
218         }
219 
220         _name = text(_doc.name);
221         _publicIdentifier = text(_doc.public_identifier);
222         _systemIdentifier = text(_doc.system_identifier);
223     }
224 
225     bool hasDoctype()
226     {
227         return _doc.has_doctype;
228     }
229 
230     string name()
231     {
232         return _name;
233     }
234 
235     string publicIdentifier()
236     {
237         return _publicIdentifier;
238     }
239 
240     string systemIdentifier()
241     {
242         return _systemIdentifier;
243     }
244 
245     GumboQuirksModeEnum docTypeQuirksMode()
246     {
247         return _doc.doc_type_quirks_mode;
248     }
249 }
250 
251 class Element : Node {
252     enum Tag {
253         html, head, title, base, link, meta, style, script, noscript, body_,
254         section, nav, article, aside, h1, h2, h3, h4, h5, h6, hgroup, header,
255         footer, address, p, hr, pre, blockquote, ol, ul, li, dl, dt, dd,
256         figure, figcaption, div, a, em, strong, small, s, cite, q, dfn, abbr,
257         time, code, var, samp, kbd, sub, sup, i, b, mark, ruby, rt, rp, bdi,
258         bdo, span, br, wbr, ins, del, image, img, iframe, embed, object, param,
259         video, audio, source, track, canvas, map, area, math, mi, mo, mn, ms,
260         mtext, mglyph, malignmark, annotationXML, svg, foreignObject, desc,
261         table, caption, colgroup, col, tbody, thead, tfoot, tr, td, th, form,
262         fieldset, legend, label, input, button, select, datalist, optgroup,
263         option, textarea, keygen, output, progress, meter, details, summary,
264         command, menu, applet, acronym, bgsound, dir, frame, frameset,
265         noFrames, isIndex, listing, xmp, nextId, noEmbed, plainText, rb,
266         strike, baseFont, big, blink, center, font, marquee, multicol, nobr,
267         spacer, tt, u, unknown, last,
268     };
269 
270     GumboElement *    _element;
271     Attribute[string] _attributes;
272 
273 public:
274     this(GumboNode * node)
275     {
276         super(node);
277         _element = &node.v.element;
278 
279         for(uint i = 0; i < _element.children.length; i++) {
280             GumboNode * child = cast(GumboNode*)_element.children.data[i];
281             _children ~= Node.fromCAPI(child);
282         }
283 
284 
285         for(uint i = 0; i < _element.attributes.length; i++) {
286             Attribute attr = new Attribute(cast(GumboAttribute*)_element.attributes.data[i]);
287             _attributes[attr.name()] = attr;
288         }
289     }
290 
291     Attribute[string] attributes()
292     {
293         return _attributes;
294     }
295 
296     Attribute getAttribute(string name) {
297         if(name in _attributes)
298             return _attributes[name];
299         return null;
300     }
301 
302     Tag tag()
303     {
304         return cast(Tag)_element.tag;
305     }
306 
307     GumboNamespaceEnum tagNamespace()
308     {
309         return _element.tag_namespace;
310     }
311 
312     GumboStringPiece originalTag()
313     {
314         return _element.original_tag;
315     }
316 
317     GumboStringPiece originalEndTag()
318     {
319         return _element.original_end_tag;
320     }
321 
322     GumboSourcePosition startPos()
323     {
324         return _element.start_pos;
325     }
326 
327     GumboSourcePosition endPos()
328     {
329         return _element.end_pos;
330     }
331 }
332 
333 class Text : Node {
334 private:
335     GumboText * _gtext;
336     string _text;
337 
338 public:
339     this(GumboNode * node)
340     {
341         super(node);
342 
343         _gtext = &node.v.text;
344         _text = std.conv.text(_gtext.text);
345     }
346 
347     string text()
348     {
349         return _text;
350     }
351 
352     GumboStringPiece originalText()
353     {
354         return _gtext.original_text;
355     }
356 
357     GumboSourcePosition startPos()
358     {
359         return _gtext.start_pos;
360     }
361 }