diff --git a/bmp.d b/bmp.d
index 5837d8e..61f6aec 100644
--- a/bmp.d
+++ b/bmp.d
@@ -555,6 +555,11 @@ void main() {
}
+/
+/++
+ Writes a bitmap file to a delegate, byte by byte, with data from the given image.
+
+ If `prependFileHeader` is `true`, it will add the bitmap file header too.
++/
void writeBmpIndirect(MemoryImage img, scope void delegate(ubyte) fwrite, bool prependFileHeader) {
void write4(uint what){
diff --git a/dom.d b/dom.d
index 0dc9b6c..d03ed24 100644
--- a/dom.d
+++ b/dom.d
@@ -91,14 +91,58 @@ bool isConvenientAttribute(string name) {
*/
-/// The main document interface, including a html parser.
+/++
+ The main document interface, including a html or xml parser.
+
+ There's three main ways to create a Document:
+
+ If you want to parse something and inspect the tags, you can use the [this|constructor]:
+ ---
+ // create and parse some HTML in one call
+ auto document = new Document("");
+
+ // or some XML
+ auto document = new Document(" element
- /// instead of inner text)
+ /++
+ Family of convenience functions to quickly add a tag with some text or
+ other relevant info (for example, it's a src for an
element
+ instead of inner text). They forward to [Element.make] then calls [appendChild].
+
+ ---
+ div.addChild("span", "hello there");
+ div.addChild("div", Html("
children of the div
")); + --- + +/ Element addChild(string tagName, string childInfo = null, string childInfo2 = null) in { assert(tagName !is null); @@ -1715,50 +1804,12 @@ class Element : DomParent { return appendChild(e); } - /// Another convenience function. Adds a child directly after the current one, returning - /// the new child. - /// - /// Between this, addChild, and parentNode, you can build a tree as a single expression. - Element addSibling(string tagName, string childInfo = null, string childInfo2 = null) - in { - assert(tagName !is null); - assert(parentNode !is null); - } - out(e) { - assert(e.parentNode is this.parentNode); - assert(e.parentDocument is this.parentDocument); - } - do { - auto e = Element.make(tagName, childInfo, childInfo2); - return parentNode.insertAfter(this, e); - } - - /// - Element addSibling(Element e) { - return parentNode.insertAfter(this, e); - } - - /// + /// ditto Element addChild(Element e) { return this.appendChild(e); } - /// Convenience function to append text intermixed with other children. - /// For example: div.addChildren("You can visit my website by ", new Link("mysite.com", "clicking here"), "."); - /// or div.addChildren("Hello, ", user.name, "!"); - - /// See also: appendHtml. This might be a bit simpler though because you don't have to think about escaping. - void addChildren(T...)(T t) { - foreach(item; t) { - static if(is(item : Element)) - appendChild(item); - else static if (is(isSomeString!(item))) - appendText(to!string(item)); - else static assert(0, "Cannot pass " ~ typeof(item).stringof ~ " to addChildren"); - } - } - - ///. + /// ditto Element addChild(string tagName, Element firstChild, string info2 = null) in { assert(firstChild !is null); @@ -1778,7 +1829,7 @@ class Element : DomParent { return e; } - /// + /// ditto Element addChild(string tagName, in Html innerHtml, string info2 = null) in { } @@ -1795,13 +1846,51 @@ class Element : DomParent { } - /// . + /// Another convenience function. Adds a child directly after the current one, returning + /// the new child. + /// + /// Between this, addChild, and parentNode, you can build a tree as a single expression. + /// See_Also: [addChild] + Element addSibling(string tagName, string childInfo = null, string childInfo2 = null) + in { + assert(tagName !is null); + assert(parentNode !is null); + } + out(e) { + assert(e.parentNode is this.parentNode); + assert(e.parentDocument is this.parentDocument); + } + do { + auto e = Element.make(tagName, childInfo, childInfo2); + return parentNode.insertAfter(this, e); + } + + /// ditto + Element addSibling(Element e) { + return parentNode.insertAfter(this, e); + } + + /// Convenience function to append text intermixed with other children. + /// For example: div.addChildren("You can visit my website by ", new Link("mysite.com", "clicking here"), "."); + /// or div.addChildren("Hello, ", user.name, "!"); + /// See also: appendHtml. This might be a bit simpler though because you don't have to think about escaping. + void addChildren(T...)(T t) { + foreach(item; t) { + static if(is(item : Element)) + appendChild(item); + else static if (is(isSomeString!(item))) + appendText(to!string(item)); + else static assert(0, "Cannot pass " ~ typeof(item).stringof ~ " to addChildren"); + } + } + + /// Appends the list of children to this element. void appendChildren(Element[] children) { foreach(ele; children) appendChild(ele); } - ///. + /// Removes this element form its current parent and appends it to the given `newParent`. void reparent(Element newParent) in { assert(newParent !is null); @@ -2017,7 +2106,7 @@ class Element : DomParent { parent_ = doc; } - ///. + /// Returns the parent node in the tree this element is attached to. inout(Element) parentNode() inout { if(parent_ is null) return null; @@ -2069,6 +2158,8 @@ class Element : DomParent { On February 8, 2021, the `selfClosedElements` parameter was added. Previously, it used a private immutable global list for HTML. It still defaults to the same list, but you can change it now via the parameter. + See_Also: + [addChild], [addSibling] +/ static Element make(string tagName, string childInfo = null, string childInfo2 = null, const string[] selfClosedElements = htmlSelfClosedElements) { bool selfClosed = tagName.isInArray(selfClosedElements); @@ -2168,6 +2259,7 @@ class Element : DomParent { return e; } + /// ditto static Element make(string tagName, in Html innerHtml, string childInfo2 = null) { // FIXME: childInfo2 is ignored when info1 is null auto m = Element.make(tagName, "not null"[0..0], childInfo2); @@ -2175,6 +2267,7 @@ class Element : DomParent { return m; } + /// ditto static Element make(string tagName, Element child, string childInfo2 = null) { auto m = Element.make(tagName, cast(string) null, childInfo2); m.appendChild(child); @@ -2235,13 +2328,13 @@ class Element : DomParent { return children.length ? children[0] : null; } - /// + /// Returns the last child of the element, or null if it has no children. Remember, text nodes are children too. @property Element lastChild() { return children.length ? children[$ - 1] : null; } - /// UNTESTED - /// the next element you would encounter if you were reading it in the source + // FIXME UNTESTED + /// the next or previous element you would encounter if you were reading it in the source. May be a text node or other special non-tag object if you enabled them. Element nextInSource() { auto n = firstChild; if(n is null) @@ -2256,7 +2349,6 @@ class Element : DomParent { return n; } - /// UNTESTED /// ditto Element previousInSource() { auto p = previousSibling; @@ -2270,12 +2362,25 @@ class Element : DomParent { return p; } - ///. + /++ + Returns the next or previous sibling that is not a text node. Please note: the behavior with comments is subject to change. Currently, it will return a comment or other nodes if it is in the tree (if you enabled it with [Document.enableAddingSpecialTagsToDom] or [Document.parseSawComment]) and not if you didn't, but the implementation will probably change at some point to skip them regardless. + + Equivalent to [previousSibling]/[nextSibling]("*"). + + Please note it may return `null`. + +/ @property Element previousElementSibling() { return previousSibling("*"); } - ///. + /// ditto + @property Element nextElementSibling() { + return nextSibling("*"); + } + + /++ + Returns the next or previous sibling matching the `tagName` filter. The default filter of `null` will return the first sibling it sees, even if it is a comment or text node, or anything else. A filter of `"*"` will match any tag with a name. Otherwise, the string must match the [tagName] of the sibling you want to find. + +/ @property Element previousSibling(string tagName = null) { if(this.parentNode is null) return null; @@ -2292,12 +2397,7 @@ class Element : DomParent { return ps; } - ///. - @property Element nextElementSibling() { - return nextSibling("*"); - } - - ///. + /// ditto @property Element nextSibling(string tagName = null) { if(this.parentNode is null) return null; @@ -2324,8 +2424,11 @@ class Element : DomParent { } - /// Gets the nearest node, going up the chain, with the given tagName - /// May return null or throw. + /++ + Gets the nearest node, going up the chain, with the given tagName + May return null or throw. The type `T` will specify a subclass like + [Form], [Table], or [Link], which it will cast for you when found. + +/ T getParent(T = Element)(string tagName = null) if(is(T : Element)) { if(tagName is null) { static if(is(T == Form)) @@ -2353,7 +2456,9 @@ class Element : DomParent { return t; } - ///. + /++ + Searches this element and the tree of elements under it for one matching the given `id` attribute. + +/ Element getElementById(string id) { // FIXME: I use this function a lot, and it's kinda slow // not terribly slow, but not great. @@ -2384,12 +2489,6 @@ class Element : DomParent { return null; } - /// a more standards-compliant alias for getElementsBySelector - @scriptable - Element[] querySelectorAll(string selector) { - return getElementsBySelector(selector); - } - /// If the element matches the given selector. Previously known as `matchesSelector`. @scriptable bool matches(string selector) { @@ -2455,8 +2554,11 @@ class Element : DomParent { There should be two functions: given element, does it match the selector? and given a selector, give me all the elements + + The name `getElementsBySelector` was the original name, written back before the name `querySelector` was standardized (this library is older than you might think!), but they do the same thing.. */ - Element[] getElementsBySelector(string selector) { + @scriptable + Element[] querySelectorAll(string selector) { // FIXME: this function could probably use some performance attention // ... but only mildly so according to the profiler in the big scheme of things; probably negligible in a big app. @@ -2471,13 +2573,22 @@ class Element : DomParent { return ret; } - /// . + /// ditto + alias getElementsBySelector = querySelectorAll; + + /++ + Returns child elements that have the given class name or tag name. + + Please note the standard specifies this should return a live node list. This means, in Javascript for example, if you loop over the value returned by getElementsByTagName and getElementsByClassName and remove the elements, the length of the list will decrease. When I implemented this, I figured that was more trouble than it was worth and returned a plain array instead. By the time I had the infrastructure to make it simple, I didn't want to do the breaking change. + + So these is incompatible with Javascript in the face of live dom mutation and will likely remain so. + +/ Element[] getElementsByClassName(string cn) { // is this correct? return getElementsBySelector("." ~ cn); } - ///. + /// ditto Element[] getElementsByTagName(string tag) { if(parentDocument && parentDocument.loose) tag = tag.toLower(); @@ -2568,7 +2679,7 @@ class Element : DomParent { } /** - Gets the class attribute's contents. Returns + Gets or sets the class attribute's contents. Returns an empty string if it has no class. */ @property string className() const { @@ -2578,7 +2689,7 @@ class Element : DomParent { return c; } - ///. + /// ditto @property Element className(string c) { setAttribute("class", c); return this; @@ -2626,12 +2737,7 @@ class Element : DomParent { /** Returns the element's children. */ - @property const(Element[]) childNodes() const { - return children; - } - - /// Mutable version of the same - @property Element[] childNodes() { // FIXME: the above should be inout + @property inout(Element[]) childNodes() inout { return children; } @@ -2700,7 +2806,7 @@ class Element : DomParent { // the next few methods are for implementing interactive kind of things private CssStyle _computedStyle; - /// Don't use this. + /// Don't use this. It can try to parse out the style element but it isn't complete and if I get back to it, it won't be for a while. @property CssStyle computedStyle() { if(_computedStyle is null) { auto style = this.getAttribute("style"); @@ -2779,13 +2885,17 @@ class Element : DomParent { children = null; } - /// History: added June 13, 2020 + /++ + Adds a sibling element before or after this one in the dom. + + History: added June 13, 2020 + +/ Element appendSibling(Element e) { parentNode.insertAfter(this, e); return e; } - /// History: added June 13, 2020 + /// ditto Element prependSibling(Element e) { parentNode.insertBefore(this, e); return e; @@ -2803,6 +2913,7 @@ class Element : DomParent { Element appendChild(Element e) in { assert(e !is null); + assert(e !is this); } out (ret) { assert((cast(DocumentFragment) this !is null) || (e.parentNode is this), e.toString);// e.parentNode ? e.parentNode.toString : "null"); @@ -2973,7 +3084,9 @@ class Element : DomParent { } - ///. + /++ + Inserts a child under this element after the element `where`. + +/ void insertChildAfter(Element child, Element where) in { assert(child !is null); @@ -3167,7 +3280,9 @@ class Element : DomParent { rs.parentNode = this; } - ///. + /++ + Replaces the element `find`, which must be a child of `this`, with the element `replace`, which must have no parent. + +/ Element replaceChild(Element find, Element replace) in { assert(find !is null); @@ -3700,8 +3815,13 @@ class Element : DomParent { // I moved these from Form because they are generally useful. // Ideally, I'd put them in arsd.html and use UFCS, but that doesn't work with the opDispatch here. - /// Tags: HTML, HTML5 // FIXME: add overloads for other label types... + /++ + Adds a form field to this element, normally a `` but `type` can also be `"textarea"`. + + This is fairly html specific and the label uses my style. I recommend you view the source before you use it to better understand what it does. + +/ + /// Tags: HTML, HTML5 Element addField(string label, string name, string type = "text", FormFieldOptions fieldOptions = FormFieldOptions.none) { auto fs = this; auto i = fs.addChild("label"); @@ -3727,6 +3847,7 @@ class Element : DomParent { return i; } + /// ditto Element addField(Element label, string name, string type = "text", FormFieldOptions fieldOptions = FormFieldOptions.none) { auto fs = this; auto i = fs.addChild("label"); @@ -3746,10 +3867,12 @@ class Element : DomParent { return i; } + /// ditto Element addField(string label, string name, FormFieldOptions fieldOptions) { return addField(label, name, "text", fieldOptions); } + /// ditto Element addField(string label, string name, string[string] options, FormFieldOptions fieldOptions = FormFieldOptions.none) { auto fs = this; auto i = fs.addChild("label"); @@ -3764,6 +3887,7 @@ class Element : DomParent { return i; } + /// ditto Element addSubmitButton(string label = null) { auto t = this; auto holder = t.addChild("div"); @@ -4932,23 +5056,29 @@ class TextNode : Element { functions for the element in HTML. */ -///. +/++ + Represents a HTML link. This provides some convenience methods for manipulating query strings, but otherwise is sthe same Element interface. + + Please note this object may not be used for all `` tags. ++/ /// Group: implementations class Link : Element { - ///. - this(Document _parentDocument) { - super(_parentDocument); - this.tagName = "a"; - } - - - ///. + /++ + Constructs `that text`. + +/ this(string href, string text) { super("a"); setAttribute("href", href); innerText = text; } + + /// ditto + this(Document _parentDocument) { + super(_parentDocument); + this.tagName = "a"; + } + /+ /// Returns everything in the href EXCEPT the query string @property string targetSansQuery() { @@ -5002,7 +5132,7 @@ class Link : Element { return hash; } - ///. + /// Replaces all the stuff after a ? in the link at once with the given assoc array values. /*private*/ void updateQueryString(string[string] vars) { string href = getAttribute("href"); @@ -5071,7 +5201,11 @@ class Link : Element { */ } -///. +/++ + Represents a HTML form. This slightly specializes Element to add a few more convenience methods for adding and extracting form data. + + Please note this object may not be used for all `