Merge with develop

--HG--
branch : compatibility-develop
hg/compatibility-develop
Nimetu 6 years ago
commit a76308cbde

@ -0,0 +1,150 @@
// 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_PARSER_H
#define CL_CSS_PARSER_H
#include "nel/misc/types_nl.h"
#include "nel/gui/css_style.h"
#include "nel/gui/css_selector.h"
namespace NLGUI
{
/**
* \brief CSS style parsing
* \date 2019-03-15 10:50 GMT
* \author Meelis Mägi (Nimetu)
*/
class CCssParser {
public:
// parse style declaration, eg "color: red; font-size: 10px;"
static TStyle parseDecls(const std::string &styleString);
// parse css stylesheet
void parseStylesheet(const std::string &cssString, std::vector<CCssStyle::SStyleRule> &rules);
private:
// stylesheet currently parsed
ucstring _Style;
// keep track of current position in _Style
size_t _Position;
std::vector<CCssStyle::SStyleRule> _Rules;
private:
// @media ( .. ) { .. }
void readAtRule();
// a#id.class[attr=val] { .. }
void readRule();
// move past whitespace
void skipWhitespace();
// skip valid IDENT
bool skipIdentifier();
// skip over {}, (), or [] block
void skipBlock();
// skip over string quoted with ' or "
void skipString();
// backslash escape
void escape();
// normalize newline chars and remove comments
void preprocess();
// parse selectors + combinators
std::vector<CCssSelector> parse_selector(const ucstring &sel, std::string &pseudoElement) const;
// parse selector and style
void parseRule(const ucstring &selectorString, const ucstring &styleString);
inline bool is_eof() const
{
return _Position >= _Style.size();
}
inline bool is_whitespace(ucchar ch) const
{
return (ch == (ucchar)' ' || ch == (ucchar)'\t' || ch == (ucchar)'\n');
}
inline bool is_hex(ucchar ch) const
{
return ((ch >= (ucchar)'0' && ch <= (ucchar)'9') ||
(ch >= (ucchar)'a' && ch <= (ucchar)'f') ||
(ch >= (ucchar)'A' && ch <= (ucchar)'F'));
}
inline bool maybe_escape() const
{
// escaping newline (\n) only allowed inside strings
return (_Style.size() - _Position) >= 1 && _Style[_Position] == (ucchar)'\\' && _Style[_Position+1] != '\n';
}
inline bool is_quote(ucchar ch) const
{
return ch== (ucchar)'"' || ch == (ucchar)'\'';
}
inline bool is_block_open(ucchar ch) const
{
return ch == (ucchar)'{' || ch == (ucchar)'[' || ch == (ucchar)'(';
}
inline bool is_block_close(ucchar ch, ucchar open) const
{
return ((open == '{' && ch == (ucchar)'}') ||
(open == '[' && ch == (ucchar)']') ||
(open == '(' && ch == (ucchar)')'));
}
inline bool is_comment_open() const
{
if (_Position+1 > _Style.size())
return false;
return _Style[_Position] == (ucchar)'/' && _Style[_Position+1] == (ucchar)'*';
}
inline bool is_nonascii(ucchar ch) const
{
return ch >= 0x80 /*&& ch <= 255*/;
}
inline bool is_alpha(ucchar ch) const
{
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
}
inline bool is_digit(ucchar ch) const
{
return ch >= '0' && ch <= '9';
}
inline bool is_nmchar(ucchar ch) const
{
// checking escape here does not check if next char is '\n' or not
return ch == '_' || ch == '-' || is_alpha(ch) || is_digit(ch) || is_nonascii(ch) || ch == '\\'/*is_escape(ch)*/;
}
};
}//namespace
#endif // CL_CSS_PARSER_H

@ -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,221 @@
// 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_STYLE_H
#define CL_CSS_STYLE_H
#include "nel/misc/types_nl.h"
#include "nel/misc/rgba.h"
#include "nel/gui/css_selector.h"
namespace NLGUI
{
class CHtmlElement;
typedef std::map<std::string, std::string> TStyle;
/**
* \brief CSS style rules
* \date 2019-03-15 10:50 GMT
* \author Meelis Mägi (Nimetu)
*/
class CStyleParams
{
public:
struct STextShadow
{
public:
STextShadow(bool enabled = false, bool outline = false, sint32 x=1, sint32 y=1, NLMISC::CRGBA color=NLMISC::CRGBA::Black)
: Enabled(enabled), Outline(outline), X(x), Y(y), Color(color)
{ }
bool Enabled;
bool Outline;
sint32 X;
sint32 Y;
NLMISC::CRGBA Color;
};
public:
CStyleParams () : FontFamily(""), TextColor(255,255,255,255), TextShadow()
{
FontSize=10;
FontWeight=400;
FontOblique=false;
Underlined=false;
StrikeThrough=false;
GlobalColor=false;
Width=-1;
Height=-1;
MaxWidth=-1;
MaxHeight=-1;
BorderWidth=1;
BackgroundColor=NLMISC::CRGBA::Black;
BackgroundColorOver=NLMISC::CRGBA::Black;
}
bool hasStyle(const std::string &key) const
{
return StyleRules.find(key) != StyleRules.end();
}
std::string getStyle(const std::string &key) const
{
TStyle::const_iterator it = StyleRules.find(key);
return (it != StyleRules.end() ? it->second : "");
}
public:
uint FontSize;
uint FontWeight;
bool FontOblique;
std::string FontFamily;
NLMISC::CRGBA TextColor;
STextShadow TextShadow;
bool GlobalColor;
bool Underlined;
bool StrikeThrough;
sint32 Width;
sint32 Height;
sint32 MaxWidth;
sint32 MaxHeight;
sint32 BorderWidth;
NLMISC::CRGBA BackgroundColor;
NLMISC::CRGBA BackgroundColorOver;
std::string WhiteSpace;
std::string TextAlign;
std::string VerticalAlign;
TStyle StyleRules;
};
class CCssStyle {
public:
struct SStyleRule {
std::vector<CCssSelector> Selector;
TStyle Properties;
// pseudo element like ':before'
std::string PseudoElement;
// returns selector specificity
uint specificity() const;
};
// 'browser' style, overwriten with '<html>'
CStyleParams Root;
// current element style
CStyleParams Current;
// known style rules sorted by specificity
std::vector<SStyleRule> _StyleRules;
private:
std::vector<CStyleParams> _StyleStack;
// test if str is one of "thin/medium/thick" and return its pixel value
bool scanCssLength(const std::string& str, uint32 &px) const;
// read style attribute
void getStyleParams(const std::string &styleString, CStyleParams &style, const CStyleParams &current) const;
void getStyleParams(const TStyle &styleRules, CStyleParams &style, const CStyleParams &current) const;
// merge src into dest by overwriting key in dest
void merge(TStyle &dst, const TStyle &src) const;
// match selector to dom path
bool match(const std::vector<CCssSelector> &selector, const CHtmlElement &elm) const;
// parse 'background' into 'background-color', 'background-image', etc
void parseBackgroundShorthand(const std::string &value, CStyleParams &style) const;
public:
void reset();
// parse <style>..</style> tag or css file content
void parseStylesheet(const std::string &styleString);
// set element style from matching css rules
void getStyleFor(CHtmlElement &elm) const;
inline uint getFontSizeSmaller() const
{
if (Current.FontSize < 5)
return 3;
return Current.FontSize-2;
}
sint styleStackIndex = 0;
inline void pushStyle()
{
styleStackIndex++;
_StyleStack.push_back(Current);
Current.Width=-1;
Current.Height=-1;
Current.MaxWidth=-1;
Current.MaxHeight=-1;
Current.BorderWidth=1;
Current.StyleRules.clear();
}
inline void popStyle()
{
styleStackIndex--;
if (_StyleStack.empty())
{
Current = Root;
}
else
{
Current = _StyleStack.back();
_StyleStack.pop_back();
}
}
// apply style to this.Root
void applyRootStyle(const std::string &styleString);
void applyRootStyle(const TStyle &styleRules);
// apply style to this.Current
void applyStyle(const std::string &styleString);
void applyStyle(const TStyle &styleRules);
void applyCssMinMax(sint32 &width, sint32 &height, sint32 minw=0, sint32 minh=0, sint32 maxw=0, sint32 maxh=0) const;
// check if current style property matches value
bool checkStyle(const std::string &key, const std::string &val) const
{
return Current.hasStyle(key) && Current.getStyle(key) == val;
}
bool hasStyle(const std::string &key) const
{
return Current.hasStyle(key);
}
std::string getStyle(const std::string &key) const
{
return Current.getStyle(key);
}
};
}//namespace
#endif // CL_CSS_STYLE_H

@ -24,12 +24,12 @@
#include "nel/gui/ctrl_button.h"
#include "nel/gui/group_table.h"
#include "nel/gui/libwww_types.h"
#include "nel/gui/html_element.h"
#include "nel/gui/css_style.h"
// forward declaration
typedef void CURLM;
typedef std::map<std::string, std::string> TStyle;
namespace NLGUI
{
class CCtrlButton;
@ -76,60 +76,8 @@ namespace NLGUI
static SWebOptions options;
// text-shadow
struct STextShadow
{
public:
STextShadow(bool enabled = false, bool outline = false, sint32 x=1, sint32 y=1, NLMISC::CRGBA color=NLMISC::CRGBA::Black)
: Enabled(enabled), Outline(outline), X(x), Y(y), Color(color)
{ }
bool Enabled;
bool Outline;
sint32 X;
sint32 Y;
NLMISC::CRGBA Color;
};
class CStyleParams
{
public:
CStyleParams () : FontFamily(""), TextColor(255,255,255,255), TextShadow()
{
FontSize=10;
FontWeight=400;
FontOblique=false;
Underlined=false;
StrikeThrough=false;
GlobalColor=false;
Width=-1;
Height=-1;
MaxWidth=-1;
MaxHeight=-1;
BorderWidth=1;
BackgroundColor=NLMISC::CRGBA::Black;
BackgroundColorOver=NLMISC::CRGBA::Black;
}
uint FontSize;
uint FontWeight;
bool FontOblique;
std::string FontFamily;
NLMISC::CRGBA TextColor;
STextShadow TextShadow;
bool GlobalColor;
bool Underlined;
bool StrikeThrough;
sint32 Width;
sint32 Height;
sint32 MaxWidth;
sint32 MaxHeight;
sint32 BorderWidth;
NLMISC::CRGBA BackgroundColor;
NLMISC::CRGBA BackgroundColorOver;
};
// ImageDownload system
enum TDataType {ImgType= 0, BnpType};
enum TDataType {ImgType= 0, BnpType, StylesheetType};
enum TImageType {NormalImage=0, OverImage};
// Constructor
@ -150,8 +98,11 @@ namespace NLGUI
// Browse
virtual void browse (const char *url);
// load css from local file and insert into active stylesheet collection
void parseStylesheetFile(const std::string &fname);
// parse html string using libxml2 parser
virtual bool parseHtml(std::string htmlString);
bool parseHtml(const std::string &htmlString);
// Refresh
void refresh();
@ -162,13 +113,8 @@ namespace NLGUI
// Browse error
void browseError (const char *msg);
// stop browse
void stopBrowse ();
bool isBrowsing();
void clean() { stopBrowse(); updateRefreshButton(); removeContent(); }
// Update coords
void updateCoords();
@ -293,8 +239,6 @@ namespace NLGUI
int luaInsertText(CLuaState &ls);
int luaAddString(CLuaState &ls);
int luaAddImage(CLuaState &ls);
int luaBeginElement(CLuaState &ls);
int luaEndElement(CLuaState &ls);
int luaShowDiv(CLuaState &ls);
int luaParseHtml(CLuaState &ls);
int luaRenderHtml(CLuaState &ls);
@ -309,8 +253,6 @@ namespace NLGUI
REFLECT_LUA_METHOD("insertText", luaInsertText)
REFLECT_LUA_METHOD("addString", luaAddString)
REFLECT_LUA_METHOD("addImage", luaAddImage)
REFLECT_LUA_METHOD("beginElement", luaBeginElement)
REFLECT_LUA_METHOD("endElement", luaEndElement)
REFLECT_LUA_METHOD("showDiv", luaShowDiv)
REFLECT_LUA_METHOD("parseHtml", luaParseHtml)
REFLECT_LUA_METHOD("renderHtml", luaRenderHtml)
@ -326,26 +268,20 @@ namespace NLGUI
// \name callback from libwww
// Begin of the parsing of a HTML document
// Begin of the rendering of a HTML document
virtual void beginBuild ();
// End of the parsing of a HTML document
// End of the rendering of a HTML document
virtual void endBuild ();
// A new text block has been parsed
virtual void addText (const char * buf, int len);
// A new begin HTML element has been parsed (<IMG> for exemple)
virtual void beginElement (uint element_number, const std::vector<bool> &present, const std::vector<const char *> &value);
virtual void beginElement(CHtmlElement &elm);
// A new end HTML element has been parsed (</IMG> for exemple)
virtual void endElement (uint element_number);
// A new begin unparsed element has been found
virtual void beginUnparsedElement(const char *buffer, int length);
// A new end unparsed element has been found
virtual void endUnparsedElement(const char *buffer, int length);
virtual void endElement(CHtmlElement &elm);
// Add GET params to the url
virtual void addHTTPGetParams (std::string &url, bool trustedDomain);
@ -353,12 +289,8 @@ namespace NLGUI
// Add POST params to the libwww list
virtual void addHTTPPostParams (SFormFields &formfields, bool trustedDomain);
// the current request is terminated
virtual void requestTerminated();
// libxml2 html parser functions
void htmlElement(xmlNode *node, int element_number);
void htmlWalkDOM(xmlNode *a_node);
// parse dom node and all child nodes recursively
void renderDOM(CHtmlElement &elm);
// Clear style stack and restore default style
void resetCssStyle();
@ -390,7 +322,7 @@ namespace NLGUI
void addString(const ucstring &str);
// Add an image in the current paragraph
void addImage(const std::string &id, const char *image, bool reloadImg=false, const CStyleParams &style = CStyleParams());
void addImage(const std::string &id, const std::string &img, bool reloadImg=false, const CStyleParams &style = CStyleParams());
// Add a text area in the current paragraph
CInterfaceGroup *addTextArea (const std::string &templateName, const char *name, uint rows, uint cols, bool multiLine, const ucstring &content, uint maxlength);
@ -430,6 +362,12 @@ namespace NLGUI
std::string _DocumentUrl;
std::string _DocumentDomain;
std::string _DocumentHtml; // not updated only set by first render
// If true, then render _DocumentHtml on next update (replaces content)
bool _RenderNextTime;
// true if renderer is waiting for css files to finish downloading (link rel=stylesheet)
bool _WaitingForStylesheet;
// list of css file urls that are queued up for download
std::vector<std::string> _StylesheetQueue;
// Valid base href was found
bool _IgnoreBaseUrlTag;
@ -463,7 +401,6 @@ namespace NLGUI
// Browsing..
bool _Browsing;
bool _Connecting;
double _TimeoutValue; // the timeout in seconds
double _ConnectingTimeout;
sint _RedirectsRemaining;
@ -548,33 +485,13 @@ namespace NLGUI
// IL mode
bool _LI;
// Current active style
CStyleParams _Style;
// Default style
CStyleParams _StyleDefault;
// Nested style stack
std::vector<CStyleParams> _StyleParams;
inline void pushStyle()
{
_StyleParams.push_back(_Style);
}
inline void popStyle()
{
if (_StyleParams.empty())
_Style = _StyleDefault;
else
{
_Style = _StyleParams.back();
_StyleParams.pop_back();
}
}
inline uint getFontSizeSmaller() const
{
if (_Style.FontSize < 5)
return 3;
return _Style.FontSize-2;
}
// Keep track of current element style
CCssStyle _Style;
CHtmlElement _HtmlDOM;
CHtmlElement *_CurrentHTMLElement;
// Backup of CurrentHTMLElement->nextSibling before ::beginElement() is called
// for luaParseHtml() to insert nodes into right place in right order
CHtmlElement *_CurrentHTMLNextSibling;
// Current link
std::vector<std::string> _Link;
@ -638,14 +555,6 @@ namespace NLGUI
return _TR.back();
}
std::vector<STextShadow> _TextShadow;
inline STextShadow getTextShadow() const
{
if (_TextShadow.empty())
return STextShadow();
return _TextShadow.back();
}
// Forms
class CForm
{
@ -807,10 +716,6 @@ namespace NLGUI
typedef std::map<uint32, NLMISC::CRefPtr<CGroupHTML> > TGroupHtmlByUIDMap;
static TGroupHtmlByUIDMap _GroupHtmlByUID;
// read style attribute
void getStyleParams(const std::string &styleString, CStyleParams &style, const CStyleParams &current);
void applyCssMinMax(sint32 &width, sint32 &height, sint32 minw=0, sint32 minh=0, sint32 maxw=0, sint32 maxh=0);
// load and render local html file (from bnp for example)
void doBrowseLocalFile(const std::string &filename);
@ -824,6 +729,11 @@ namespace NLGUI
void buildHTTPPostParams (SFormFields &formfields);
private:
friend class CHtmlParser;
// move src->Children into CurrentHtmlElement.parent.children element
void spliceFragment(std::list<CHtmlElement>::iterator src);
// decode all HTML entities
static ucstring decodeHTMLEntities(const ucstring &str);
@ -847,6 +757,7 @@ namespace NLGUI
{
if (t == ImgType) imgs.push_back(CDataImageDownload(i, style, imagetype));
}
~CDataDownload();
public:
CCurlWWWData *data;
@ -860,11 +771,13 @@ namespace NLGUI
std::vector<CDataImageDownload> imgs;
};
std::vector<CDataDownload> Curls;
std::list<CDataDownload> Curls;
CURLM *MultiCurl;
int RunningCurls;
bool startCurlDownload(CDataDownload &download);
void finishCurlDownload(const CDataDownload &download);
void pumpCurlQueue();
void initImageDownload();
void checkImageDownload();
@ -883,11 +796,100 @@ namespace NLGUI
bool addBnpDownload(std::string url, const std::string &action, const std::string &script, const std::string &md5sum);
std::string localBnpName(const std::string &url);
// add css file from <link href=".." rel="stylesheet"> to download queue
void addStylesheetDownload(std::vector<std::string> links);
// stop all curl downalods (html and data)
void releaseDownloads();
void checkDownloads();
// _CurlWWW download finished
void htmlDownloadFinished(bool success, const std::string &error);
// images, stylesheets, etc finished downloading
void dataDownloadFinished(bool success, const std::string &error, CDataDownload &data);
// HtmlType download finished
void htmlDownloadFinished(const std::string &content, const std::string &type, long code);
// stylesheet finished downloading. if local file does not exist, then it failed (404)
void cssDownloadFinished(const std::string &url, const std::string &local);
// read common table/tr/td parameters and push them to _CellParams
void getCellsParameters(const CHtmlElement &elm, bool inherit);
// render _HtmlDOM
void renderDocument();
// :before, :after rendering
void renderPseudoElement(const std::string &pseudo, const CHtmlElement &elm);
// HTML elements
void htmlA(const CHtmlElement &elm);
void htmlAend(const CHtmlElement &elm);
void htmlBASE(const CHtmlElement &elm);
void htmlBODY(const CHtmlElement &elm);
void htmlBR(const CHtmlElement &elm);
void htmlDD(const CHtmlElement &elm);
void htmlDDend(const CHtmlElement &elm);
//void htmlDEL(const CHtmlElement &elm);
void htmlDIV(const CHtmlElement &elm);
void htmlDIVend(const CHtmlElement &elm);
void htmlDL(const CHtmlElement &elm);
void htmlDLend(const CHtmlElement &elm);
void htmlDT(const CHtmlElement &elm);
void htmlDTend(const CHtmlElement &elm);
//void htmlEM(const CHtmlElement &elm);
void htmlFONT(const CHtmlElement &elm);
void htmlFORM(const CHtmlElement &elm);
void htmlH(const CHtmlElement &elm);
void htmlHend(const CHtmlElement &elm);
void htmlHEAD(const CHtmlElement &elm);
void htmlHEADend(const CHtmlElement &elm);
void htmlHR(const CHtmlElement &elm);
void htmlHTML(const CHtmlElement &elm);
void htmlI(const CHtmlElement &elm);
void htmlIend(const CHtmlElement &elm);
void htmlIMG(const CHtmlElement &elm);
void htmlINPUT(const CHtmlElement &elm);
void htmlLI(const CHtmlElement &elm);
void htmlLIend(const CHtmlElement &elm);
void htmlLUA(const CHtmlElement &elm);
void htmlLUAend(const CHtmlElement &elm);
void htmlMETA(const CHtmlElement &elm);
void htmlOBJECT(const CHtmlElement &elm);
void htmlOBJECTend(const CHtmlElement &elm);
void htmlOL(const CHtmlElement &elm);
void htmlOLend(const CHtmlElement &elm);
void htmlOPTION(const CHtmlElement &elm);
void htmlOPTIONend(const CHtmlElement &elm);
void htmlP(const CHtmlElement &elm);
void htmlPend(const CHtmlElement &elm);
void htmlPRE(const CHtmlElement &elm);
void htmlPREend(const CHtmlElement &elm);
void htmlSCRIPT(const CHtmlElement &elm);
void htmlSCRIPTend(const CHtmlElement &elm);
void htmlSELECT(const CHtmlElement &elm);
void htmlSELECTend(const CHtmlElement &elm);
//void htmlSMALL(const CHtmlElement &elm);
//void htmlSPAN(const CHtmlElement &elm);
//void htmlSTRONG(const CHtmlElement &elm);
void htmlSTYLE(const CHtmlElement &elm);
void htmlSTYLEend(const CHtmlElement &elm);
void htmlTABLE(const CHtmlElement &elm);
void htmlTABLEend(const CHtmlElement &elm);
void htmlTD(const CHtmlElement &elm);
void htmlTDend(const CHtmlElement &elm);
void htmlTEXTAREA(const CHtmlElement &elm);
void htmlTEXTAREAend(const CHtmlElement &elm);
void htmlTH(const CHtmlElement &elm);
void htmlTHend(const CHtmlElement &elm);
void htmlTITLE(const CHtmlElement &elm);
void htmlTITLEend(const CHtmlElement &elm);
void htmlTR(const CHtmlElement &elm);
void htmlTRend(const CHtmlElement &elm);
//void htmlU(const CHtmlElement &elm);
void htmlUL(const CHtmlElement &elm);
void htmlULend(const CHtmlElement &elm);
};
// adapter group that store y offset for inputs inside an html form

@ -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,52 @@
// 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_PARSER_H
#define CL_HTML_PARSER_H
#include "nel/misc/types_nl.h"
namespace NLGUI
{
class CHtmlElement;
/**
* \brief HTML parsing
* \date 2019-03-15 10:50 GMT
* \author Meelis Mägi (Nimetu)
*/
class CHtmlParser
{
public:
bool parseHtml(std::string htmlString) const;
// parse html string into DOM, extract <style> tags into styleString, <link stylesheet> urls into links
void getDOM(std::string htmlString, CHtmlElement &parent, std::string &styleString, std::vector<std::string> &links) const;
private:
// iterate over libxml html tree, build DOM, and join all <style> tags together
void parseNode(xmlNode *a_node, CHtmlElement &parent, std::string &styleString, std::vector<std::string> &links) const;
// read <style> tag and add its content to styleString
void parseStyle(xmlNode *a_node, std::string &styleString) const;
// update parent/sibling in elm.Children
void reindexChilds(CHtmlElement &elm) const;
};
}
#endif

@ -276,9 +276,14 @@ namespace NLGUI
HTML_ATTR(H6,STYLE),
};
#undef HTML_ATTR
// ***************************************************************************
// Read HTML color value from src and set dest
// Can handle #rgb(a), #rrggbb(aa) or rgb()/rgba(), hsl(), hsla() formats
// or color name directly
bool scanHTMLColor(const char *src, NLMISC::CRGBA &dest);
// ***************************************************************************
// Read a CSS length value, return true if one of supported units '%, rem, em, px, pt'
// On failure: 'value' and 'unit' values are undefined
@ -292,6 +297,9 @@ namespace NLGUI
// Parse a HTML color
NLMISC::CRGBA getColor (const char *color);
// return css color in rgba() format
std::string getRGBAString(const NLMISC::CRGBA &color);
// ***************************************************************************
const std::string &setCurrentDomain(const std::string &uri);

@ -276,7 +276,6 @@ template <class T> T trimRightWhiteSpaces (const T &str)
return str.substr (0, end);
}
// remove spaces and tabs at the begin and end of the string
template <class T> T trimSeparators (const T &str)
{
@ -290,6 +289,17 @@ template <class T> T trimSeparators (const T &str)
return str.substr (start, end-start);
}
// if both first and last char are quotes (' or "), then remove them
template <class T> T trimQuotes (const T&str)
{
typename T::size_type size = str.size();
if (size == 0)
return str;
if (str[0] != str[size-1] && (str[0] != '"' || str[0] != '\''))
return str;
return str.substr(1, size - 1);
}
//////////////////////////////////////////////////////////////////////////
// **** DEPRECATED *****: PLEASE DON'T USE THESE METHODS BUT FUNCTIONS ABOVE toLower() and toUpper()
//////////////////////////////////////////////////////////////////////////

@ -0,0 +1,716 @@
// 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_parser.h"
#include "nel/gui/css_style.h"
#include "nel/gui/css_selector.h"
using namespace NLMISC;
#ifdef DEBUG_NEW
#define new DEBUG_NEW
#endif
namespace NLGUI
{
// ***************************************************************************
// Parse style declarations style, eg. "color:red; font-size: 10px;"
//
// key is converted to lowercase
// value is left as is
TStyle CCssParser::parseDecls(const std::string &styleString)
{
TStyle styles;
std::vector<std::string> elements;
NLMISC::splitString(styleString, ";", elements);
for(uint i = 0; i < elements.size(); ++i)
{
std::string::size_type pos;
pos = elements[i].find_first_of(':');
if (pos != std::string::npos)
{
std::string key = trim(toLower(elements[i].substr(0, pos)));
std::string value = trim(elements[i].substr(pos+1));
styles[key] = value;
}
}
return styles;
}
// ***************************************************************************
// Parse stylesheet, eg content from main.css file
//
// Return all found rules
void CCssParser::parseStylesheet(const std::string &cssString, std::vector<CCssStyle::SStyleRule> &result)
{
_Rules.clear();
_Style.clear();
_Style.fromUtf8(cssString);
preprocess();
_Position = 0;
while(!is_eof())
{
skipWhitespace();
if (_Style[_Position] == (ucchar)'@')
readAtRule();
else
readRule();
}
result.insert(result.end(), _Rules.begin(), _Rules.end());
_Rules.clear();
}
// ***************************************************************************
// Parse selector with style string
// selector: "a#id .class"
// style: "color: red; font-size: 10px;"
//
// @internal
void CCssParser::parseRule(const ucstring &selectorString, const ucstring &styleString)
{
std::vector<ucstring> selectors;
NLMISC::explode(selectorString, ucstring(","), selectors);
TStyle props;
props = parseDecls(styleString.toUtf8());
// duplicate props to each selector in selector list,
// example 'div > p, h1' creates 'div>p' and 'h1'
for(uint i=0; i<selectors.size(); ++i)
{
CCssStyle::SStyleRule rule;
rule.Selector = parse_selector(trim(selectors[i]), rule.PseudoElement);
rule.Properties = props;
if (!rule.Selector.empty())
{
_Rules.push_back(rule);
}
}
}
// ***************************************************************************
// Skip over at-rule
// @import ... ;
// @charset ... ;
// @media query { .. }
//
// @internal
void CCssParser::readAtRule()
{
// skip '@'
_Position++;
// skip 'import', 'media', etc
skipIdentifier();
// skip at-rule statement
while(!is_eof() && _Style[_Position] != (ucchar)';')
{
if (maybe_escape())
{
escape();
}
else if (is_quote(_Style[_Position]))
{
skipString();
}
else if (is_block_open(_Style[_Position]))
{
bool mustBreak = (_Style[_Position] == '{');
skipBlock();
if(mustBreak)
{
break;
}
}
else
{
_Position++;
}
}
// skip ';' or '}'
_Position++;
}
// ***************************************************************************
// skip over "elm#id.selector[attr]:peseudo, .sel2 { rule }" block
// @internal
void CCssParser::readRule()
{
size_t start;
// selector
start = _Position;
while(!is_eof())
{
if (maybe_escape())
_Position++;
else if (is_quote(_Style[_Position]))
skipString();
else if (_Style[_Position] == (ucchar)'[')
skipBlock();
else if (_Style[_Position] == (ucchar)'{')
break;
else
_Position++;
}
if (!is_eof())
{
ucstring selector;
selector.append(_Style, start, _Position - start);
skipWhitespace();
// declaration block
start = _Position;
skipBlock();
if (_Position <= _Style.size())
{
ucstring rules;
rules.append(_Style, start + 1, _Position - start - 2);
parseRule(selector, rules);
}
}
}
// ***************************************************************************
// skip over \abcdef escaped sequence or escaped newline char
// @internal
void CCssParser::escape()
{
// skip '\'
_Position++;
if (is_hex(_Style[_Position]))
{
// TODO: '\abc def' should be considered one string
for(uint i=0; i<6 && is_hex(_Style[_Position]); i++)
_Position++;
if (_Style[_Position] == (ucchar)' ')
_Position++;
}
else if (_Style[_Position] != 0x0A)
_Position++;
}
// ***************************************************************************
// @internal
bool CCssParser::skipIdentifier()
{
size_t start = _Position;
bool valid = true;
while(!is_eof() && valid)
{
if (maybe_escape())
{
escape();
continue;
}
else if (is_alpha(_Style[_Position]))
{
// valid
}
else if (is_digit(_Style[_Position]))
{
if (_Position == start)
{
// cannot start with digit
valid = false;
}
else if ((_Position - start) == 0 && _Style[_Position-1] == (ucchar)'-')
{
// cannot start with -#
valid = false;
}
}
else if (_Style[_Position] == (ucchar)'_')
{
// valid
}
else if (_Style[_Position] >= 0x0080)
{
// valid
}
else if (_Style[_Position] == (ucchar)'-')
{
if ((_Position - start) == 1 && _Style[_Position-1] == (ucchar)'-')
{
// cannot start with --
valid = false;
}
}
else
{
// we're done
break;
}
_Position++;
}
return valid && !is_eof();
}
// ***************************************************************************
// skip over (..), [..], or {..} blocks
// @internal
void CCssParser::skipBlock()
{
ucchar startChar = _Style[_Position];
// block start
_Position++;
while(!is_eof() && !is_block_close(_Style[_Position], startChar))
{
if (maybe_escape())
// skip backslash and next char
_Position += 2;
else if (is_quote(_Style[_Position]))
skipString();
else if (is_block_open(_Style[_Position]))
skipBlock();
else
_Position++;
}
// block end
_Position++;
}
// ***************************************************************************
// skip over quoted string
// @internal
void CCssParser::skipString()
{
ucchar endChar = _Style[_Position];
// quote start
_Position++;
while(!is_eof() && _Style[_Position] != endChar)
{
if (maybe_escape())
_Position++;
_Position++;
}
// quote end
_Position++;
}
// ***************************************************************************
// @internal
void CCssParser::skipWhitespace()
{
while(!is_eof() && is_whitespace(_Style[_Position]))
_Position++;
}
// ***************************************************************************
// parse selector list
// @internal
std::vector<CCssSelector> CCssParser::parse_selector(const ucstring &sel, std::string &pseudoElement) const
{
std::vector<CCssSelector> result;
CCssSelector current;
pseudoElement.clear();
bool failed = false;
ucstring::size_type start = 0, pos = 0;
while(pos < sel.size())
{
ucstring uc;
uc = sel[pos];
if (is_nmchar(sel[pos]) && current.empty())
{
pos++;
while(pos < sel.size() && is_nmchar(sel[pos]))
pos++;
current.Element = toLower(sel.substr(start, pos - start).toUtf8());
start = pos;
continue;
}
if(sel[pos] == '#')
{
pos++;
start=pos;
while(pos < sel.size() && is_nmchar(sel[pos]))
pos++;
current.Id = toLower(sel.substr(start, pos - start).toUtf8());
start = pos;
}
else if (sel[pos] == '.')
{
pos++;
start=pos;
// .classA.classB
while(pos < sel.size() && (is_nmchar(sel[pos]) || sel[pos] == '.'))
pos++;
current.setClass(toLower(sel.substr(start, pos - start).toUtf8()));
start = pos;
}
else if (sel[pos] == '[')
{
pos++;
start = pos;
if (is_whitespace(sel[pos]))
{
while(pos < sel.size() && is_whitespace(sel[pos]))
pos++;
start = pos;
}
ucstring key;
ucstring value;
ucchar op = ' ';
// key
while(pos < sel.size() && is_nmchar(sel[pos]))
pos++;
key = sel.substr(start, pos - start);
if (pos == sel.size()) break;
if (is_whitespace(sel[pos]))
{
while(pos < sel.size() && is_whitespace(sel[pos]))
pos++;
if (pos == sel.size()) break;
}
if (sel[pos] == ']')
{
current.addAttribute(key.toUtf8());
}
else
{
// operand
op = sel[pos];
if (op == '~' || op == '|' || op == '^' || op == '$' || op == '*')
{
pos++;
if (pos == sel.size()) break;
}
// invalid rule?, eg [attr^value]
if (sel[pos] != '=')
{
while(pos < sel.size() && sel[pos] != ']')
pos++;
if (pos == sel.size()) break;
start = pos;
}
else
{
// skip '='
pos++;
if (is_whitespace(sel[pos]))
{
while(pos < sel.size() && is_whitespace(sel[pos]))
pos++;
if (pos == sel.size()) break;
}
// value
start = pos;
bool quote = false;
char quoteOpen;
while(pos < sel.size())
{
if (sel[pos] == '\'' || sel[pos] == '"')
{
// value is quoted
start = pos;
pos++;
while(pos < sel.size() && sel[pos] != sel[start])
{
if (sel[pos] == '\\')
{
pos++;
}
pos++;
}
if (pos == sel.size()) break;
value = sel.substr(start + 1, pos - start - 1);
break;
}
else if (sel[pos] == '\\')
{
pos++;
}
else if (!quote && sel[pos] == ']')
{
// unquoted value
value = sel.substr(start, pos - start);
break;
}
pos++;
} // while 'value'
// TODO: scan for sel[pos] == ']'
if (pos == sel.size()) break;
// whitespace between quote and ], ie '[ attr $= "val" ]'
if (sel[pos] != ']')
{
while(pos < sel.size() && sel[pos] != ']')
pos++;
}
if (pos == sel.size()) break;
current.addAttribute(key.toUtf8(), value.toUtf8(), (char)op);
} // op error
} // no value
// skip ']'
pos++;
start = pos;
}
else if (sel[pos] == ':')
{
pos++;
start=pos;
// pseudo element, eg '::before'
if (pos < sel.size() && sel[pos] == ':')
{
pos++;
}
// :first-child
// :nth-child(2n+0)
// :not(h1, div#main)
// :not(:nth-child(2n+0))
// has no support for quotes, eg :not(h1[attr=")"]) fails
while(pos < sel.size() && (is_nmchar(sel[pos]) || sel[pos] == '('))
{
if (sel[pos] == '(')
{
uint open = 1;
pos++;
while(pos < sel.size() && open > 0)
{
if (sel[pos] == ')')
open--;
else if (sel[pos] == '(')
open++;
pos++;
}
break;
}
else
{
pos++;
}
}
std::string key = toLower(sel.substr(start, pos - start).toUtf8());
if (key.empty())
{
failed = true;
break;
}
if (key[0] == ':' || key == "after" || key == "before" || key == "cue" || key == "first-letter" || key == "first-line")
{
if (!pseudoElement.empty())
{
failed = true;
break;
}
if (key[0] != ':')
{
pseudoElement = ":" + key;
}
else
{
pseudoElement = key;
}
}
else
{
current.addPseudoClass(key);
}
start = pos;
}
else if (!current.empty())
{
// pseudo element like ':before' can only be set on the last selector
// user action pseudo classes can be used after pseudo element (ie, :focus, :hover)
// there is no support for those and its safe to just fail the selector
if (!result.empty() && !pseudoElement.empty())
{
failed = true;
break;
}
// start new selector as combinator is part of next selector
result.push_back(current);
current = CCssSelector();
// detect and remove whitespace around combinator, eg ' > '
bool isSpace = is_whitespace(sel[pos]);;
while(pos < sel.size() && is_whitespace(sel[pos]))
pos++;
if (sel[pos] == '>' || sel[pos] == '+' || sel[pos] == '~')
{
current.Combinator = sel[pos];
pos++;
while(pos < sel.size() && is_whitespace(sel[pos]))
pos++;
}
else if (isSpace)
{
current.Combinator = ' ';
}
else
{
// unknown
current.Combinator = sel[pos];
pos++;
}
start = pos;
}
else
{
pos++;
}
}
if (failed)
{
result.clear();
}
else if (result.empty() || !current.empty())
{
// pseudo element like ':before' can only be set on the last selector
if (!result.empty() && !pseudoElement.empty())
{
// failed
result.clear();
}
else
{
result.push_back(current);
}
}
return result;
}
// ***************************************************************************
// @internal
void CCssParser::preprocess()
{
_Position = 0;
size_t start;
size_t charsLeft;
bool quote = false;
ucchar quoteChar;
while(!is_eof())
{
charsLeft = _Style.size() - _Position - 1;
// FF, CR
if (_Style[_Position] == 0x0C || _Style[_Position] == 0x0D)
{
uint len = 1;
// CR, LF
if (charsLeft >= 1 && _Style[_Position] == 0x0D && _Style[_Position+1] == 0x0A)
len++;
ucstring tmp;
tmp += 0x000A;
_Style.replace(_Position, 1, tmp);
}
else if (_Style[_Position] == 0x00)
{
// Unicode replacement character
_Style[_Position] = 0xFFFD;
}
else
{
// strip comments for easier parsing
if (_Style[_Position] == '\\')
{
_Position++;
}
else if (is_quote(_Style[_Position]))
{
if (!quote)
quoteChar = _Style[_Position];
if (quote && _Style[_Position] == quoteChar)
quote = !quote;
}
else if (!quote && is_comment_open())
{
size_t pos = _Style.find(ucstring("*/"), _Position + 2);
if (pos == std::string::npos)
pos = _Style.size();
_Style.erase(_Position, pos - _Position + 2);
ucstring uc;
uc = _Style[_Position];
// _Position is already at correct place
continue;
}
}
_Position++;
}
}
} // namespace

@ -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

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

@ -17,13 +17,14 @@
#include "stdpch.h"
#include "nel/gui/html_parser.h"
#include <string>
#include <libxml/HTMLparser.h>
#include "nel/misc/types_nl.h"
#include "nel/gui/libwww.h"
#include "nel/gui/group_html.h"
#include "nel/gui/lua_ihm.h"
using namespace std;
using namespace NLMISC;
@ -34,71 +35,40 @@ using namespace NLMISC;
namespace NLGUI
{
// ***************************************************************************
void CGroupHTML::htmlElement(xmlNode *node, int element_number)
void CHtmlParser::parseStyle(xmlNode *a_node, std::string &styleString) const
{
SGML_dtd *HTML_DTD = HTML_dtd ();
if (element_number < HTML_ELEMENTS)
xmlNode *node = a_node;
while(node)
{
CXMLAutoPtr ptr;
// load attributes into libwww structs
std::vector<bool> present;
std::vector<const char *>value;
std::string strvalues[MAX_ATTRIBUTES];
present.resize(30, false);
value.resize(30);
uint nbAttributes = std::min(MAX_ATTRIBUTES, HTML_DTD->tags[element_number].number_of_attributes);
for(uint i=0; i<nbAttributes; i++)
if (node->type == XML_CDATA_SECTION_NODE)
{
std::string name;
name = toLower(std::string(HTML_DTD->tags[element_number].attributes[i].name));
ptr = xmlGetProp(node, (const xmlChar *)name.c_str());
if (ptr)
{
// copy xmlChar to string (xmlChar will be released)
strvalues[i] = (const char *)(ptr);
// now use string pointer in value[] array
value[i] = strvalues[i].c_str();
present[i] = true;
}
styleString += (const char*)node->content;
}
else
{
nlwarning("<style> tag has child elements other than cdata[%d]", node->type);
}
beginElement(element_number, present, value);
}
else
{
beginUnparsedElement((const char *)(node->name), xmlStrlen(node->name));
}
// recursive - text content / child nodes
htmlWalkDOM(node->children);
// closing tag
if (element_number < HTML_ELEMENTS)
{
endElement(element_number);
}
else
{
endUnparsedElement((const char *)(node->name), xmlStrlen(node->name));
node = node->next;
}
}
// ***************************************************************************
// recursive function to walk html document
void CGroupHTML::htmlWalkDOM(xmlNode *a_node)
void CHtmlParser::parseNode(xmlNode *a_node, CHtmlElement &parent, std::string &styleString, std::vector<std::string> &links) const
{
SGML_dtd *HTML_DTD = HTML_dtd ();
uint childIndex = 0;
uint element_number;
xmlNode *node = a_node;
while(node)
{
if (node->type == XML_TEXT_NODE)
{
addText((const char *)(node->content), xmlStrlen(node->content));
parent.Children.push_back(CHtmlElement(CHtmlElement::TEXT_NODE, (const char*)(node->content)));
}
else
if (node->type == XML_ELEMENT_NODE)
@ -110,7 +80,123 @@ namespace NLGUI
break;
}
htmlElement(node, element_number);
// get pointer to previous sibling
CHtmlElement *prevSibling = NULL;
if (!parent.Children.empty())
{
// skip text nodes
for(std::list<CHtmlElement>::reverse_iterator it = parent.Children.rbegin(); it != parent.Children.rend(); ++it)
{
if (it->Type == CHtmlElement::ELEMENT_NODE)
{
prevSibling = &(*it);
break;
}
}
}
parent.Children.push_back(CHtmlElement(CHtmlElement::ELEMENT_NODE, toLower((const char*)node->name)));
CHtmlElement &elm = parent.Children.back();
elm.ID = element_number;
elm.parent = &parent;
elm.childIndex = childIndex;
// previous/next sibling that is ELEMENT_NODE
elm.previousSibling = prevSibling;
if (prevSibling)
{
prevSibling->nextSibling = &parent.Children.back();
}
childIndex++;
// TODO: harvest <link type="css">, <style>, <img>
elm.Attributes.clear();
for (xmlAttr *cur_attr = node->properties; cur_attr; cur_attr = cur_attr->next) {
std::string key(toLower((const char *)(cur_attr->name)));
std::string value;
if (cur_attr->children)
{
value = (const char *)(cur_attr->children->content);
}
elm.Attributes[key] = value;
}
if (elm.hasAttribute("class"))
{
std::vector<std::string> parts;
NLMISC::splitString(elm.getAttribute("class"), " ", parts);
for(uint i = 0; i<parts.size();++i)
{
elm.ClassNames.insert(toLower(trim(parts[i])));
}
}
if (elm.Value == "style")
{
// <style type="text/css" media="all, screen">
// ...
// </style>
bool useStyle = true;
if (elm.hasAttribute("media"))
{
std::string media = trim(toLower(elm.Attributes["media"]));
useStyle = media.empty() || media.find("all") != std::string::npos || media.find("screen") != std::string::npos;
// <style media="ryzom"> for ingame browser
useStyle = useStyle || media == "ryzom";
}
if (useStyle)
{
parseStyle(node->children, styleString);
}
// style tag is kept in dom
}
if (elm.Value == "link" && elm.getAttribute("rel") == "stylesheet")
{
bool useStyle = true;
if (elm.hasAttribute("media"))
{
std::string media = trim(toLower(elm.Attributes["media"]));
useStyle = media.empty() || media.find("all") != std::string::npos || media.find("screen") != std::string::npos;
// <style media="ryzom"> for ingame browser
useStyle = useStyle || media == "ryzom";
}
if (useStyle)
{
links.push_back(elm.getAttribute("href"));
}
// link tag is kept in dom
}
else if (node->children)
{
parseNode(node->children, elm, styleString, links);
// must cleanup nested tags that libxml2 does not fix
// dt without end tag: <dl><dt><dt></dl>
// dd without end tag: <dl><dd><dd></dl>
if (!elm.Children.empty() && (elm.Value == "dt" || elm.Value == "dd"))
{
std::string tag = elm.Value;
std::list<CHtmlElement>::iterator it;
for(it = elm.Children.begin(); it != elm.Children.end(); ++it)
{
if (it->Type == CHtmlElement::ELEMENT_NODE && it->Value == tag)
{
// relocate this and remaining elements over to parent
parent.Children.splice(parent.Children.end(), elm.Children, it, elm.Children.end());
break;
}
}
elm.reindexChilds();
parent.reindexChilds();
}
}
}
// move into next sibling
@ -297,13 +383,13 @@ namespace NLGUI
}
// ***************************************************************************
bool CGroupHTML::parseHtml(std::string htmlString)
void CHtmlParser::getDOM(std::string htmlString, CHtmlElement &dom, std::string &styleString, std::vector<std::string> &links) const
{
htmlParserCtxtPtr parser = htmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL, XML_CHAR_ENCODING_UTF8);
if (!parser)
{
nlwarning("Creating html parser context failed");
return false;
return;
}
htmlCtxtUseOptions(parser, HTML_PARSE_NOBLANKS | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING | HTML_PARSE_NONET);
@ -314,45 +400,28 @@ namespace NLGUI
htmlParseChunk(parser, htmlString.c_str(), htmlString.size(), 0);
htmlParseChunk(parser, "", 0, 1);
bool success = true;
if (parser->myDoc)
{
xmlNode *root = xmlDocGetRootElement(parser->myDoc);
if (root)
{
htmlWalkDOM(root);
styleString.clear();
parseNode(root, dom, styleString, links);
}
else
{
nlwarning("html root node failed");
success = false;
}
xmlFreeDoc(parser->myDoc);
}
else
{
nlwarning("htmlstring parsing failed");
success = false;
}
htmlFreeParserCtxt(parser);
if (success)
_DocumentHtml = htmlString;
return success;
}
// ***************************************************************************
int CGroupHTML::luaParseHtml(CLuaState &ls)
{
const char *funcName = "parseHtml";
CLuaIHM::checkArgCount(ls, funcName, 1);
CLuaIHM::checkArgType(ls, funcName, 1, LUA_TSTRING);
std::string html = ls.toString(1);
parseHtml(html);
return 0;
}
}

@ -345,6 +345,373 @@ namespace NLGUI
}
}
static bool isHexa(char c)
{
return isdigit(c) || (tolower(c) >= 'a' && tolower(c) <= 'f');
}
static uint8 convertHexa(char c)
{
return (uint8) (tolower(c) - (isdigit(c) ? '0' : ('a' - 10)));
}
// scan a color component, and return pointer to next position
static const char *scanColorComponent(const char *src, uint8 &intensity)
{
if (!src) return NULL;
if (!isHexa(*src)) return NULL;
uint8 value = convertHexa(*src++) << 4;
if (!isHexa(*src)) return NULL;
value += convertHexa(*src++);
intensity = value;
return src;
}
static float hueToRgb(float m1, float m2, float h)
{
if (h < 0) h += 1.0f;
if (h > 1) h -= 1.0f;
if (h*6 < 1.0f) return m1 + (m2 - m1)*h*6;
if (h*2 < 1.0f) return m2;
if (h*3 < 2.0f) return m1 + (m2 - m1) * (2.0f/3.0f - h)*6;
return m1;
}
static void hslToRgb(float h, float s, float l, CRGBA &result)
{
float m1, m2;
if (l <= 0.5f)
m2 = l * (s + 1.0f);
else
m2 = l + s - l * s;
m1 = l*2 - m2;
result.R = 255 * hueToRgb(m1, m2, h + 1.0f/3.0f);
result.G = 255 * hueToRgb(m1, m2, h);
result.B = 255 * hueToRgb(m1, m2, h - 1.0f/3.0f);
result.A = 255;
}
class CNameToCol
{
public:
const char *Name;
CRGBA Color;
CNameToCol(const char *name, CRGBA color) : Name(name), Color(color) {}
};
static CNameToCol htmlColorNameToRGBA[] =
{
CNameToCol("AliceBlue", CRGBA(0xF0, 0xF8, 0xFF)),
CNameToCol("AntiqueWhite", CRGBA(0xFA, 0xEB, 0xD7)),
CNameToCol("Aqua", CRGBA(0x00, 0xFF, 0xFF)),
CNameToCol("Aquamarine", CRGBA(0x7F, 0xFF, 0xD4)),
CNameToCol("Azure", CRGBA(0xF0, 0xFF, 0xFF)),
CNameToCol("Beige", CRGBA(0xF5, 0xF5, 0xDC)),
CNameToCol("Bisque", CRGBA(0xFF, 0xE4, 0xC4)),
CNameToCol("Black", CRGBA(0x00, 0x00, 0x00)),
CNameToCol("BlanchedAlmond", CRGBA(0xFF, 0xEB, 0xCD)),
CNameToCol("Blue", CRGBA(0x00, 0x00, 0xFF)),
CNameToCol("BlueViolet", CRGBA(0x8A, 0x2B, 0xE2)),
CNameToCol("Brown", CRGBA(0xA5, 0x2A, 0x2A)),
CNameToCol("BurlyWood", CRGBA(0xDE, 0xB8, 0x87)),
CNameToCol("CadetBlue", CRGBA(0x5F, 0x9E, 0xA0)),
CNameToCol("Chartreuse", CRGBA(0x7F, 0xFF, 0x00)),
CNameToCol("Chocolate", CRGBA(0xD2, 0x69, 0x1E)),
CNameToCol("Coral", CRGBA(0xFF, 0x7F, 0x50)),
CNameToCol("CornflowerBlue", CRGBA(0x64, 0x95, 0xED)),
CNameToCol("Cornsilk", CRGBA(0xFF, 0xF8, 0xDC)),
CNameToCol("Crimson", CRGBA(0xDC, 0x14, 0x3C)),
CNameToCol("Cyan", CRGBA(0x00, 0xFF, 0xFF)),
CNameToCol("DarkBlue", CRGBA(0x00, 0x00, 0x8B)),
CNameToCol("DarkCyan", CRGBA(0x00, 0x8B, 0x8B)),
CNameToCol("DarkGoldenRod", CRGBA(0xB8, 0x86, 0x0B)),
CNameToCol("DarkGray", CRGBA(0xA9, 0xA9, 0xA9)),
CNameToCol("DarkGreen", CRGBA(0x00, 0x64, 0x00)),
CNameToCol("DarkKhaki", CRGBA(0xBD, 0xB7, 0x6B)),
CNameToCol("DarkMagenta", CRGBA(0x8B, 0x00, 0x8B)),
CNameToCol("DarkOliveGreen", CRGBA(0x55, 0x6B, 0x2F)),
CNameToCol("Darkorange", CRGBA(0xFF, 0x8C, 0x00)),
CNameToCol("DarkOrchid", CRGBA(0x99, 0x32, 0xCC)),
CNameToCol("DarkRed", CRGBA(0x8B, 0x00, 0x00)),
CNameToCol("DarkSalmon", CRGBA(0xE9, 0x96, 0x7A)),
CNameToCol("DarkSeaGreen", CRGBA(0x8F, 0xBC, 0x8F)),
CNameToCol("DarkSlateBlue", CRGBA(0x48, 0x3D, 0x8B)),
CNameToCol("DarkSlateGray", CRGBA(0x2F, 0x4F, 0x4F)),
CNameToCol("DarkTurquoise", CRGBA(0x00, 0xCE, 0xD1)),
CNameToCol("DarkViolet", CRGBA(0x94, 0x00, 0xD3)),
CNameToCol("DeepPink", CRGBA(0xFF, 0x14, 0x93)),
CNameToCol("DeepSkyBlue", CRGBA(0x00, 0xBF, 0xFF)),
CNameToCol("DimGray", CRGBA(0x69, 0x69, 0x69)),
CNameToCol("DodgerBlue", CRGBA(0x1E, 0x90, 0xFF)),
CNameToCol("Feldspar", CRGBA(0xD1, 0x92, 0x75)),
CNameToCol("FireBrick", CRGBA(0xB2, 0x22, 0x22)),
CNameToCol("FloralWhite", CRGBA(0xFF, 0xFA, 0xF0)),
CNameToCol("ForestGreen", CRGBA(0x22, 0x8B, 0x22)),
CNameToCol("Fuchsia", CRGBA(0xFF, 0x00, 0xFF)),
CNameToCol("Gainsboro", CRGBA(0xDC, 0xDC, 0xDC)),
CNameToCol("GhostWhite", CRGBA(0xF8, 0xF8, 0xFF)),
CNameToCol("Gold", CRGBA(0xFF, 0xD7, 0x00)),
CNameToCol("GoldenRod", CRGBA(0xDA, 0xA5, 0x20)),
CNameToCol("Gray", CRGBA(0x80, 0x80, 0x80)),
CNameToCol("Green", CRGBA(0x00, 0x80, 0x00)),
CNameToCol("GreenYellow", CRGBA(0xAD, 0xFF, 0x2F)),
CNameToCol("HoneyDew", CRGBA(0xF0, 0xFF, 0xF0)),
CNameToCol("HotPink", CRGBA(0xFF, 0x69, 0xB4)),
CNameToCol("IndianRed ", CRGBA(0xCD, 0x5C, 0x5C)),
CNameToCol("Indigo ", CRGBA(0x4B, 0x00, 0x82)),
CNameToCol("Ivory", CRGBA(0xFF, 0xFF, 0xF0)),
CNameToCol("Khaki", CRGBA(0xF0, 0xE6, 0x8C)),
CNameToCol("Lavender", CRGBA(0xE6, 0xE6, 0xFA)),
CNameToCol("LavenderBlush", CRGBA(0xFF, 0xF0, 0xF5)),
CNameToCol("LawnGreen", CRGBA(0x7C, 0xFC, 0x00)),
CNameToCol("LemonChiffon", CRGBA(0xFF, 0xFA, 0xCD)),
CNameToCol("LightBlue", CRGBA(0xAD, 0xD8, 0xE6)),
CNameToCol("LightCoral", CRGBA(0xF0, 0x80, 0x80)),
CNameToCol("LightCyan", CRGBA(0xE0, 0xFF, 0xFF)),
CNameToCol("LightGoldenRodYellow", CRGBA(0xFA, 0xFA, 0xD2)),
CNameToCol("LightGrey", CRGBA(0xD3, 0xD3, 0xD3)),
CNameToCol("LightGreen", CRGBA(0x90, 0xEE, 0x90)),
CNameToCol("LightPink", CRGBA(0xFF, 0xB6, 0xC1)),
CNameToCol("LightSalmon", CRGBA(0xFF, 0xA0, 0x7A)),
CNameToCol("LightSeaGreen", CRGBA(0x20, 0xB2, 0xAA)),
CNameToCol("LightSkyBlue", CRGBA(0x87, 0xCE, 0xFA)),
CNameToCol("LightSlateBlue", CRGBA(0x84, 0x70, 0xFF)),
CNameToCol("LightSlateGray", CRGBA(0x77, 0x88, 0x99)),
CNameToCol("LightSteelBlue", CRGBA(0xB0, 0xC4, 0xDE)),
CNameToCol("LightYellow", CRGBA(0xFF, 0xFF, 0xE0)),
CNameToCol("Lime", CRGBA(0x00, 0xFF, 0x00)),
CNameToCol("LimeGreen", CRGBA(0x32, 0xCD, 0x32)),
CNameToCol("Linen", CRGBA(0xFA, 0xF0, 0xE6)),
CNameToCol("Magenta", CRGBA(0xFF, 0x00, 0xFF)),
CNameToCol("Maroon", CRGBA(0x80, 0x00, 0x00)),
CNameToCol("MediumAquaMarine", CRGBA(0x66, 0xCD, 0xAA)),
CNameToCol("MediumBlue", CRGBA(0x00, 0x00, 0xCD)),
CNameToCol("MediumOrchid", CRGBA(0xBA, 0x55, 0xD3)),
CNameToCol("MediumPurple", CRGBA(0x93, 0x70, 0xD8)),
CNameToCol("MediumSeaGreen", CRGBA(0x3C, 0xB3, 0x71)),
CNameToCol("MediumSlateBlue", CRGBA(0x7B, 0x68, 0xEE)),
CNameToCol("MediumSpringGreen", CRGBA(0x00, 0xFA, 0x9A)),
CNameToCol("MediumTurquoise", CRGBA(0x48, 0xD1, 0xCC)),
CNameToCol("MediumVioletRed", CRGBA(0xC7, 0x15, 0x85)),
CNameToCol("MidnightBlue", CRGBA(0x19, 0x19, 0x70)),
CNameToCol("MintCream", CRGBA(0xF5, 0xFF, 0xFA)),
CNameToCol("MistyRose", CRGBA(0xFF, 0xE4, 0xE1)),
CNameToCol("Moccasin", CRGBA(0xFF, 0xE4, 0xB5)),
CNameToCol("NavajoWhite", CRGBA(0xFF, 0xDE, 0xAD)),
CNameToCol("Navy", CRGBA(0x00, 0x00, 0x80)),
CNameToCol("OldLace", CRGBA(0xFD, 0xF5, 0xE6)),
CNameToCol("Olive", CRGBA(0x80, 0x80, 0x00)),
CNameToCol("OliveDrab", CRGBA(0x6B, 0x8E, 0x23)),
CNameToCol("Orange", CRGBA(0xFF, 0xA5, 0x00)),
CNameToCol("OrangeRed", CRGBA(0xFF, 0x45, 0x00)),
CNameToCol("Orchid", CRGBA(0xDA, 0x70, 0xD6)),
CNameToCol("PaleGoldenRod", CRGBA(0xEE, 0xE8, 0xAA)),
CNameToCol("PaleGreen", CRGBA(0x98, 0xFB, 0x98)),
CNameToCol("PaleTurquoise", CRGBA(0xAF, 0xEE, 0xEE)),
CNameToCol("PaleVioletRed", CRGBA(0xD8, 0x70, 0x93)),
CNameToCol("PapayaWhip", CRGBA(0xFF, 0xEF, 0xD5)),
CNameToCol("PeachPuff", CRGBA(0xFF, 0xDA, 0xB9)),
CNameToCol("Peru", CRGBA(0xCD, 0x85, 0x3F)),
CNameToCol("Pink", CRGBA(0xFF, 0xC0, 0xCB)),
CNameToCol("Plum", CRGBA(0xDD, 0xA0, 0xDD)),
CNameToCol("PowderBlue", CRGBA(0xB0, 0xE0, 0xE6)),
CNameToCol("Purple", CRGBA(0x80, 0x00, 0x80)),
CNameToCol("Red", CRGBA(0xFF, 0x00, 0x00)),
CNameToCol("RosyBrown", CRGBA(0xBC, 0x8F, 0x8F)),
CNameToCol("RoyalBlue", CRGBA(0x41, 0x69, 0xE1)),
CNameToCol("SaddleBrown", CRGBA(0x8B, 0x45, 0x13)),
CNameToCol("Salmon", CRGBA(0xFA, 0x80, 0x72)),
CNameToCol("SandyBrown", CRGBA(0xF4, 0xA4, 0x60)),
CNameToCol("SeaGreen", CRGBA(0x2E, 0x8B, 0x57)),
CNameToCol("SeaShell", CRGBA(0xFF, 0xF5, 0xEE)),
CNameToCol("Sienna", CRGBA(0xA0, 0x52, 0x2D)),
CNameToCol("Silver", CRGBA(0xC0, 0xC0, 0xC0)),
CNameToCol("SkyBlue", CRGBA(0x87, 0xCE, 0xEB)),
CNameToCol("SlateBlue", CRGBA(0x6A, 0x5A, 0xCD)),
CNameToCol("SlateGray", CRGBA(0x70, 0x80, 0x90)),
CNameToCol("Snow", CRGBA(0xFF, 0xFA, 0xFA)),
CNameToCol("SpringGreen", CRGBA(0x00, 0xFF, 0x7F)),
CNameToCol("SteelBlue", CRGBA(0x46, 0x82, 0xB4)),
CNameToCol("Tan", CRGBA(0xD2, 0xB4, 0x8C)),
CNameToCol("Teal", CRGBA(0x00, 0x80, 0x80)),
CNameToCol("Thistle", CRGBA(0xD8, 0xBF, 0xD8)),
CNameToCol("Tomato", CRGBA(0xFF, 0x63, 0x47)),
CNameToCol("Turquoise", CRGBA(0x40, 0xE0, 0xD0)),
CNameToCol("Violet", CRGBA(0xEE, 0x82, 0xEE)),
CNameToCol("VioletRed", CRGBA(0xD0, 0x20, 0x90)),
CNameToCol("Wheat", CRGBA(0xF5, 0xDE, 0xB3)),
CNameToCol("White", CRGBA(0xFF, 0xFF, 0xFF)),
CNameToCol("WhiteSmoke", CRGBA(0xF5, 0xF5, 0xF5)),
CNameToCol("Yellow", CRGBA(0xFF, 0xFF, 0x00)),
CNameToCol("YellowGreen", CRGBA(0x9A, 0xCD, 0x32))
};
// scan a color from a HTML form (#rrggbb format)
bool scanHTMLColor(const char *src, CRGBA &dest)
{
if (!src || *src == '\0') return false;
if (*src == '#')
{
++src;
if (strlen(src) == 3 || strlen(src) == 4)
{
bool hasAlpha = (strlen(src) == 4);
// check RGB for valid hex
if (isHexa(src[0]) && isHexa(src[1]) && isHexa(src[2]))
{
// check optional A for valid hex
if (hasAlpha && !isHexa(src[3])) return false;
dest.R = convertHexa(src[0]);
dest.G = convertHexa(src[1]);
dest.B = convertHexa(src[2]);
dest.R = dest.R << 4 | dest.R;
dest.G = dest.G << 4 | dest.G;
dest.B = dest.B << 4 | dest.B;
if (hasAlpha)
{
dest.A = convertHexa(src[3]);
dest.A = dest.A << 4 | dest.A;
}
else
dest.A = 255;
return true;
}
return false;
}
CRGBA result;
src = scanColorComponent(src, result.R); if (!src) return false;
src = scanColorComponent(src, result.G); if (!src) return false;
src = scanColorComponent(src, result.B); if (!src) return false;
src = scanColorComponent(src, result.A);
if (!src)
{
// Alpha is optional
result.A = 255;
}
dest = result;
return true;
}
// TODO: CSS Colors Level 4 support
// Whitespace syntax, aliases: rgb == rgba, hsl == hsla
// rgb(51 170 51 / 0.4) /* 40% opaque green */
// rgb(51 170 51 / 40%) /* 40% opaque green */
if (strnicmp(src, "rgb(", 4) == 0 || strnicmp(src, "rgba(", 5) == 0)
{
src += 4;
if (*src == '(') src++;
std::vector<std::string> parts;
NLMISC::splitString(src, ",", parts);
if (parts.size() >= 3)
{
CRGBA result;
sint tmpv;
float tmpf;
// R
if (getPercentage(tmpv, tmpf, parts[0].c_str())) tmpv = 255 * tmpf;
clamp(tmpv, 0, 255);
result.R = tmpv;
// G
if (getPercentage(tmpv, tmpf, parts[1].c_str())) tmpv = 255 * tmpf;
clamp(tmpv, 0, 255);
result.G = tmpv;
// B
if (getPercentage(tmpv, tmpf, parts[2].c_str())) tmpv = 255 * tmpf;
clamp(tmpv, 0, 255);
result.B = tmpv;
// A
if (parts.size() == 4)
{
if (!fromString(parts[3], tmpf)) return false;
if (parts[3].find_first_of("%") != std::string::npos)
tmpf /= 100;
tmpv = 255 * tmpf;
clamp(tmpv, 0, 255);
result.A = tmpv;
}
else
result.A = 255;
dest = result;
return true;
}
return false;
}
if (strnicmp(src, "hsl(", 4) == 0 || strnicmp(src, "hsla(", 5) == 0)
{
src += 4;
if (*src == '(') src++;
std::vector<std::string> parts;
NLMISC::splitString(src, ",", parts);
if (parts.size() >= 3)
{
sint tmpv;
float h, s, l;
// hue
if (!fromString(parts[0], tmpv)) return false;
tmpv = ((tmpv % 360) + 360) % 360;
h = (float) tmpv / 360.0f;
// saturation
if (!getPercentage(tmpv, s, parts[1].c_str())) return false;
clamp(s, 0.0f, 1.0f);
// lightness
if (!getPercentage(tmpv, l, parts[2].c_str())) return false;
clamp(l, 0.0f, 1.0f);
CRGBA result;
hslToRgb(h, s, l, result);
// A
if (parts.size() == 4)
{
float tmpf;
if (!fromString(parts[3], tmpf)) return false;
if (parts[3].find_first_of("%") != std::string::npos)
tmpf /= 100;
clamp(tmpf, 0.0f, 1.0f);
result.A = 255 * tmpf;
}
dest = result;
return true;
}
return false;
}
{
// slow but should suffice for now
for(uint k = 0; k < sizeofarray(htmlColorNameToRGBA); ++k)
{
if (nlstricmp(src, htmlColorNameToRGBA[k].Name) == 0)
{
dest = htmlColorNameToRGBA[k].Color;
return true;
}
}
return false;
}
}
// ***************************************************************************
CRGBA getColor (const char *color)
@ -381,6 +748,11 @@ namespace NLGUI
return dst;
}
std::string getRGBAString(const CRGBA &color)
{
return toString("rgba(%d, %d, %d, %.1f)", color.R, color.G, color.B, color.A / 255.f);
}
// update HTTPCookies list
static void receiveCookie(const char *nsformat, const std::string &domain, bool trusted)
{

@ -784,6 +784,8 @@ static HTTag tags[HTML_ELEMENTS] = {
{ "U" , gen_attr, HTML_GEN_ATTRIBUTES },
{ "UL" , ul_attr, HTML_UL_ATTRIBUTES },
{ "VAR" , gen_attr, HTML_GEN_ATTRIBUTES },
//
{ "LUA" , gen_attr, HTML_GEN_ATTRIBUTES },
};
static SGML_dtd HTMLP_dtd = {

@ -25,6 +25,7 @@
#include "nel/gui/group_list.h"
#include "nel/gui/group_paragraph.h"
#include "nel/gui/libwww.h"
#include "nel/gui/html_element.h"
#include "interface_manager.h"
#include "nel/gui/action_handler.h"
#include "nel/misc/xml_auto_ptr.h"
@ -215,33 +216,29 @@ void CGroupQuickHelp::setGroupTextSize (CInterfaceGroup *group, bool selected)
extern CActionsContext ActionsContext;
void CGroupQuickHelp::beginElement (uint element_number, const std::vector<bool> &present, const std::vector<const char *>&value)
void CGroupQuickHelp::beginElement(CHtmlElement &elm)
{
CGroupHTML::beginElement (element_number, present, value);
CGroupHTML::beginElement (elm);
// Paragraph ?
switch(element_number)
switch(elm.ID)
{
case HTML_A:
// Quick help
if (_TrustedDomain && present[MY_HTML_A_Z_ACTION_SHORTCUT] && value[MY_HTML_A_Z_ACTION_SHORTCUT])
if (_TrustedDomain && elm.hasNonEmptyAttribute("z_action_shortcut"))
{
// Get the action category
string category;
if (present[MY_HTML_A_Z_ACTION_CATEGORY] && value[MY_HTML_A_Z_ACTION_CATEGORY])
category = value[MY_HTML_A_Z_ACTION_CATEGORY];
string category = elm.getAttribute("z_action_category");
// Get the action params
string params;
if (present[MY_HTML_A_Z_ACTION_PARAMS] && value[MY_HTML_A_Z_ACTION_PARAMS])
params = value[MY_HTML_A_Z_ACTION_PARAMS];
string params = elm.getAttribute("z_action_params");
// Get the action descriptor
CActionsManager *actionManager = ActionsContext.getActionsManager (category);
if (actionManager)
{
const CActionsManager::TActionComboMap &actionCombo = actionManager->getActionComboMap ();
CActionsManager::TActionComboMap::const_iterator ite = actionCombo.find (CAction::CName (value[MY_HTML_A_Z_ACTION_SHORTCUT], params.c_str()));
CActionsManager::TActionComboMap::const_iterator ite = actionCombo.find (CAction::CName (elm.getAttribute("z_action_shortcut").c_str(), params.c_str()));
if (ite != actionCombo.end())
{
addString (ite->second.toUCString());
@ -252,7 +249,7 @@ void CGroupQuickHelp::beginElement (uint element_number, const std::vector<bool>
case HTML_P:
// Get the action name
if (present[MY_HTML_P_QUICK_HELP_EVENTS])
if (elm.hasAttribute("quick_help_events"))
{
// This page is a quick help
_IsQuickHelp = true;
@ -260,38 +257,29 @@ void CGroupQuickHelp::beginElement (uint element_number, const std::vector<bool>
_Steps.push_back (CStep());
CStep &step = _Steps.back();
if (value[MY_HTML_P_QUICK_HELP_EVENTS])
// Get the event names
string events = elm.getAttribute("quick_help_events");
if (!events.empty())
{
// Get the event names
string events = value[MY_HTML_P_QUICK_HELP_EVENTS];
if (!events.empty())
uint first = 0;
while (first < events.size())
{
uint first = 0;
while (first < events.size())
{
// String end
string::size_type last = events.find_first_of(" ", first);
if (last == string::npos)
last = events.size();
// Extract the string
step.EventToComplete.insert (events.substr (first, last-first));
first = (uint)last+1;
}
// String end
string::size_type last = events.find_first_of(" ", first);
if (last == string::npos)
last = events.size();
// Extract the string
step.EventToComplete.insert (events.substr (first, last-first));
first = (uint)last+1;
}
}
// Get the condition
if (present[MY_HTML_P_QUICK_HELP_CONDITION] && value[MY_HTML_P_QUICK_HELP_CONDITION])
{
step.Condition = value[MY_HTML_P_QUICK_HELP_CONDITION];
}
step.Condition = elm.getAttribute("quick_help_condition");
// Get the action handlers to run
if (present[MY_HTML_P_QUICK_HELP_LINK] && value[MY_HTML_P_QUICK_HELP_LINK])
{
step.URL = value[MY_HTML_P_QUICK_HELP_LINK];
}
step.URL = elm.getAttribute("quick_help_link");
}
break;
}

@ -23,7 +23,6 @@
#include "nel/misc/types_nl.h"
#include "nel/gui/group_html.h"
/**
* Quick help group
* \author Cyril 'Hulud' Corvazier
@ -48,7 +47,7 @@ private:
virtual void updateCoords();
// From CGroupHTML
virtual void beginElement (uint element_number, const std::vector<bool> &present, const std::vector<const char *>&value);
virtual void beginElement (NLGUI::CHtmlElement &elm);
virtual void endBuild ();
virtual void browse (const char *url);
virtual std::string home();

@ -3271,7 +3271,6 @@ void CLuaIHMRyzom::browseNpcWebPage(const std::string &htmlId, const std::string
groupHtml->setTimeout((float)std::max(0.0, timeout));
// Browse the url
groupHtml->clean();
groupHtml->browse(url.c_str());
// Set top of the page
CCtrlScroll *pScroll = groupHtml->getScrollBar();

Loading…
Cancel
Save