mirror of https://github.com/buggins/dlangui.git
CSS parser
This commit is contained in:
parent
206d9b7c60
commit
0d5c3509f0
|
@ -3,6 +3,7 @@
|
|||
/**
|
||||
This module contains implementation of CSS support - Cascading Style Sheets.
|
||||
|
||||
Port of CoolReader Engine written in C++.
|
||||
|
||||
Synopsis:
|
||||
|
||||
|
@ -19,10 +20,12 @@ module dlangui.core.css;
|
|||
|
||||
import std.traits;
|
||||
import std.conv : to;
|
||||
import std.string : startsWith, endsWith;
|
||||
import std.string;
|
||||
import std.array : empty;
|
||||
import std.algorithm : equal;
|
||||
|
||||
import dlangui.core.dom;
|
||||
|
||||
/// display property values
|
||||
enum CssDisplay : ubyte {
|
||||
inherit,
|
||||
|
@ -276,6 +279,181 @@ class CssStyle {
|
|||
CssValue textIndent = CssValue.inherited;
|
||||
}
|
||||
|
||||
/// selector rule type
|
||||
enum CssSelectorRuleType : ubyte {
|
||||
universal, // *
|
||||
parent, // E > F
|
||||
ancessor, // E F
|
||||
predecessor, // E + F
|
||||
attrset, // E[foo]
|
||||
attreq, // E[foo="value"]
|
||||
attrhas, // E[foo~="value"]
|
||||
attrstarts, // E[foo|="value"]
|
||||
id, // E#id
|
||||
class_ // E.class
|
||||
}
|
||||
|
||||
class CssSelectorRule
|
||||
{
|
||||
private:
|
||||
CssSelectorRuleType _type;
|
||||
elem_id _id;
|
||||
attr_id _attrid;
|
||||
CssSelectorRule _next;
|
||||
string _value;
|
||||
public:
|
||||
this(CssSelectorRuleType type) {
|
||||
_type = type;
|
||||
}
|
||||
this(const CssSelectorRule v) {
|
||||
_type = v._type;
|
||||
_id = v._id;
|
||||
_attrid = v._attrid;
|
||||
_value = v._value;
|
||||
}
|
||||
~this() {
|
||||
//if (_next)
|
||||
// destroy(_next);
|
||||
}
|
||||
|
||||
@property elem_id id() { return _id; }
|
||||
@property void id(elem_id newid) { _id = newid; }
|
||||
@property attr_id attrid() { return _attrid; }
|
||||
@property void setAttr(attr_id newid, string value) { _attrid = newid; _value = value; }
|
||||
@property CssSelectorRule next() { return _next; }
|
||||
@property void next(CssSelectorRule v) { _next = v; }
|
||||
/// check condition for node
|
||||
bool check(ref Node node) {
|
||||
if (!node || !node.parent)
|
||||
return false;
|
||||
switch (_type) with (CssSelectorRuleType) {
|
||||
case parent: // E > F
|
||||
node = node.parent;
|
||||
if (!node)
|
||||
return false;
|
||||
return node.id == _id;
|
||||
|
||||
case ancessor: // E F
|
||||
for (;;) {
|
||||
node = node.parent;
|
||||
if (!node)
|
||||
return false;
|
||||
if (node.id == _id)
|
||||
return true;
|
||||
}
|
||||
|
||||
case predecessor: // E + F
|
||||
int index = node.index;
|
||||
// while
|
||||
if (index > 0) {
|
||||
Node elem = node.parent.childElement(index-1, _id);
|
||||
if ( elem ) {
|
||||
node = elem;
|
||||
//CRLog::trace("+ selector: found pred element");
|
||||
return true;
|
||||
}
|
||||
//index--;
|
||||
}
|
||||
return false;
|
||||
|
||||
case attrset: // E[foo]
|
||||
return node.hasAttr(_attrid);
|
||||
|
||||
case attreq: // E[foo="value"]
|
||||
string val = node.attrValue(Ns.any, _attrid);
|
||||
return (val == _value);
|
||||
|
||||
case attrhas: // E[foo~="value"]
|
||||
// one of space separated values
|
||||
string val = node.attrValue(Ns.any, _attrid);
|
||||
int p = val.indexOf(_value);
|
||||
if (p < 0)
|
||||
return false;
|
||||
if ( (p > 0 && val[p - 1] != ' ')
|
||||
|| ( p + _value.length < val.length && val[p + _value.length] != ' '))
|
||||
return false;
|
||||
return true;
|
||||
|
||||
case attrstarts: // E[foo|="value"]
|
||||
string val = node.attrValue(Ns.any, _attrid);
|
||||
if (_value.length > val.length)
|
||||
return false;
|
||||
return val[0 .. _value.length] == _value;
|
||||
|
||||
case id: // E#id
|
||||
string val = node.attrValue(Ns.any, Attr.id);
|
||||
return val == _value;
|
||||
|
||||
case class_: // E.class
|
||||
string val = node.attrValue(Ns.any, Attr.class_);
|
||||
return !val.icmp(_value);
|
||||
|
||||
case universal: // *
|
||||
return true;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** simple CSS selector
|
||||
|
||||
Currently supports only element name and universal selector.
|
||||
|
||||
- * { } - universal selector
|
||||
- element-name { } - selector by element name
|
||||
- element1, element2 { } - several selectors delimited by comma
|
||||
*/
|
||||
class CssSelector {
|
||||
private:
|
||||
uint _id;
|
||||
CssDeclaration _decl;
|
||||
int _specificity;
|
||||
CssSelector _next;
|
||||
CssSelectorRule _rules;
|
||||
void insertRuleStart(CssSelectorRule rule) {
|
||||
}
|
||||
void insertRuleAfterStart(CssSelectorRule rule) {
|
||||
}
|
||||
public:
|
||||
this(CssSelector v) {
|
||||
_id = v._id;
|
||||
_decl = v._decl;
|
||||
_specificity = v._specificity;
|
||||
}
|
||||
this() { }
|
||||
~this() {
|
||||
//if (_next)
|
||||
// destroy(_next);
|
||||
//if (_rules)
|
||||
// destroy(_rules);
|
||||
}
|
||||
bool parse(ref string str) { //, lxmlDocBase * doc
|
||||
return false;
|
||||
}
|
||||
@property uint tagId() { return _id; }
|
||||
bool check(const Node node) const {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
/// apply to style if selector matches
|
||||
void apply(const Node node, CssStyle style) const
|
||||
{
|
||||
if (check(node))
|
||||
_decl.apply(style);
|
||||
}
|
||||
void setDeclaration(CssDeclaration decl) {
|
||||
_decl = decl;
|
||||
}
|
||||
int getSpecificity() {
|
||||
return _specificity;
|
||||
}
|
||||
@property CssSelector next() { return _next; }
|
||||
@property void next(CssSelector next) { _next = next; }
|
||||
//lUInt32 getHash();
|
||||
}
|
||||
|
||||
struct CssDeclItem {
|
||||
CssDeclType type = CssDeclType.unknown;
|
||||
union {
|
||||
|
@ -284,7 +462,7 @@ struct CssDeclItem {
|
|||
}
|
||||
string str;
|
||||
|
||||
void apply(CssStyle style) {
|
||||
void apply(CssStyle style) const {
|
||||
switch (type) with (CssDeclType) {
|
||||
case display: style.display = cast(CssDisplay)value; break;
|
||||
case white_space: style.whiteSpace = cast(CssWhiteSpace)value; break;
|
||||
|
@ -357,7 +535,7 @@ class CssDeclaration {
|
|||
_list ~= item;
|
||||
}
|
||||
|
||||
void apply(CssStyle style) {
|
||||
void apply(CssStyle style) const {
|
||||
foreach(item; _list)
|
||||
item.apply(style);
|
||||
}
|
||||
|
@ -938,9 +1116,9 @@ unittest {
|
|||
string src = "{ display: inline; text-decoration: underline; white-space: pre; text-align: right; text-align-last: left; "
|
||||
"hyphenate: auto; width: 70%; height: 1.5pt; margin-left: 2.0em; "
|
||||
"font-family: Arial, 'Times New Roman', sans-serif; font-size: 18pt; line-height: 120%; letter-spacing: 2px; font-weight: 300; "
|
||||
" }";
|
||||
" }t";
|
||||
assert(decl.parse(src, true));
|
||||
assert(!src.empty && src[0] == '}');
|
||||
assert(!src.empty && src[0] == 't');
|
||||
assert(style.display == CssDisplay.block);
|
||||
assert(style.textDecoration == CssTextDecoration.inherit);
|
||||
assert(style.whiteSpace == CssWhiteSpace.inherit);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
/**
|
||||
This module contains implementation DOM - document object model.
|
||||
|
||||
Port of CoolReader Engine written in C++.
|
||||
|
||||
Synopsis:
|
||||
|
||||
|
@ -84,6 +85,16 @@ public:
|
|||
string attrValue(ns_id nsid, attr_id attrid) { return null; }
|
||||
/// get attribute value by namespace and attribute ids
|
||||
string attrValue(string nsname, string attrname) { return attrValue(_document.nsId(nsname), _document.attrId(attrname)); }
|
||||
/// returns true if node has attribute with specified name
|
||||
bool hasAttr(string attrname) {
|
||||
return hasAttr(document.attrId(attrname));
|
||||
}
|
||||
/// returns true if node has attribute with specified id
|
||||
bool hasAttr(attr_id attrid) {
|
||||
if (Attribute a = attr(Ns.any, attrid))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// child nodes
|
||||
|
||||
|
@ -101,6 +112,14 @@ public:
|
|||
/// return node index in parent's child node collection, -1 if not found
|
||||
@property int index() { return _parent ? _parent.childIndex(this) : -1; }
|
||||
|
||||
/// returns child node by index and optionally compares its tag id, returns null if child with this index is not an element or id does not match
|
||||
Element childElement(int index, elem_id id = 0) {
|
||||
Element res = cast(Element)child(index);
|
||||
if (res && (id == 0 || res.id == id))
|
||||
return res;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// append text child
|
||||
Node appendText(dstring s, int index = -1) { assert(false); }
|
||||
/// append element child - by namespace and tag names
|
||||
|
|
Loading…
Reference in New Issue