1 module arrogant;
2 
3 import arrogant.c.modest;
4 
5 // Public enums & stuffs
6 public import arrogant.c.common;
7 
8 import std.traits;
9 import std.conv;
10 import std.typecons : Nullable, Flag, Yes, No, RefCounted, RefCountedAutoInitialize;
11 import std.traits;
12 import std..string : toStringz; 
13 
14 // To manage c memory correctly! (I hope so...)
15 template CObjectWrapper(T, alias dtor = null)
16 {
17    import std.typecons : RefCounted, RefCountedAutoInitialize;
18 
19    // This struct is refcounted!
20    struct ObjectWrapper
21    {
22       // These function are not used
23       @disable this(this);
24       void opAssign(T rhs) { assert(0, "ObjectWrapper: can't assign."); }
25 
26       this(T payload) { this.data = payload; }
27       ~this() { dtor(data); }
28 
29       private T data;
30    }
31 
32    private RefCounted!(ObjectWrapper, RefCountedAutoInitialize.no) payload;
33    private void initWrapper(T data) { payload = typeof(payload)(data); }
34 }
35 
36 /** Use this enum with `node.byAttribute()` search  */
37 enum AttributeSearchType
38 {
39    exact,            /// 
40    startsWith,       /// 
41    endsWith,         ///
42    contains,         ///
43    spaceSeparated,   ///
44    hypenSeparated    ///
45 }
46 
47 class ArrogantException : Exception 
48 {
49    this(uint err) 
50    { 
51       import std.conv : to; 
52       super("Arrogant exception: " ~ to!string(err)); 
53    }
54 }
55 
56 /** An html attribute of a tag */
57 struct Attribute
58 {
59    /** The attribute key */
60    @property auto key() 
61    {
62       
63       size_t length; 
64       auto k = myhtml_attribute_key(attribute, &length); 
65       return k[0..length].to!string;
66    }
67 
68    // The attribute value */
69    @property auto value() 
70    { 
71       size_t length; 
72       auto v = myhtml_attribute_value(attribute, &length); 
73       return v[0..length].to!string;
74    }
75 
76    @disable this();
77    
78    private this(myhtml_tree_attr_t* attr) { attribute = attr; }
79    private myhtml_tree_attr_t* attribute;
80 }
81 
82 /** A HTML Node */
83 struct Node
84 {
85    /** 
86    * Get the tag id for this node (ex: a, div, body, ...) 
87    * Examples:
88    * --------------------
89    * tree.body.tag.writeln(); // prints "body"
90    * --------------------
91    */
92    MyHtmlTagId  tagId() { return cast(MyHtmlTagId )myhtml_tree_node.tag_id; }
93    string  tag() { return tagId.to!string; } /// Ditto
94 
95    /** 
96    * "in" operator to check for an attribute inside a node 
97    * Examples:
98    * --------------------
99    * if ("href" in node) writeln("Link: ", node["href"]);
100    * --------------------
101    */ 
102    bool opBinaryRight(string op)(string key) if (op == "in")
103    {
104       auto attr = myhtml_attribute_by_key (myhtml_tree_node, key.toStringz, key.length);
105       return attr !is null;
106    }
107 
108    /** Read an attribute from node */
109    auto opIndex(string attribute) 
110    { 
111       Nullable!string value;
112       auto attr = myhtml_attribute_by_key (myhtml_tree_node, attribute.toStringz, attribute.length);
113 
114       if (attr !is null) 
115       {
116          size_t length; 
117          auto v = myhtml_attribute_value(attr, &length); 
118          return Nullable!string(v[0..length].to!string);
119       }
120 
121       return value;
122    }
123 
124    /** Write an attribute */
125    auto opIndexAssign(string value, string key) 
126    { 
127       removeAttribute(key);
128       myhtml_attribute_add (myhtml_tree_node, key.toStringz, key.length, value.toStringz, value.length, MyEncodingList.default_);
129       return value;
130    }
131 
132    auto opIndexAssign(typeof(null) value, string key) 
133    { 
134       removeAttribute(key);
135       myhtml_attribute_add (myhtml_tree_node, key.toStringz, key.length, null, 0, MyEncodingList.default_);
136       return value;
137    }
138 
139    /** 
140       Remove an attribute 
141       Returns: `true` if attribute exists `false` otherwise.
142    */
143    bool removeAttribute(string key)
144    {
145       auto attr = myhtml_attribute_by_key (myhtml_tree_node, key.toStringz, key.length);
146       if (attr !is null)
147       {
148          myhtml_attribute_delete(myhtml_node_tree(myhtml_tree_node), myhtml_tree_node, attr);
149          return true;
150       }
151 
152       return false;
153    }
154 
155    /** Create a new html node */
156    this(ref Tree tree, MyHtmlTagId  tag, MyHtmlNamespace ns = MyHtmlNamespace.html)
157    {
158       myhtml_tree_node = myhtml_node_create (
159          tree.myhtml_tree,
160          tag,
161          ns
162       );
163    }
164 
165    /** Remove node from tree and delete it */
166    void deleteNode() { myhtml_node_delete_recursive(myhtml_tree_node); }
167    
168    ///
169    Nullable!Node firstChild()
170    {
171       Nullable!Node ret;
172       auto val = myhtml_node_child(myhtml_tree_node);
173 
174       if (val !is null) ret = Node(val);
175       return ret;
176    }
177 
178    ///
179    Nullable!Node lastChild()
180    {
181       Nullable!Node ret;
182       auto val = myhtml_node_last_child(myhtml_tree_node);
183 
184       if (val !is null) ret = Node(val);
185       return ret;
186    }
187 
188    ///
189    Nullable!Node parent()
190    {
191       Nullable!Node ret;
192       auto val = myhtml_node_parent(myhtml_tree_node);
193 
194       if (val !is null) ret = Node(val);
195       return ret;
196    }
197 
198    ///
199    Nullable!Node next()
200    {
201       Nullable!Node ret;
202       auto val = myhtml_node_next(myhtml_tree_node);
203 
204       if (val !is null) ret = Node(val);
205       return ret;
206    }
207 
208    ///
209    Nullable!Node previous()
210    {
211       Nullable!Node ret;
212       auto val = myhtml_node_prev(myhtml_tree_node);
213 
214       if (val !is null) ret = Node(val);
215       return ret;
216    }
217 
218    /*
219       Get children of this node.
220       Returns: a lazy `NodeRange`. If you want to edit children, convert to array before.
221    */
222    auto children()
223    {
224       struct ChildrenRange
225       {
226          this(myhtml_tree_node_t *n) { parent = n; current = myhtml_node_child(parent); }
227 
228          @property empty() {  return current == null; }
229          auto front() { return Node(current); }
230          void popFront() { current = myhtml_node_next(current); }
231 
232          private:
233          myhtml_tree_node_t *current;
234          myhtml_tree_node_t *parent;
235       }
236 
237       return ChildrenRange(myhtml_tree_node);
238    }
239 
240    /** All node's attributes 
241       Returns: a lazy range of Attributes
242    */
243    auto attributes()
244    {
245       struct AttributesRange
246       {
247          this(myhtml_tree_node_t *n) { parent = n; current = myhtml_node_attribute_first(parent); }
248 
249          @property empty() {  return current == null; }
250          auto front() { return Attribute(current); }
251          void popFront() { current = myhtml_attribute_next(current); }
252 
253          private:
254          myhtml_tree_attr_t *current;
255          myhtml_tree_node_t *parent;
256       }
257 
258       return AttributesRange(myhtml_tree_node);
259    }
260 
261    /** Get the text of this node. Only for text nodes! */
262    @property string text()
263    {
264       return myhtml_node_text(myhtml_tree_node, null).to!string;
265    }
266 
267    /** Set the text of this node. Only for text nodes! */
268    @property void text(string s)
269    {
270       myhtml_node_text_set(myhtml_tree_node, s.toStringz, s.length, MyEncodingList.default_);
271    }
272 
273    /** Return node html representation */
274    string toString() 
275    {
276       return innerHTML();
277    }
278 
279    /// Ditto
280    @property string innerHTML()
281    {
282       mycore_string_raw_t str_raw;
283       mycore_string_raw_clean_all(&str_raw);
284       scope(exit) mycore_string_raw_destroy(&str_raw, false);
285 
286       if(myhtml_serialization_tree_buffer(myhtml_tree_node, &str_raw)) return "";
287       return str_raw.data[0..str_raw.length].to!string;
288    }
289 
290    /** Set node html. All children will be deleted. */
291    @property void innerHTML(string s)
292    {
293       // Create a new tree to parse fragment
294       auto tree = Tree(myhtml_tree_get_myhtml(myhtml_node_tree(myhtml_tree_node)));
295       tree.parseFragment(s);
296 
297       // Clone fragment and move to current tree
298       auto cloned = tree.first.clone(myhtml_node_tree(myhtml_tree_node));
299 
300       // Delete all children!
301       import std.array : array;
302       foreach(node; children.array)
303          node.deleteNode;
304 
305       // Append new child
306       appendChild(cloned);
307    }
308 
309    /** Set node innerText. All children will be deleted. */
310    @property void innerText(string s)
311    {
312       import std.array : array;
313       
314       // Create a text node
315       auto text_node = myhtml_node_create (
316          myhtml_node_tree(myhtml_tree_node),
317          MyHtmlTagId ._text,
318          MyHtmlNamespace.html
319       );
320 
321       Node nodeToAppend = Node(text_node);
322       nodeToAppend.text = s;
323 
324       // Delete all children!
325       foreach(node; children.array)
326          node.deleteNode;
327 
328       // Append new child
329       appendChild(nodeToAppend);
330    }
331 
332    ///
333    @property string innerText()
334    {
335 
336       struct CallbackPayload
337       {
338          bool insideTag;
339          string data;
340       }
341 
342       CallbackPayload payload;
343 
344       extern(C) mystatus_t callback(const char* buffer, size_t size, void* ctx)
345       {
346          import std.array : replace;
347          if (size == 0) return 0;
348 
349          auto pl = cast(CallbackPayload*) ctx;
350 
351          if (buffer[0] == '<') pl.insideTag = true; 
352          else if (buffer[0] == '>') pl.insideTag = false; 
353          else if (!pl.insideTag) pl.data ~= buffer[0..size].replace("&amp;", "&").replace("&gt;", ">").replace("&lt;", "<").replace("&nbsp;","\n"); 
354 
355          return 0;
356       }
357 
358       if(myhtml_serialization_tree_callback(myhtml_tree_node, &callback, &payload)) return "";
359       return payload.data;
360 
361    }
362 
363    /** 
364       Create a copy of this node owned by another tree
365       Returns: a `Node` owned by `destination`
366    */
367    Node clone(Tree destination) { return clone(destination.myhtml_tree); }
368    
369    // Create a copy of this node
370    Node clone() { return clone(myhtml_node_tree(myhtml_tree_node)); }
371 
372    ///
373    bool isSelfClosing() { return myhtml_node_is_close_self(myhtml_tree_node); }
374    
375    ///
376    bool isVoidElement() { return myhtml_node_is_void_element(myhtml_tree_node); }
377    
378    /** Detach node from tree without destroying */
379    void detach() { myhtml_node_remove(myhtml_tree_node); }
380 
381    ///
382    void appendChild(Node n) { n.detach(); myhtml_node_append_child(myhtml_tree_node, n.myhtml_tree_node); }
383    
384    ///
385    void insertBefore(Node n) { myhtml_node_insert_before(myhtml_tree_node, n.myhtml_tree_node); }
386    
387    ///
388    void insertAfter(Node n) { myhtml_node_insert_after(myhtml_tree_node, n.myhtml_tree_node); }
389    
390    ///
391    void insertToAppropriatePlace(Node n) { myhtml_node_insert_to_appropriate_place(myhtml_tree_node, n.myhtml_tree_node); }
392 
393    /** 
394       Search children using a css 3.1 selector 
395       Returns: a lazy range of nodes
396       See_Also: byAttribute, byAttributeKey, byTagName, byClass, byId
397    */
398    auto byCssSelector(string selector)
399    {
400       import std.exception : enforce;
401 
402       auto mycss  = mycss_create();
403       mycss_init(mycss);
404      
405       auto entry  = mycss_entry_create();
406       mycss_entry_init(mycss, entry);
407 
408       auto finder = modest_finder_create_simple();
409 
410       mystatus_t out_status;
411       mycss_selectors_list_t *list = mycss_selectors_parse
412       (
413          mycss_entry_selectors(entry),
414          MyEncodingList.default_,
415          selector.toStringz, selector.length,
416          &out_status
417       );
418 
419       enforce(list != null && ((list.flags & mycss_selectors_flags.MyCSS_SELECTORS_FLAGS_SELECTOR_BAD) == 0), "Can't compile css selector: " ~ selector);
420 
421       myhtml_collection_t* collection = null;
422      
423       modest_finder_by_selectors_list(finder, myhtml_tree_node, list, &collection);
424 
425       // Free resources!
426       mycss_selectors_list_destroy(mycss_entry_selectors(entry), list, true);
427       modest_finder_destroy(finder, true);
428       mycss_entry_destroy(entry, true);
429       mycss_destroy(mycss, true);
430 
431       return NodeRange (collection);
432    }
433 
434    /** 
435       Search children by tag name
436       Returns: a lazy range of nodes
437       See_Also: byAttribute, byAttributeKey, byClass, byId, byCssSelector
438    */
439    auto byTagName(MyHtmlTagId  name)
440    {
441       mystatus_t status;
442       myhtml_collection_t* myhtml_collection = myhtml_collection_create(0, null);
443       auto collection = NodeRange (myhtml_get_nodes_by_tag_id_in_scope(myhtml_node_tree(myhtml_tree_node), myhtml_collection, myhtml_tree_node, name, &status));
444       
445       if (MYHTML_FAILED(status)) throw new ArrogantException(status);
446 
447       return collection;
448    }
449 
450    /// Ditto
451    auto byTagName(string name)
452    {
453       mystatus_t status;
454       myhtml_collection_t* myhtml_collection = myhtml_collection_create(0, null);
455       auto collection = NodeRange (myhtml_get_nodes_by_name_in_scope(myhtml_node_tree(myhtml_tree_node), myhtml_collection, myhtml_tree_node, name.toStringz, name.length, &status));
456       
457       if (MYHTML_FAILED(status)) throw new ArrogantException(status);
458 
459       return collection;
460    }
461 
462     /** 
463       Search children by class (space separated)
464       Returns: a lazy range of nodes
465       See_Also: byAttribute, byAttributeKey, byTagName, byId, byCssSelector
466    */
467    auto byClass(string className) { return byAttribute!(AttributeSearchType.spaceSeparated)("class", className);}
468    
469     /** 
470       Search children by id
471       Returns: a lazy range of nodes
472       See_Also: byAttribute, byAttributeKey, byTagName, byClass, byCssSelector
473    */
474    auto byId(string id) { return byAttribute("id", id);}
475    
476    /** 
477       Search children with a specified attribute
478       Returns: a lazy range of nodes
479       See_Also: byAttribute, byTagName, byClass, byId, byCssSelector
480    */
481    auto byAttributeKey(string name)
482    {
483       mystatus_t status;
484       myhtml_collection_t* myhtml_collection = myhtml_collection_create(0, null);
485       auto collection = NodeRange (myhtml_get_nodes_by_attribute_key(myhtml_node_tree(myhtml_tree_node), myhtml_collection, myhtml_tree_node, name.toStringz, name.length, &status));
486       if (MYHTML_FAILED(status)) throw new ArrogantException(status);
487 
488       return collection;
489    }
490 
491    /** 
492       Search children by tag attribute key/val. 
493       Returns: a lazy range of nodes
494       See_Also: byAttributeKey, byTagName, byClass, byId, byCssSelector
495    */
496    auto byAttribute(AttributeSearchType st = AttributeSearchType.exact, Flag!"caseInsensitive" caseInsensitive = No.caseInsensitive)(string key, string value)
497    {
498       mystatus_t status;
499       typeof(&myhtml_get_nodes_by_attribute_value) callback;
500 
501       final switch(st)
502       {
503          case AttributeSearchType.exact: callback = &myhtml_get_nodes_by_attribute_value; break;
504          case AttributeSearchType.startsWith: callback = &myhtml_get_nodes_by_attribute_value_begin; break;
505          case AttributeSearchType.endsWith: callback = &myhtml_get_nodes_by_attribute_value_end; break;
506          case AttributeSearchType.contains: callback = &myhtml_get_nodes_by_attribute_value_contain; break;
507          case AttributeSearchType.spaceSeparated : callback = &myhtml_get_nodes_by_attribute_value_whitespace_separated; break;
508          case AttributeSearchType.hypenSeparated: callback = &myhtml_get_nodes_by_attribute_value_hyphen_separated; break;
509       }
510 
511       myhtml_collection_t* myhtml_collection = myhtml_collection_create(0, null);
512 
513       auto collection = NodeRange 
514       (
515          callback
516          (
517             myhtml_node_tree(myhtml_tree_node),
518             myhtml_collection,
519             myhtml_tree_node,
520             caseInsensitive == Yes.caseInsensitive,
521             key.toStringz, key.length,  value.toStringz, value.length, 
522             &status
523          )
524       );
525 
526       if (MYHTML_FAILED(status)) throw new ArrogantException(status);
527       return collection;
528    }
529 
530    private:
531 
532    Node clone(myhtml_tree_t* destination)
533    {
534 
535       struct CopyQueueItem
536       {
537          myhtml_tree_node_t* destParent;  // Where node will be appended
538          myhtml_tree_node_t* toCopy;      // The node to copy
539       }
540       
541       import std.container.dlist;
542       auto copyQueue = DList!CopyQueueItem();
543 
544       // Clone a single node without children
545       myhtml_tree_node_t* cloneNode(myhtml_tree_t* _destination, myhtml_tree_node_t* _node)
546       {
547          // Create a new node rooted on destination treee
548          auto ret = myhtml_node_create (
549             _destination,
550             _node.tag_id,
551             _node.ns
552          );
553 
554          // Copy text if present
555          {
556             size_t textLength;
557             auto text = myhtml_node_text(_node, &textLength);
558             myhtml_node_text_set(ret, text, textLength, MyEncodingList.default_);
559          }
560 
561          // Clone attributes
562          for (auto attribute = myhtml_node_attribute_first(_node); attribute != null; attribute = myhtml_attribute_next(attribute))
563          {
564             size_t keyLength, valueLength; 
565             auto k = myhtml_attribute_key(attribute, &keyLength); 
566             auto v = myhtml_attribute_value(attribute, &valueLength); 
567             myhtml_attribute_add (ret, k, keyLength, v, valueLength, MyEncodingList.default_);
568          }
569 
570          // Return the filled node.
571          return ret;
572       }
573 
574       // Clone the root
575       auto destinationRoot = cloneNode(destination, myhtml_tree_node);
576       auto currentNode = myhtml_tree_node;
577       auto currentDestNode = destinationRoot;
578     
579       while(true)
580       {
581          // Add children of current node to queue
582          for (auto child = myhtml_node_child(currentNode); child != null; child = myhtml_node_next(child))
583             copyQueue.insertBack(CopyQueueItem(currentDestNode, child));
584 
585          if (copyQueue.empty) break;
586 
587          // Get the first item in queue
588          auto destParent = copyQueue.front.destParent;
589          currentNode = copyQueue.front.toCopy;
590 
591          // Remove first element of list
592          copyQueue.removeFront();
593          
594          // Clone the children and add to new parent
595          currentDestNode = cloneNode(destination, currentNode);
596          myhtml_node_append_child(destParent, currentDestNode);
597       }
598 
599       return Node(destinationRoot);
600    }
601    
602    this(myhtml_tree_node_t *node) { myhtml_tree_node = node; }
603    myhtml_tree_node_t* myhtml_tree_node;
604 }
605 
606 /** A lazy range of nodes, usually returned by a search */
607 struct NodeRange
608 {
609    // Refcount stuff!
610    mixin CObjectWrapper!(myhtml_collection_t*, data => myhtml_collection_destroy(data) );
611 
612    Node opIndex(size_t i)
613    {
614       return Node(myhtml_collection.list[i]);
615    }
616    
617    size_t length() { if (myhtml_collection) return myhtml_collection.length; return 0; }
618 
619    @property Node front() { if (empty) assert(0, "Can't read nodes from an empty collection"); return this[idx]; }
620    @property bool empty() { return idx >= length(); }
621 
622    void popFront() { idx++; }
623 
624    @property Nullable!Node frontOrNull() 
625    {
626       Nullable!Node ret;
627       if (!empty) ret = front;
628       return ret;
629    } 
630 private:
631 
632    this(myhtml_collection_t* collection)
633    {
634       myhtml_collection = collection;
635       initWrapper(collection);
636    }
637 
638    myhtml_collection_t* myhtml_collection;
639 
640    size_t idx = 0;
641 }
642 
643 /** A html tree */
644 struct Tree
645 {
646    mixin CObjectWrapper!(myhtml_tree_t*, tree => myhtml_tree_destroy(tree));
647 
648    /// See: `Node.byXXXX`
649    auto byClass(string className) { return document.byClass(className); }
650 
651    
652    /// Ditto
653    auto byId(string id) { return document.byId(id);}
654 
655 
656    /// Ditto
657    auto byCssSelector(string selector) { return document.byCssSelector(selector); }
658 
659    /// Ditto
660    auto byTagName(MyHtmlTagId  name) { return document.byTagName(name); }
661 
662    /// Ditto
663    auto byTagName(string name) { return document.byTagName(name); }
664 
665    /// Ditto
666    auto byAttributeKey(string name) { return document.byAttributeKey(name); }
667 
668    /// Ditto
669    auto byAttribute(AttributeSearchType st = AttributeSearchType.exact, Flag!"caseInsensitive" caseInsensitive = No.caseInsensitive)(string key, string value)
670    {
671       return document.byAttribute!(st, caseInsensitive)(key, value);
672    }
673 
674    /// The document root
675    Nullable!Node document()
676    {
677       Nullable!Node node;
678       auto nodePtr = myhtml_tree_get_document(myhtml_tree);
679 
680       if (nodePtr !is null) node = Node(nodePtr);
681 
682       return node; 
683    }
684 
685    /// The html node
686    Nullable!Node html()
687    {
688       Nullable!Node node;
689       auto nodePtr = myhtml_tree_get_node_html(myhtml_tree);
690 
691       if (nodePtr !is null) node = Node(nodePtr);
692 
693       return node; 
694    }
695 
696    /// The head node
697    Nullable!Node head()
698    {
699       Nullable!Node node;
700       auto nodePtr = myhtml_tree_get_node_head(myhtml_tree);
701 
702       if (nodePtr !is null) node = Node(nodePtr);
703 
704       return node; 
705    }
706 
707    /// The body node
708    Nullable!Node body()
709    {
710       Nullable!Node node;
711       auto nodePtr = myhtml_tree_get_node_body(myhtml_tree);
712 
713       if (nodePtr !is null) node = Node(nodePtr);
714 
715       return node; 
716    }
717 
718    /// Return the first node
719    Nullable!Node first()
720    {
721       Nullable!Node node;
722       auto nodePtr = myhtml_node_first(myhtml_tree);
723 
724       if (nodePtr !is null) node = Node(nodePtr);
725 
726       return node; 
727    }
728 
729    string toString() { Node tmp = first(); return tmp.toString(); }
730 
731 private:
732    
733    
734    void parse(T)(T html, MyEncodingList encoding = MyEncodingList.default_) if (isSomeString!T)
735    {
736       auto status = myhtml_parse(myhtml_tree, encoding, html.toStringz, html.length);
737       if (MYHTML_FAILED(status)) throw new ArrogantException(status);
738    }
739 
740    void parseFragment(T)(T html, MyHtmlTagId  wrap = MyHtmlTagId .div, MyEncodingList encoding = MyEncodingList.default_, MyHtmlNamespace ns = MyHtmlNamespace.html) if (isSomeString!T)
741    {
742       auto status = myhtml_parse_fragment (
743          myhtml_tree,
744          encoding,
745          html.toStringz, html.length,
746          wrap,
747          ns
748       );
749       if (MYHTML_FAILED(status)) throw new ArrogantException(status);
750    }
751    
752    this(ref Arrogant parent) { this(parent.myhtml); }
753    
754    this(myhtml_t* parent)
755    {
756       myhtml_tree = myhtml_tree_create();
757       auto status = myhtml_tree_init(myhtml_tree, parent);
758       initWrapper(myhtml_tree);
759 
760       if (MYHTML_FAILED(status)) throw new ArrogantException(status);
761    }
762 
763    @disable this();
764    myhtml_tree_t* myhtml_tree;
765 }
766 
767 struct Arrogant
768 {
769    mixin CObjectWrapper!(myhtml_t*, myhtml => myhtml_destroy(myhtml));
770 
771    ///
772    this(MyHtmlOptions options, size_t threadCount = 1, size_t queueSize = 0) 
773    { 
774       initArrogant(options, threadCount, queueSize);
775    }
776 
777    /// Parse a html document
778    Tree parse(T)(T html, MyEncodingList encoding = MyEncodingList.default_) if (isSomeString!T)
779    {
780       if (!isInited) initArrogant();
781       Tree tree = Tree(myhtml);
782       tree.parse(html, encoding);
783       return tree;
784    }
785 
786    /// Parse a html fragment
787    Tree parseFragment(T)(T html, MyHtmlTagId  wrap = MyHtmlTagId .div, MyEncodingList encoding = MyEncodingList.default_, MyHtmlNamespace ns = MyHtmlNamespace.html,) if (isSomeString!T)
788    {
789       if (!isInited) initArrogant();
790       Tree tree = Tree(myhtml);
791       tree.parseFragment(html, wrap, encoding, ns);
792       return tree;
793    }
794 
795    private:
796 
797    void initArrogant(MyHtmlOptions options = MyHtmlOptions.default_, size_t threadCount = 1, size_t queueSize = 0)
798    {
799       if (isInited) return;
800       isInited = true;
801 
802       myhtml = myhtml_create(); 
803       auto status = myhtml_init(myhtml, options, threadCount, queueSize);
804       initWrapper(myhtml);
805 
806       if (MYHTML_FAILED(status))
807          throw new ArrogantException(status);
808    }
809 
810    bool isInited = false;
811    myhtml_t* myhtml;
812 }