mirror of https://github.com/buggins/dlangui.git
DOM
This commit is contained in:
parent
d090f8ad49
commit
e2a847f108
|
@ -4,7 +4,19 @@ import dlangui.core.collections;
|
||||||
|
|
||||||
import std.traits;
|
import std.traits;
|
||||||
import std.conv : to;
|
import std.conv : to;
|
||||||
import std.string;
|
import std.string : startsWith, endsWith;
|
||||||
|
import std.array : empty;
|
||||||
|
import std.algorithm : equal;
|
||||||
|
|
||||||
|
// Namespace, element tag and attribute names are stored as numeric ids for better performance and lesser memory consumption.
|
||||||
|
|
||||||
|
/// id type for interning namespaces
|
||||||
|
alias ns_id = ushort;
|
||||||
|
/// id type for interning element names
|
||||||
|
alias elem_id = uint;
|
||||||
|
/// id type for interning attribute names
|
||||||
|
alias attr_id = ushort;
|
||||||
|
|
||||||
|
|
||||||
/// Base class for DOM nodes
|
/// Base class for DOM nodes
|
||||||
class Node {
|
class Node {
|
||||||
|
@ -17,6 +29,15 @@ public:
|
||||||
/// returns document node
|
/// returns document node
|
||||||
@property Document document() { return _document; }
|
@property Document document() { return _document; }
|
||||||
|
|
||||||
|
/// return element tag id
|
||||||
|
@property elem_id id() { return 0; }
|
||||||
|
/// return element namespace id
|
||||||
|
@property ns_id nsid() { return 0; }
|
||||||
|
/// return element tag name
|
||||||
|
@property string name() { return document.tagName(id); }
|
||||||
|
/// return element namespace name
|
||||||
|
@property string nsname() { return document.nsName(nsid); }
|
||||||
|
|
||||||
// node properties
|
// node properties
|
||||||
|
|
||||||
/// returns true if node is text
|
/// returns true if node is text
|
||||||
|
@ -42,6 +63,18 @@ public:
|
||||||
/// returns last child node
|
/// returns last child node
|
||||||
@property Node lastChild() { return null; }
|
@property Node lastChild() { return null; }
|
||||||
|
|
||||||
|
/// find child node, return its index if found, -1 if not found or not child of this node
|
||||||
|
int childIndex(Node child) { return -1; }
|
||||||
|
/// return node index in parent's child node collection, -1 if not found
|
||||||
|
@property int index() { return _parent ? _parent.childIndex(this) : -1; }
|
||||||
|
|
||||||
|
/// append text child
|
||||||
|
Node appendText(dstring s, int index = -1) { assert(false); }
|
||||||
|
/// append element child - by namespace and tag names
|
||||||
|
Node appendElement(string ns, string tag, int index = -1) { assert(false); }
|
||||||
|
/// append element child - by namespace and tag ids
|
||||||
|
Node appendElement(ns_id ns, elem_id tag, int index = -1) { assert(false); }
|
||||||
|
|
||||||
/// node text
|
/// node text
|
||||||
@property dstring text() { return null; }
|
@property dstring text() { return null; }
|
||||||
/// ditto
|
/// ditto
|
||||||
|
@ -54,7 +87,10 @@ public:
|
||||||
class Text : Node {
|
class Text : Node {
|
||||||
private:
|
private:
|
||||||
dstring _text;
|
dstring _text;
|
||||||
|
this(Document doc, dstring text = null) {
|
||||||
|
_document = doc;
|
||||||
|
_text = text;
|
||||||
|
}
|
||||||
public:
|
public:
|
||||||
/// node text
|
/// node text
|
||||||
override @property dstring text() { return _text; }
|
override @property dstring text() { return _text; }
|
||||||
|
@ -66,7 +102,21 @@ public:
|
||||||
class Element : Node {
|
class Element : Node {
|
||||||
private:
|
private:
|
||||||
Collection!Node _children;
|
Collection!Node _children;
|
||||||
|
elem_id _id; // element tag id
|
||||||
|
ns_id _ns; // element namespace id
|
||||||
|
|
||||||
|
this(Document doc, ns_id ns, elem_id id) {
|
||||||
|
_document = doc;
|
||||||
|
_ns = ns;
|
||||||
|
_id = id;
|
||||||
|
}
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
/// return element tag id
|
||||||
|
override @property elem_id id() { return _id; }
|
||||||
|
/// return element namespace id
|
||||||
|
override @property ns_id nsid() { return _ns; }
|
||||||
|
|
||||||
// child nodes
|
// child nodes
|
||||||
|
|
||||||
/// returns child node count
|
/// returns child node count
|
||||||
|
@ -77,15 +127,88 @@ public:
|
||||||
override @property Node firstChild() { return _children.length > 0 ? _children[0] : null; }
|
override @property Node firstChild() { return _children.length > 0 ? _children[0] : null; }
|
||||||
/// returns last child node
|
/// returns last child node
|
||||||
override @property Node lastChild() { return _children.length > 0 ? _children[_children.length - 1] : null; }
|
override @property Node lastChild() { return _children.length > 0 ? _children[_children.length - 1] : null; }
|
||||||
|
/// find child node, return its index if found, -1 if not found or not child of this node
|
||||||
|
override int childIndex(Node child) {
|
||||||
|
for (int i = 0; i < _children.length; i++)
|
||||||
|
if (child is _children[i])
|
||||||
|
return i;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// append text child
|
||||||
|
override Node appendText(dstring s, int index = -1) {
|
||||||
|
Node item = document.createText(s);
|
||||||
|
_children.add(item, index >= 0 ? index : size_t.max);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
/// append element child - by namespace and tag names
|
||||||
|
override Node appendElement(string ns, string tag, int index = -1) {
|
||||||
|
Node item = document.createElement(ns, tag);
|
||||||
|
_children.add(item, index >= 0 ? index : size_t.max);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
/// append element child - by namespace and tag ids
|
||||||
|
override Node appendElement(ns_id ns, elem_id tag, int index = -1) {
|
||||||
|
Node item = document.createElement(ns, tag);
|
||||||
|
_children.add(item, index >= 0 ? index : size_t.max);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Document node
|
/// Document node
|
||||||
class Document : Element {
|
class Document : Element {
|
||||||
public:
|
public:
|
||||||
this() {
|
this() {
|
||||||
|
super(null, 0, 0);
|
||||||
_elemIds.init!Tag();
|
_elemIds.init!Tag();
|
||||||
_attrIds.init!Attr();
|
_attrIds.init!Attr();
|
||||||
_nsIds.init!Ns();
|
_nsIds.init!Ns();
|
||||||
|
_document = this;
|
||||||
|
}
|
||||||
|
/// create text node
|
||||||
|
Text createText(dstring text) {
|
||||||
|
return new Text(this, text);
|
||||||
|
}
|
||||||
|
/// create element node by namespace and tag ids
|
||||||
|
Element createElement(ns_id ns, elem_id tag) {
|
||||||
|
return new Element(this, ns, tag);
|
||||||
|
}
|
||||||
|
/// create element node by namespace and tag names
|
||||||
|
Element createElement(string ns, string tag) {
|
||||||
|
return new Element(this, internNs(ns), internTag(tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ids
|
||||||
|
|
||||||
|
/// return name for element tag id
|
||||||
|
string tagName(elem_id id) {
|
||||||
|
return _elemIds[id];
|
||||||
|
}
|
||||||
|
/// return name for namespace id
|
||||||
|
string nsName(ns_id id) {
|
||||||
|
return _nsIds[id];
|
||||||
|
}
|
||||||
|
/// return name for attribute id
|
||||||
|
string attrName(ns_id id) {
|
||||||
|
return _attrIds[id];
|
||||||
|
}
|
||||||
|
/// get id for element tag name
|
||||||
|
elem_id internTag(string s) {
|
||||||
|
if (s.empty)
|
||||||
|
return 0;
|
||||||
|
return _elemIds.intern(s);
|
||||||
|
}
|
||||||
|
/// get id for namespace name
|
||||||
|
ns_id internNs(string s) {
|
||||||
|
if (s.empty)
|
||||||
|
return 0;
|
||||||
|
return _nsIds.intern(s);
|
||||||
|
}
|
||||||
|
/// get id for namespace name
|
||||||
|
attr_id internAttr(string s) {
|
||||||
|
if (s.empty)
|
||||||
|
return 0;
|
||||||
|
return _attrIds.intern(s);
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
IdentMap!(elem_id) _elemIds;
|
IdentMap!(elem_id) _elemIds;
|
||||||
|
@ -93,12 +216,7 @@ private:
|
||||||
IdentMap!(ns_id) _nsIds;
|
IdentMap!(ns_id) _nsIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// id type for interning namespaces
|
|
||||||
alias ns_id = ushort;
|
|
||||||
/// id type for interning element names
|
|
||||||
alias elem_id = uint;
|
|
||||||
/// id type for interning attribute names
|
|
||||||
alias attr_id = ushort;
|
|
||||||
|
|
||||||
/// remove trailing _ from string, e.g. "body_" -> "body"
|
/// remove trailing _ from string, e.g. "body_" -> "body"
|
||||||
private string removeTrailingUnderscore(string s) {
|
private string removeTrailingUnderscore(string s) {
|
||||||
|
@ -112,11 +230,14 @@ struct IdentMap(ident_t) {
|
||||||
/// initialize with elements of enum
|
/// initialize with elements of enum
|
||||||
void init(E)() if (is(E == enum)) {
|
void init(E)() if (is(E == enum)) {
|
||||||
foreach(member; EnumMembers!E) {
|
foreach(member; EnumMembers!E) {
|
||||||
internString(removeTrailingUnderscore(member.to!string), member);
|
static if (member.to!int) {
|
||||||
|
//pragma(msg, "interning string '" ~ removeTrailingUnderscore(member.to!string) ~ "' for " ~ E.stringof);
|
||||||
|
intern(removeTrailingUnderscore(member.to!string), member);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// intern string - return ID assigned for it
|
/// intern string - return ID assigned for it
|
||||||
ident_t internString(string s, ident_t id = 0) {
|
ident_t intern(string s, ident_t id = 0) {
|
||||||
if (auto p = s in _stringToId)
|
if (auto p = s in _stringToId)
|
||||||
return *p;
|
return *p;
|
||||||
ident_t res;
|
ident_t res;
|
||||||
|
@ -133,12 +254,16 @@ struct IdentMap(ident_t) {
|
||||||
}
|
}
|
||||||
/// lookup id for string, return 0 if string is not found
|
/// lookup id for string, return 0 if string is not found
|
||||||
ident_t opIndex(string s) {
|
ident_t opIndex(string s) {
|
||||||
|
if (s.empty)
|
||||||
|
return 0;
|
||||||
if (auto p = s in _stringToId)
|
if (auto p = s in _stringToId)
|
||||||
return *p;
|
return *p;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/// lookup name for id, return null if not found
|
/// lookup name for id, return null if not found
|
||||||
string opIndex(ident_t id) {
|
string opIndex(ident_t id) {
|
||||||
|
if (!id)
|
||||||
|
return null;
|
||||||
if (auto p = id in _idToString)
|
if (auto p = id in _idToString)
|
||||||
return *p;
|
return *p;
|
||||||
return null;
|
return null;
|
||||||
|
@ -187,5 +312,16 @@ unittest {
|
||||||
assert(map[Tag.div].equal("div"));
|
assert(map[Tag.div].equal("div"));
|
||||||
|
|
||||||
Document doc = new Document();
|
Document doc = new Document();
|
||||||
|
auto body_ = doc.appendElement(null, "body");
|
||||||
|
assert(body_.id == Tag.body_);
|
||||||
|
assert(body_.name.equal("body"));
|
||||||
|
auto div = body_.appendElement(null, "div");
|
||||||
|
assert(body_.childCount == 1);
|
||||||
|
assert(div.id == Tag.body_);
|
||||||
|
assert(div.name.equal("div"));
|
||||||
|
div.appendText("Some text"d);
|
||||||
|
assert(div.childCount == 1);
|
||||||
|
assert(div.child(0).text.equal("Some text"d));
|
||||||
|
|
||||||
destroy(doc);
|
destroy(doc);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue