Changed: Implement HTML renderer with CSS styling
--HG-- branch : developfeature/clean-deprecated
parent
9587e9bcef
commit
fb54672815
@ -0,0 +1,107 @@
|
|||||||
|
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
|
||||||
|
// Copyright (C) 2010 Winch Gate Property Limited
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as
|
||||||
|
// published by the Free Software Foundation, either version 3 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#ifndef CL_CSS_SELECTOR_H
|
||||||
|
#define CL_CSS_SELECTOR_H
|
||||||
|
|
||||||
|
#include "nel/misc/types_nl.h"
|
||||||
|
|
||||||
|
namespace NLGUI
|
||||||
|
{
|
||||||
|
class CHtmlElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief CSS selector
|
||||||
|
* \date 2019-03-15 10:50 GMT
|
||||||
|
* \author Meelis Mägi (Nimetu)
|
||||||
|
*/
|
||||||
|
class CCssSelector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum ECombinator {
|
||||||
|
NONE = 0,
|
||||||
|
GENERAL_CHILD,
|
||||||
|
ADJACENT_SIBLING,
|
||||||
|
GENERAL_SIBLING,
|
||||||
|
CHILD_OF
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SAttribute {
|
||||||
|
std::string key;
|
||||||
|
std::string value;
|
||||||
|
char op; // =, ~, |, ^, $, *
|
||||||
|
SAttribute(const std::string &k, const std::string &v, char o)
|
||||||
|
:key(k),value(v),op(o)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string Element;
|
||||||
|
std::string Id;
|
||||||
|
std::vector<std::string> Class;
|
||||||
|
std::vector<SAttribute> Attr;
|
||||||
|
std::vector<std::string> PseudoClass;
|
||||||
|
|
||||||
|
// css combinator or \0 missing (first element)
|
||||||
|
char Combinator;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// TODO: rewrite for ECombinator enum
|
||||||
|
CCssSelector(std::string elm="", std::string id="", std::string cls="", char comb = '\0');
|
||||||
|
|
||||||
|
// helper for sorting
|
||||||
|
uint32 specificity() const;
|
||||||
|
|
||||||
|
// set classes used, eg 'class1 class2'
|
||||||
|
void setClass(const std::string &cls);
|
||||||
|
|
||||||
|
// add attribute to selector
|
||||||
|
// ' ' op means 'key exists, ignore value'
|
||||||
|
void addAttribute(const std::string &key, const std::string &val = "", char op = ' ');
|
||||||
|
|
||||||
|
// add pseudo class to selector, eg 'first-child'
|
||||||
|
void addPseudoClass(const std::string &key);
|
||||||
|
|
||||||
|
// true if no rules have been defined
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
return Element.empty() && Id.empty() && Class.empty() && Attr.empty() && PseudoClass.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test current selector to html DOM element
|
||||||
|
// NOTE: Does not check combinator
|
||||||
|
bool match(const CHtmlElement &elm) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool matchClass(const CHtmlElement &elm) const;
|
||||||
|
bool matchAttributes(const CHtmlElement &elm) const;
|
||||||
|
bool matchPseudoClass(const CHtmlElement &elm) const;
|
||||||
|
|
||||||
|
// match An+B rule to child index (1 based)
|
||||||
|
bool matchNth(sint childNr, sint a, sint b) const;
|
||||||
|
|
||||||
|
// parse nth-child string to 'a' and 'b' components
|
||||||
|
// :nth-child(odd)
|
||||||
|
// :nth-child(even)
|
||||||
|
// :nth-child(An+B)
|
||||||
|
// :nth-child(-An+b)
|
||||||
|
void parseNth(const std::string &pseudo, sint &a, sint &b) const;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}//namespace
|
||||||
|
|
||||||
|
#endif // CL_CSS_SELECTOR_H
|
||||||
|
|
@ -0,0 +1,86 @@
|
|||||||
|
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
|
||||||
|
// Copyright (C) 2010 Winch Gate Property Limited
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as
|
||||||
|
// published by the Free Software Foundation, either version 3 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#ifndef CL_HTML_ELEMENT_H
|
||||||
|
#define CL_HTML_ELEMENT_H
|
||||||
|
|
||||||
|
#include "nel/misc/types_nl.h"
|
||||||
|
#include "nel/gui/css_style.h"
|
||||||
|
|
||||||
|
namespace NLGUI
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief HTML element
|
||||||
|
* \date 2019-04-25 18:23 GMT
|
||||||
|
* \author Meelis Mägi (Nimetu)
|
||||||
|
*/
|
||||||
|
class CHtmlElement
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum ENodeType {
|
||||||
|
NONE = 0,
|
||||||
|
ELEMENT_NODE = 1,
|
||||||
|
TEXT_NODE = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
uint ID; // libwww element enum
|
||||||
|
ENodeType Type;
|
||||||
|
std::string Value; // text node value or element node name
|
||||||
|
std::map<std::string, std::string> Attributes;
|
||||||
|
std::list<CHtmlElement> Children;
|
||||||
|
|
||||||
|
// class names for css matching
|
||||||
|
std::set<std::string> ClassNames;
|
||||||
|
|
||||||
|
// defined style and :before/:after pseudo elements
|
||||||
|
TStyle Style;
|
||||||
|
TStyle StyleBefore;
|
||||||
|
TStyle StyleAfter;
|
||||||
|
|
||||||
|
// hierarchy
|
||||||
|
CHtmlElement *parent;
|
||||||
|
CHtmlElement *previousSibling;
|
||||||
|
CHtmlElement *nextSibling;
|
||||||
|
|
||||||
|
// n'th ELEMENT_NODE in parent.Children, for :nth-child() rules
|
||||||
|
uint childIndex;
|
||||||
|
|
||||||
|
CHtmlElement(ENodeType type = NONE, std::string value = "");
|
||||||
|
|
||||||
|
// returns true if rhs is same pointer
|
||||||
|
friend bool operator==(const CHtmlElement &lhs, const CHtmlElement &rhs)
|
||||||
|
{
|
||||||
|
return &lhs == &rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasAttribute(const std::string &key) const;
|
||||||
|
|
||||||
|
bool hasNonEmptyAttribute(const std::string &key) const;
|
||||||
|
|
||||||
|
std::string getAttribute(const std::string &key) const;
|
||||||
|
|
||||||
|
bool hasClass(const std::string &key) const;
|
||||||
|
|
||||||
|
// update Children index/parent/next/prevSibling pointers
|
||||||
|
void reindexChilds();
|
||||||
|
|
||||||
|
// debug
|
||||||
|
std::string toString(bool tree = false, uint depth = 0) const;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,314 @@
|
|||||||
|
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
|
||||||
|
// Copyright (C) 2010 Winch Gate Property Limited
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as
|
||||||
|
// published by the Free Software Foundation, either version 3 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include "stdpch.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "nel/misc/types_nl.h"
|
||||||
|
#include "nel/gui/css_selector.h"
|
||||||
|
#include "nel/gui/html_element.h"
|
||||||
|
|
||||||
|
using namespace NLMISC;
|
||||||
|
|
||||||
|
#ifdef DEBUG_NEW
|
||||||
|
#define new DEBUG_NEW
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace NLGUI
|
||||||
|
{
|
||||||
|
CCssSelector::CCssSelector(std::string elm, std::string id, std::string cls, char comb)
|
||||||
|
: Element(elm), Id(id), Class(), Attr(), PseudoClass(), Combinator(comb)
|
||||||
|
{
|
||||||
|
if (!cls.empty())
|
||||||
|
{
|
||||||
|
setClass(cls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 CCssSelector::specificity() const
|
||||||
|
{
|
||||||
|
uint ret = 0;
|
||||||
|
|
||||||
|
if (!Element.empty() && Element != "*") ret += 0x000001;
|
||||||
|
// Pseudo Element is added in CCssStyle
|
||||||
|
//if (!PseudoElement.empty()) ret += 0x000001;
|
||||||
|
|
||||||
|
if (!Class.empty()) ret += 0x000100 * Class.size();
|
||||||
|
if (!Attr.empty()) ret += 0x000100 * Attr.size();
|
||||||
|
// TODO: has different cases
|
||||||
|
if (!PseudoClass.empty()) ret += 0x000100 * PseudoClass.size();
|
||||||
|
|
||||||
|
if (!Id.empty()) ret += 0x010000;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCssSelector::setClass(const std::string &cls)
|
||||||
|
{
|
||||||
|
std::vector<std::string> parts;
|
||||||
|
NLMISC::splitString(toLower(cls), ".", parts);
|
||||||
|
|
||||||
|
for(uint i = 0; i< parts.size(); i++)
|
||||||
|
{
|
||||||
|
std::string cname = trim(parts[i]);
|
||||||
|
if (!cname.empty())
|
||||||
|
{
|
||||||
|
Class.push_back(cname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCssSelector::addAttribute(const std::string &key, const std::string &val, char op)
|
||||||
|
{
|
||||||
|
Attr.push_back(SAttribute(key, val, op));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCssSelector::addPseudoClass(const std::string &key)
|
||||||
|
{
|
||||||
|
if (key.empty()) return;
|
||||||
|
|
||||||
|
PseudoClass.push_back(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CCssSelector::match(const CHtmlElement &elm) const
|
||||||
|
{
|
||||||
|
if (!Element.empty() && Element != "*" && Element != elm.Value)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Id.empty() && Id != elm.getAttribute("id"))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Class.empty() && !matchClass(elm))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Attr.empty() && !matchAttributes(elm))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PseudoClass.empty() && !matchPseudoClass(elm))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CCssSelector::matchClass(const CHtmlElement &elm) const
|
||||||
|
{
|
||||||
|
// make sure all class names we have, other has as well
|
||||||
|
for(uint i = 0; i< Class.size(); ++i)
|
||||||
|
{
|
||||||
|
if (!elm.hasClass(Class[i]))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CCssSelector::matchAttributes(const CHtmlElement &elm) const
|
||||||
|
{
|
||||||
|
// TODO: refactor into matchAttributeSelector
|
||||||
|
for(uint i = 0; i< Attr.size(); ++i)
|
||||||
|
{
|
||||||
|
if (!elm.hasAttribute(Attr[i].key)) return false;
|
||||||
|
|
||||||
|
std::string value = elm.getAttribute(Attr[i].key);
|
||||||
|
switch(Attr[i].op)
|
||||||
|
{
|
||||||
|
case '=':
|
||||||
|
if (Attr[i].value != value) return false;
|
||||||
|
break;
|
||||||
|
case '~':
|
||||||
|
{
|
||||||
|
// exact match to any of whitespace separated words from element attribute
|
||||||
|
if (Attr[i].value.empty()) return false;
|
||||||
|
|
||||||
|
std::vector<std::string> parts;
|
||||||
|
NLMISC::splitString(value, " ", parts);
|
||||||
|
bool found = false;
|
||||||
|
for(uint j = 0; j < parts.size(); j++)
|
||||||
|
{
|
||||||
|
if (Attr[i].value == parts[j])
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '|':
|
||||||
|
// exact value, or start with val+'-'
|
||||||
|
if (value != Attr[i].value && value.find(Attr[i].value + "-") == std::string::npos) return false;
|
||||||
|
break;
|
||||||
|
case '^':
|
||||||
|
// prefix, starts with
|
||||||
|
if (Attr[i].value.empty()) return false;
|
||||||
|
if (value.find(Attr[i].value) != 0) return false;
|
||||||
|
break;
|
||||||
|
case '$':
|
||||||
|
// suffic, ends with
|
||||||
|
if (Attr[i].value.empty() || value.size() < Attr[i].value.size()) return false;
|
||||||
|
if (Attr[i].value == value.substr(value.size() - Attr[i].value.size())) return false;
|
||||||
|
break;
|
||||||
|
case '*':
|
||||||
|
if (Attr[i].value.empty()) return false;
|
||||||
|
if (value.find(Attr[i].value) == std::string::npos) return false;
|
||||||
|
break;
|
||||||
|
case ' ':
|
||||||
|
// contains key, ignore value
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// unknown comparison
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CCssSelector::matchPseudoClass(const CHtmlElement &elm) const
|
||||||
|
{
|
||||||
|
for(uint i = 0; i< PseudoClass.size(); i++)
|
||||||
|
{
|
||||||
|
if (PseudoClass[i] == "root")
|
||||||
|
{
|
||||||
|
// ':root' is just 'html' with higher specificity
|
||||||
|
if (elm.Value != "html") return false;
|
||||||
|
}
|
||||||
|
else if (PseudoClass[i] == "only-child")
|
||||||
|
{
|
||||||
|
if (elm.parent && !elm.parent->Children.empty()) return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (PseudoClass[i] == "first-child")
|
||||||
|
{
|
||||||
|
if (elm.previousSibling) return false;
|
||||||
|
}
|
||||||
|
else if (PseudoClass[i] == "last-child")
|
||||||
|
{
|
||||||
|
if (elm.nextSibling) return false;
|
||||||
|
}
|
||||||
|
else if (PseudoClass[i].find("nth-child(") != std::string::npos)
|
||||||
|
{
|
||||||
|
sint a, b;
|
||||||
|
// TODO: there might be multiple :nth-child() on single selector, so current can't cache it
|
||||||
|
parseNth(PseudoClass[i], a, b);
|
||||||
|
|
||||||
|
// 1st child should be '1' and not '0'
|
||||||
|
if (!matchNth(elm.childIndex+1, a, b)) return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCssSelector::parseNth(const std::string &pseudo, sint &a, sint &b) const
|
||||||
|
{
|
||||||
|
a = 0;
|
||||||
|
b = 0;
|
||||||
|
|
||||||
|
std::string::size_type start = pseudo.find_first_of("(") + 1;
|
||||||
|
std::string::size_type end = pseudo.find_first_of(")");
|
||||||
|
|
||||||
|
if (start == std::string::npos) return;
|
||||||
|
|
||||||
|
std::string expr = toLower(pseudo.substr(start, end - start));
|
||||||
|
|
||||||
|
if (expr.empty()) return;
|
||||||
|
|
||||||
|
if (expr == "even")
|
||||||
|
{
|
||||||
|
// 2n+0
|
||||||
|
a = 2;
|
||||||
|
b = 0;
|
||||||
|
}
|
||||||
|
else if (expr == "odd")
|
||||||
|
{
|
||||||
|
// 2n+1
|
||||||
|
a = 2;
|
||||||
|
b = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// -An+B, An+B, An-B
|
||||||
|
std::string::size_type pos;
|
||||||
|
|
||||||
|
start = 0;
|
||||||
|
pos = expr.find_first_of("n", start);
|
||||||
|
if (pos == std::string::npos)
|
||||||
|
{
|
||||||
|
fromString(expr, b);
|
||||||
|
}
|
||||||
|
else if (pos == 0)
|
||||||
|
{
|
||||||
|
// 'n' == '1n'
|
||||||
|
a = 1;
|
||||||
|
}
|
||||||
|
else if (expr[0] == '-' && pos == 1)
|
||||||
|
{
|
||||||
|
// '-n' == '-1n'
|
||||||
|
a = -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fromString(expr.substr(start, pos - start), a);
|
||||||
|
}
|
||||||
|
|
||||||
|
start = pos;
|
||||||
|
pos = expr.find_first_of("+-", start);
|
||||||
|
if (pos != std::string::npos && (expr[pos] == '+' || expr[pos] == '-'))
|
||||||
|
{
|
||||||
|
// copy with sign char
|
||||||
|
fromString(expr.substr(pos, end - pos), b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CCssSelector::matchNth(sint childNr, sint a, sint b) const
|
||||||
|
{
|
||||||
|
if (a == 0)
|
||||||
|
{
|
||||||
|
return childNr == b;
|
||||||
|
}
|
||||||
|
else if (a > 0)
|
||||||
|
{
|
||||||
|
return childNr >= b && (childNr - b) % a == 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// a is negative from '-An+B'
|
||||||
|
return childNr <= b && (b - childNr) % (-a) == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,168 @@
|
|||||||
|
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
|
||||||
|
// Copyright (C) 2010 Winch Gate Property Limited
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as
|
||||||
|
// published by the Free Software Foundation, either version 3 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#include "stdpch.h"
|
||||||
|
|
||||||
|
#include "nel/gui/html_element.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace NLMISC;
|
||||||
|
|
||||||
|
#ifdef DEBUG_NEW
|
||||||
|
#define new DEBUG_NEW
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace NLGUI
|
||||||
|
{
|
||||||
|
// ***************************************************************************
|
||||||
|
CHtmlElement::CHtmlElement(ENodeType type, std::string value)
|
||||||
|
: ID(0), Type(type), Value(value), parent(NULL), previousSibling(NULL), nextSibling(NULL), childIndex(0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
// ***************************************************************************
|
||||||
|
bool CHtmlElement::hasAttribute(const std::string &key) const
|
||||||
|
{
|
||||||
|
return Attributes.find(key) != Attributes.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ***************************************************************************
|
||||||
|
bool CHtmlElement::hasNonEmptyAttribute(const std::string &key) const
|
||||||
|
{
|
||||||
|
return !getAttribute(key).empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ***************************************************************************
|
||||||
|
std::string CHtmlElement::getAttribute(const std::string &key) const
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string>::const_iterator it = Attributes.find(key);
|
||||||
|
return (it != Attributes.end() ? it->second : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ***************************************************************************
|
||||||
|
bool CHtmlElement::hasClass(const std::string &key) const
|
||||||
|
{
|
||||||
|
return ClassNames.find(key) != ClassNames.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ***************************************************************************
|
||||||
|
void CHtmlElement::reindexChilds()
|
||||||
|
{
|
||||||
|
uint index = 0;
|
||||||
|
CHtmlElement *prev = NULL;
|
||||||
|
std::list<CHtmlElement>::iterator it;
|
||||||
|
for(it = Children.begin(); it != Children.end(); ++it)
|
||||||
|
{
|
||||||
|
if (it->Type == ELEMENT_NODE)
|
||||||
|
{
|
||||||
|
it->parent = this;
|
||||||
|
it->previousSibling = prev;
|
||||||
|
it->nextSibling = NULL;
|
||||||
|
if (prev)
|
||||||
|
{
|
||||||
|
prev->nextSibling = &(*it);
|
||||||
|
}
|
||||||
|
it->childIndex = index;
|
||||||
|
index++;
|
||||||
|
prev = &(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ***************************************************************************
|
||||||
|
std::string CHtmlElement::toString(bool tree, uint depth) const
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
if (depth > 0)
|
||||||
|
{
|
||||||
|
result = NLMISC::toString("[%d]", depth);
|
||||||
|
result.append(depth, '-');
|
||||||
|
}
|
||||||
|
if (Type == NONE || Type == ELEMENT_NODE)
|
||||||
|
{
|
||||||
|
result += "<" + Value;
|
||||||
|
for(std::map<std::string, std::string>::const_iterator it = Attributes.begin(); it != Attributes.end(); ++it)
|
||||||
|
{
|
||||||
|
if (it->first == "class")
|
||||||
|
{
|
||||||
|
result += " class=\"";
|
||||||
|
for(std::set<std::string>::const_iterator it2 = ClassNames.begin(); it2 != ClassNames.end(); ++it2)
|
||||||
|
{
|
||||||
|
if (it2 != ClassNames.begin())
|
||||||
|
{
|
||||||
|
result += " ";
|
||||||
|
}
|
||||||
|
result += *it2;
|
||||||
|
}
|
||||||
|
result += "\"";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result += " " + it->first + "=\"" + it->second + "\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result += NLMISC::toString(" data-debug=\"childIndex: %d\"", childIndex);
|
||||||
|
result += ">";
|
||||||
|
|
||||||
|
if (tree)
|
||||||
|
{
|
||||||
|
result += "\n";
|
||||||
|
for(std::list<CHtmlElement>::const_iterator it = Children.begin(); it != Children.end(); ++it)
|
||||||
|
{
|
||||||
|
result += it->toString(tree, depth+1);
|
||||||
|
}
|
||||||
|
if (depth > 0)
|
||||||
|
{
|
||||||
|
result += NLMISC::toString("[%d]", depth);
|
||||||
|
result.append(depth, '-');
|
||||||
|
}
|
||||||
|
result += "</" + Value + ">\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Value.find("\n") == std::string::npos)
|
||||||
|
{
|
||||||
|
result += "{" + Value + "}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result += "{";
|
||||||
|
std::string::size_type start = 0;
|
||||||
|
std::string::size_type pos = Value.find_first_of("\n\r\t");
|
||||||
|
while(pos != std::string::npos)
|
||||||
|
{
|
||||||
|
result += Value.substr(start, pos - start);
|
||||||
|
if (Value[pos] == '\n')
|
||||||
|
{
|
||||||
|
result += "⏎";
|
||||||
|
}
|
||||||
|
else if (Value[pos] == '\t')
|
||||||
|
{
|
||||||
|
result += "⇥";
|
||||||
|
}
|
||||||
|
|
||||||
|
start = pos+1;
|
||||||
|
pos = Value.find_first_of("\n\r\t", start);
|
||||||
|
}
|
||||||
|
result += "}";
|
||||||
|
}
|
||||||
|
if (tree) result += "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
} // namespace
|
Loading…
Reference in New Issue