1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2021 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2021 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "nel/gui/html_element.h"
24 #include "nel/gui/libwww.h"
27 using namespace NLMISC
;
35 // ***************************************************************************
36 CHtmlElement::CHtmlElement(ENodeType type
, std::string value
)
37 : ID(0), Type(type
), Value(value
), parent(NULL
), previousSibling(NULL
), nextSibling(NULL
), childIndex(0)
40 // ***************************************************************************
41 bool CHtmlElement::hasAttribute(const std::string
&key
) const
43 return Attributes
.find(key
) != Attributes
.end();
46 // ***************************************************************************
47 bool CHtmlElement::hasNonEmptyAttribute(const std::string
&key
) const
49 return !getAttribute(key
).empty();
52 // ***************************************************************************
53 std::string
CHtmlElement::getAttribute(const std::string
&key
) const
55 std::map
<std::string
, std::string
>::const_iterator it
= Attributes
.find(key
);
56 return (it
!= Attributes
.end() ? it
->second
: "");
59 // ***************************************************************************
60 bool CHtmlElement::hasClass(const std::string
&key
) const
62 return ClassNames
.find(key
) != ClassNames
.end();
65 // ***************************************************************************
66 void CHtmlElement::reindexChilds()
69 CHtmlElement
*prev
= NULL
;
70 std::list
<CHtmlElement
>::iterator it
;
71 for(it
= Children
.begin(); it
!= Children
.end(); ++it
)
73 if (it
->Type
== ELEMENT_NODE
)
76 it
->previousSibling
= prev
;
77 it
->nextSibling
= NULL
;
80 prev
->nextSibling
= &(*it
);
82 it
->childIndex
= index
;
89 // ***************************************************************************
90 void CHtmlElement::clearPseudo()
95 // ***************************************************************************
96 bool CHtmlElement::hasPseudo(const std::string
&key
) const
98 return _Pseudo
.find(key
) != _Pseudo
.end();
101 // ***************************************************************************
102 TStyle
CHtmlElement::getPseudo(const std::string
&key
) const
104 std::map
<std::string
, TStyle
>::const_iterator it
= _Pseudo
.find(key
);
105 if (it
!= _Pseudo
.end())
111 // ***************************************************************************
112 void CHtmlElement::setPseudo(const std::string
&key
, const TStyle
&style
)
114 std::map
<std::string
, TStyle
>::iterator it
= _Pseudo
.find(key
);
115 if (it
!= _Pseudo
.end())
117 // insert into previous, override previous values if they exist
118 for(TStyle::const_iterator itStyle
= style
.begin(); itStyle
!= style
.end(); ++itStyle
)
120 it
->second
[itStyle
->first
] = itStyle
->second
;
125 _Pseudo
[key
] = style
;
129 // ***************************************************************************
130 std::string
CHtmlElement::getInheritedLanguage() const
132 const CHtmlElement
*node
= this;
135 if (node
->hasAttribute("lang"))
136 return node
->getAttribute("lang");
144 // ***************************************************************************
145 std::string
CHtmlElement::htmlEscape(const std::string
&val
) const
147 if (val
.find_first_of("\"'&<>\xA0") == std::string::npos
)
151 // resize is quaranteed, make room for some free replacements
152 ret
.reserve(val
.size() + 24);
153 for(size_t pos
= 0; pos
!= val
.size(); pos
++)
157 case '"': ret
.append("""); break;
158 case '\'': ret
.append("'"); break;
159 case '&': ret
.append("&"); break;
160 case '<': ret
.append("<"); break;
161 case '>': ret
.append(">"); break;
162 case '\xA0': ret
.append(" "); break;
163 default : ret
.append(&val
[pos
],1); break;
170 // ***************************************************************************
171 std::string
CHtmlElement::serializeAttributes(bool escape
) const
174 for(std::map
<std::string
, std::string
>::const_iterator it
= Attributes
.begin(); it
!= Attributes
.end(); ++it
)
176 if (it
->first
== "class")
178 result
+= " class=\"";
179 for(std::set
<std::string
>::const_iterator it2
= ClassNames
.begin(); it2
!= ClassNames
.end(); ++it2
)
181 if (it2
!= ClassNames
.begin())
185 result
+= (escape
? htmlEscape(*it2
) : *it2
);
191 result
+= " " + it
->first
+ "=\"" + (escape
? htmlEscape(it
->second
) : it
->second
) + "\"";
197 // ***************************************************************************
198 std::string
CHtmlElement::serializeChilds(bool escape
) const
201 for(std::list
<CHtmlElement
>::const_iterator it
= Children
.begin(); it
!= Children
.end(); ++it
)
202 result
+= it
->serialize(escape
);
207 // ***************************************************************************
208 std::string
CHtmlElement::serialize(bool escape
) const
210 if (Type
== TEXT_NODE
)
212 if (parent
&& (parent
->ID
== HTML_SCRIPT
|| parent
->ID
== HTML_STYLE
||
213 parent
->ID
== HTML_IFRAME
|| parent
->ID
== HTML_NOEMBED
||
214 parent
->ID
== HTML_NOSCRIPT
))
218 return htmlEscape(Value
);
224 std::string result
= "<" + Value
+ serializeAttributes(escape
) + ">";
226 if (ID
== HTML_AREA
|| ID
== HTML_BASE
|| ID
== HTML_BR
||
227 ID
== HTML_COL
|| ID
== HTML_EMBED
|| ID
== HTML_HR
||
228 ID
== HTML_IMG
|| ID
== HTML_INPUT
|| ID
== HTML_LINK
||
229 ID
== HTML_META
|| ID
== HTML_PARAM
|| ID
== HTML_WBR
)
234 // first linebreak that will be ignored on parse time
235 if (ID
== HTML_PRE
|| ID
== HTML_TEXTAREA
)
238 if (!Children
.empty())
239 result
+= serializeChilds(escape
);
241 result
+= "</" + Value
+ ">";
246 // ***************************************************************************
247 std::string
CHtmlElement::toString(bool tree
, uint depth
) const
252 result
= NLMISC::toString("[%d]", depth
);
253 result
.append(depth
, '-');
255 if (Type
== NONE
|| Type
== ELEMENT_NODE
)
257 result
+= "<" + Value
;
258 for(std::map
<std::string
, std::string
>::const_iterator it
= Attributes
.begin(); it
!= Attributes
.end(); ++it
)
260 if (it
->first
== "class")
262 result
+= " class=\"";
263 for(std::set
<std::string
>::const_iterator it2
= ClassNames
.begin(); it2
!= ClassNames
.end(); ++it2
)
265 if (it2
!= ClassNames
.begin())
275 result
+= " " + it
->first
+ "=\"" + it
->second
+ "\"";
278 result
+= NLMISC::toString(" data-debug=\"childIndex: %d\"", childIndex
);
284 for(std::list
<CHtmlElement
>::const_iterator it
= Children
.begin(); it
!= Children
.end(); ++it
)
286 result
+= it
->toString(tree
, depth
+1);
290 result
+= NLMISC::toString("[%d]", depth
);
291 result
.append(depth
, '-');
293 result
+= "</" + Value
+ ">\n";
298 if (Value
.find("\n") == std::string::npos
)
300 result
+= "{" + Value
+ "}";
305 std::string::size_type start
= 0;
306 std::string::size_type pos
= Value
.find_first_of("\n\r\t");
307 while(pos
!= std::string::npos
)
309 result
+= Value
.substr(start
, pos
- start
);
310 if (Value
[pos
] == '\n')
312 result
+= "\xE2\x8F\x8E"; // \u23CE
314 else if (Value
[pos
] == '\t')
316 result
+= "\xE2\x87\xA5"; // \u21E5
320 pos
= Value
.find_first_of("\n\r\t", start
);
324 if (tree
) result
+= "\n";