Merge branch 'fixes' into main/rendor-staging
[ryzomcore.git] / nel / src / gui / html_element.cpp
blobf4e770a4a35397c6d19f345b36bd2f6a34f4c01a
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2021 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2021 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
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/>.
21 #include "stdpch.h"
23 #include "nel/gui/html_element.h"
24 #include "nel/gui/libwww.h"
26 using namespace std;
27 using namespace NLMISC;
29 #ifdef DEBUG_NEW
30 #define new DEBUG_NEW
31 #endif
33 namespace NLGUI
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()
68 uint index = 0;
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)
75 it->parent = this;
76 it->previousSibling = prev;
77 it->nextSibling = NULL;
78 if (prev)
80 prev->nextSibling = &(*it);
82 it->childIndex = index;
83 index++;
84 prev = &(*it);
89 // ***************************************************************************
90 void CHtmlElement::clearPseudo()
92 _Pseudo.clear();
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())
106 return it->second;
108 return TStyle();
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;
123 else
125 _Pseudo[key] = style;
129 // ***************************************************************************
130 std::string CHtmlElement::getInheritedLanguage() const
132 const CHtmlElement *node = this;
133 while(node)
135 if (node->hasAttribute("lang"))
136 return node->getAttribute("lang");
138 node = node->parent;
141 return "";
144 // ***************************************************************************
145 std::string CHtmlElement::htmlEscape(const std::string &val) const
147 if (val.find_first_of("\"'&<>\xA0") == std::string::npos)
148 return val;
150 std::string ret;
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++)
155 switch(val[pos])
157 case '"': ret.append("&quot;"); break;
158 case '\'': ret.append("&#39;"); break;
159 case '&': ret.append("&amp;"); break;
160 case '<': ret.append("&lt;"); break;
161 case '>': ret.append("&gt;"); break;
162 case '\xA0': ret.append("&nbsp;"); break;
163 default : ret.append(&val[pos],1); break;
167 return ret;
170 // ***************************************************************************
171 std::string CHtmlElement::serializeAttributes(bool escape) const
173 std::string result;
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())
183 result += " ";
185 result += (escape ? htmlEscape(*it2) : *it2);
187 result += "\"";
189 else
191 result += " " + it->first + "=\"" + (escape ? htmlEscape(it->second) : it->second) + "\"";
194 return result;
197 // ***************************************************************************
198 std::string CHtmlElement::serializeChilds(bool escape) const
200 std::string result;
201 for(std::list<CHtmlElement>::const_iterator it = Children.begin(); it != Children.end(); ++it)
202 result += it->serialize(escape);
204 return result;
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))
216 return Value;
217 } else if (escape) {
218 return htmlEscape(Value);
219 } else {
220 return 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)
231 return result;
234 // first linebreak that will be ignored on parse time
235 if (ID == HTML_PRE || ID == HTML_TEXTAREA)
236 result += "\n";
238 if (!Children.empty())
239 result += serializeChilds(escape);
241 result += "</" + Value + ">";
243 return result;
246 // ***************************************************************************
247 std::string CHtmlElement::toString(bool tree, uint depth) const
249 std::string result;
250 if (depth > 0)
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())
267 result += " ";
269 result += *it2;
271 result += "\"";
273 else
275 result += " " + it->first + "=\"" + it->second + "\"";
278 result += NLMISC::toString(" data-debug=\"childIndex: %d\"", childIndex);
279 result += ">";
281 if (tree)
283 result += "\n";
284 for(std::list<CHtmlElement>::const_iterator it = Children.begin(); it != Children.end(); ++it)
286 result += it->toString(tree, depth+1);
288 if (depth > 0)
290 result += NLMISC::toString("[%d]", depth);
291 result.append(depth, '-');
293 result += "</" + Value + ">\n";
296 else
298 if (Value.find("\n") == std::string::npos)
300 result += "{" + Value + "}";
302 else
304 result += "{";
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
319 start = pos+1;
320 pos = Value.find_first_of("\n\r\t", start);
322 result += "}";
324 if (tree) result += "\n";
327 return result;
329 } // namespace