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 }