From d090f8ad491b3814f7b75f6b0608892139532fa9 Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Wed, 23 Dec 2015 11:32:14 +0300 Subject: [PATCH] DOM initial implementation --- dlangui-monod-linux.dproj | 1 + dlangui-monod-osx.dproj | 1 + dlangui-monod-windows.dproj | 1 + dlangui-msvc.visualdproj | 3 +- src/dlangui/core/dom.d | 191 ++++++++++++++++++++++++++++++++++++ 5 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 src/dlangui/core/dom.d diff --git a/dlangui-monod-linux.dproj b/dlangui-monod-linux.dproj index 1ee96595..50a15b47 100644 --- a/dlangui-monod-linux.dproj +++ b/dlangui-monod-linux.dproj @@ -246,6 +246,7 @@ + diff --git a/dlangui-monod-osx.dproj b/dlangui-monod-osx.dproj index 3acfd22f..58cd93f9 100644 --- a/dlangui-monod-osx.dproj +++ b/dlangui-monod-osx.dproj @@ -105,6 +105,7 @@ + diff --git a/dlangui-monod-windows.dproj b/dlangui-monod-windows.dproj index efb8cf6c..d873d9ac 100644 --- a/dlangui-monod-windows.dproj +++ b/dlangui-monod-windows.dproj @@ -82,6 +82,7 @@ + diff --git a/dlangui-msvc.visualdproj b/dlangui-msvc.visualdproj index 901ccc7f..fd58584f 100644 --- a/dlangui-msvc.visualdproj +++ b/dlangui-msvc.visualdproj @@ -33,7 +33,7 @@ 0 0 0 - 0 + 1 0 0 0 @@ -542,6 +542,7 @@ + diff --git a/src/dlangui/core/dom.d b/src/dlangui/core/dom.d new file mode 100644 index 00000000..5e7a3fde --- /dev/null +++ b/src/dlangui/core/dom.d @@ -0,0 +1,191 @@ +module dlangui.core.dom; + +import dlangui.core.collections; + +import std.traits; +import std.conv : to; +import std.string; + +/// Base class for DOM nodes +class Node { +private: + Node _parent; + Document _document; +public: + /// returns parent node + @property Node parent() { return _parent; } + /// returns document node + @property Document document() { return _document; } + + // node properties + + /// returns true if node is text + @property bool isText() { return false; } + /// returns true if node is element + @property bool isElement() { return false; } + /// returns true if node has child nodes + @property bool hasChildren() { return false; } + + // attributes + + /// returns attribute count + @property int attrCount() { return 0; } + + // child nodes + + /// returns child node count + @property int childCount() { return 0; } + /// returns child node by index + @property Node child(int index) { return null; } + /// returns first child node + @property Node firstChild() { return null; } + /// returns last child node + @property Node lastChild() { return null; } + + /// node text + @property dstring text() { return null; } + /// ditto + @property void text(dstring s) { } + + +} + +/// Text node +class Text : Node { +private: + dstring _text; + +public: + /// node text + override @property dstring text() { return _text; } + /// ditto + override @property void text(dstring s) { _text = s; } +} + +/// Element node +class Element : Node { +private: + Collection!Node _children; +public: + // child nodes + + /// returns child node count + override @property int childCount() { return cast(int)_children.length; } + /// returns child node by index + override @property Node child(int index) { return index >= 0 && index < _children.length ? _children[index] : null; } + /// returns first child node + override @property Node firstChild() { return _children.length > 0 ? _children[0] : null; } + /// returns last child node + override @property Node lastChild() { return _children.length > 0 ? _children[_children.length - 1] : null; } +} + +/// Document node +class Document : Element { +public: + this() { + _elemIds.init!Tag(); + _attrIds.init!Attr(); + _nsIds.init!Ns(); + } +private: + IdentMap!(elem_id) _elemIds; + IdentMap!(attr_id) _attrIds; + 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" +private string removeTrailingUnderscore(string s) { + if (s.endsWith("_")) + return s[0..$-1]; + return s; +} + +/// String identifier to Id map - for interning strings +struct IdentMap(ident_t) { + /// initialize with elements of enum + void init(E)() if (is(E == enum)) { + foreach(member; EnumMembers!E) { + internString(removeTrailingUnderscore(member.to!string), member); + } + } + /// intern string - return ID assigned for it + ident_t internString(string s, ident_t id = 0) { + if (auto p = s in _stringToId) + return *p; + ident_t res; + if (id > 0) { + if (_nextId <= id) + _nextId = cast(ident_t)(id + 1); + res = id; + } else { + res = _nextId++; + } + _idToString[res] = s; + _stringToId[s] = res; + return res; + } + /// lookup id for string, return 0 if string is not found + ident_t opIndex(string s) { + if (auto p = s in _stringToId) + return *p; + return 0; + } + /// lookup name for id, return null if not found + string opIndex(ident_t id) { + if (auto p = id in _idToString) + return *p; + return null; + } +private: + string[ident_t] _idToString; + ident_t[string] _stringToId; + ident_t _nextId = 1; +} + +/// standard tags +enum Tag { + NONE, + body_, + pre, + div, + span +} + +/// standard attributes +enum Attr { + NONE, + id, + class_, + style +} + +/// standard namespaces +enum Ns { + NONE, + xmlns, + xs, + xlink, + l, + xsi +} + +unittest { + import std.algorithm : equal; + //import std.stdio; + IdentMap!(elem_id) map; + map.init!Tag(); + //writeln("running DOM unit test"); + assert(map["pre"] == Tag.pre); + assert(map["body"] == Tag.body_); + assert(map[Tag.div].equal("div")); + + Document doc = new Document(); + destroy(doc); +}