37 return c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r';
40 std::string
trim(
const std::string &s) {
42 while (start < s.size() &&
isWhitespace(s[start])) ++start;
43 size_t end = s.size();
45 return s.substr(start, end - start);
55 : _Name(name), _Value(value) {}
58 : _Name(prop._Name), _Value(prop._Value) {}
81 : _Properties(decl._Properties) {}
87 _Properties = decl._Properties;
93 std::string tname = trim(name);
94 std::string tvalue = trim(value);
96 if (tname.empty())
return;
98 for (
auto &prop : _Properties) {
99 if (prop.getName() == tname) {
100 prop.setValue(tvalue);
104 _Properties.emplace_back(tname, tvalue);
108 std::string tname = trim(name);
110 std::remove_if(_Properties.begin(), _Properties.end(),
111 [&tname](
const CSSProperty &p) { return p.getName() == tname; }),
117 std::string tname = trim(name);
118 for (
const auto &prop : _Properties) {
119 if (prop.getName() == tname)
return ∝
130 for (
size_t i = 0; i < _Properties.size(); ++i) {
131 result += _Properties[i].getName();
133 result += _Properties[i].getValue();
135 if (i + 1 < _Properties.size()) result +=
" ";
144 size_t len = input.size();
148 while (pos < len && isWhitespace(input[pos])) ++pos;
149 if (pos >= len)
break;
152 size_t colon = input.find(
':', pos);
153 if (colon == std::string::npos)
break;
155 std::string propName = trim(input.substr(pos, colon - pos));
161 size_t valueStart = pos;
163 bool inSingleQuote =
false;
164 bool inDoubleQuote =
false;
170 if (c ==
'\'') inSingleQuote =
false;
175 if (c ==
'"') inDoubleQuote =
false;
180 if (c ==
'\'') { inSingleQuote =
true; ++pos;
continue; }
181 if (c ==
'"') { inDoubleQuote =
true; ++pos;
continue; }
182 if (c ==
'(') { ++parenDepth; ++pos;
continue; }
183 if (c ==
')') {
if (parenDepth > 0) --parenDepth; ++pos;
continue; }
185 if (c ==
';' && parenDepth == 0) {
191 std::string propValue = trim(input.substr(valueStart, pos - valueStart));
193 if (!propName.empty()) {
194 addProperty(propName, propValue);
197 if (pos < len && input[pos] ==
';') ++pos;
210 : _Selector(trim(selector)) {}
213 : _Selector(rule._Selector), _Declaration(rule._Declaration) {}
219 _Selector = rule._Selector;
220 _Declaration = rule._Declaration;
234 result += _Selector +
" {\n";
235 for (
const auto &prop : _Declaration.getProperties()) {
236 result +=
" " + prop.getName() +
": " + prop.getValue() +
";\n";
240 result += _Selector +
"{";
252 : _Rules(sheet._Rules) {}
257 if (
this != &sheet) {
258 _Rules = sheet._Rules;
263void libhtmlpp::CSSStyleSheet::_skipWhitespace(
const std::string &input,
size_t &pos)
const {
264 while (pos < input.size() && isWhitespace(input[pos])) ++pos;
267void libhtmlpp::CSSStyleSheet::_skipComment(
const std::string &input,
size_t &pos)
const {
268 if (pos + 1 < input.size() && input[pos] ==
'/' && input[pos + 1] ==
'*') {
270 while (pos + 1 < input.size()) {
271 if (input[pos] ==
'*' && input[pos + 1] ==
'/') {
285 size_t len = input.size();
288 _skipWhitespace(input, pos);
289 if (pos >= len)
break;
292 if (pos + 1 < len && input[pos] ==
'/' && input[pos + 1] ==
'*') {
293 _skipComment(input, pos);
298 if (input[pos] ==
'@') {
299 size_t atStart = pos;
303 size_t semiPos = input.find(
';', pos);
304 size_t bracePos = input.find(
'{', pos);
306 if (bracePos != std::string::npos && (semiPos == std::string::npos || bracePos < semiPos)) {
308 std::string atSelector = trim(input.substr(atStart, bracePos - atStart));
312 size_t blockStart = pos;
313 while (pos < len && depth > 0) {
314 if (input[pos] ==
'{') ++depth;
315 else if (input[pos] ==
'}') --depth;
316 if (depth > 0) ++pos;
319 std::string blockContent = input.substr(blockStart, pos - blockStart);
320 pos = (pos < len) ? pos + 1 : pos;
324 nested.
parse(blockContent);
327 for (
const auto &nestedRule : nested.
getRules()) {
329 rule.
setSelector(atSelector +
" " + nestedRule.getSelector());
330 for (
const auto &prop : nestedRule.getDeclaration().getProperties()) {
333 _Rules.push_back(rule);
337 if (nested.
getRuleCount() == 0 && !blockContent.empty()) {
340 _Rules.push_back(rule);
342 }
else if (semiPos != std::string::npos) {
344 std::string atRule = trim(input.substr(atStart, semiPos - atStart));
346 _Rules.push_back(rule);
356 size_t braceOpen = input.find(
'{', pos);
357 if (braceOpen == std::string::npos)
break;
359 std::string selector = trim(input.substr(pos, braceOpen - pos));
364 size_t declStart = pos;
365 while (pos < len && depth > 0) {
366 if (pos + 1 < len && input[pos] ==
'/' && input[pos + 1] ==
'*') {
367 _skipComment(input, pos);
370 if (input[pos] ==
'{') ++depth;
371 else if (input[pos] ==
'}') --depth;
372 if (depth > 0) ++pos;
375 std::string declarations = input.substr(declStart, pos - declStart);
376 pos = (pos < len) ? pos + 1 : pos;
378 if (!selector.empty()) {
381 _Rules.push_back(rule);
387 _Rules.push_back(rule);
391 if (index < _Rules.size()) {
392 _Rules.erase(_Rules.begin() +
static_cast<std::ptrdiff_t
>(index));
397 if (index < _Rules.size())
return &_Rules[index];
402 return _Rules.size();
411 for (
size_t i = 0; i < _Rules.size(); ++i) {
412 result += _Rules[i].
serialize(formatted);
413 if (formatted) result +=
"\n";
414 if (i + 1 < _Rules.size() && formatted) result +=
"\n";
void addProperty(const std::string &name, const std::string &value)
void removeProperty(const std::string &name)
const std::vector< CSSProperty > & getProperties() const
std::string serialize() const
const CSSProperty * getProperty(const std::string &name) const
void parse(const std::string &input)
CSSDeclaration & operator=(const CSSDeclaration &decl)
void setName(const std::string &name)
const std::string & getName() const
void setValue(const std::string &value)
CSSProperty & operator=(const CSSProperty &prop)
const std::string & getValue() const
CSSDeclaration & getDeclaration()
std::string serialize(bool formatted=false) const
void setSelector(const std::string &selector)
CSSRule & operator=(const CSSRule &rule)
const std::string & getSelector() const
void parse(const std::string &input)
const std::vector< CSSRule > & getRules() const
CSSStyleSheet & operator=(const CSSStyleSheet &sheet)
static CSSDeclaration parseInlineStyle(const std::string &style)
std::string serialize(bool formatted=false) const
const CSSRule * getRule(size_t index) const
void addRule(const CSSRule &rule)
void removeRule(size_t index)
size_t getRuleCount() const
bool isWhitespace(char c)
std::string trim(const std::string &s)