Skip to content

Commit ef0a2cd

Browse files
authored
Merge pull request #281 from MicCalo/css_counters_basic
basic implementation for css counters
2 parents a81c927 + c695424 commit ef0a2cd

File tree

11 files changed

+280
-21
lines changed

11 files changed

+280
-21
lines changed

include/litehtml/element.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ namespace litehtml
3535

3636
virtual void select_all(const css_selector& selector, elements_list& res);
3737
element::ptr _add_before_after(int type, const style& style);
38+
39+
private:
40+
std::map<string_id, int> m_counter_values;
41+
3842
public:
3943
explicit element(const std::shared_ptr<document>& doc);
4044
virtual ~element() = default;
@@ -142,6 +146,16 @@ namespace litehtml
142146
{
143147
return _add_before_after(1, style);
144148
}
149+
150+
string get_counter_value(const string& counter_name);
151+
string get_counters_value(const string_vector& parameters);
152+
void increment_counter(const string_id& counter_name_id, const int increment = 1);
153+
void reset_counter(const string_id& counter_name_id, const int value = 0);
154+
155+
private:
156+
std::vector<element::ptr> get_siblings_before() const;
157+
bool find_counter(const string_id& counter_name_id, std::map<string_id, int>::iterator& map_iterator);
158+
void parse_counter_tokens(const string_vector& tokens, const int default_value, std::function<void(const string_id&, const int)> handler) const;
145159
};
146160

147161
//////////////////////////////////////////////////////////////////////////

include/litehtml/html.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
namespace litehtml
2525
{
26-
void trim(string &s);
26+
void trim(string &s, const string& chars_to_trim = " \n\r\t");
2727
void lcase(string &s);
2828
int value_index(const string& val, const string& strings, int defValue = -1, char delim = ';');
2929
string index_value(int index, const string& strings, char delim = ';');
@@ -36,7 +36,9 @@ namespace litehtml
3636

3737
int t_strcasecmp(const char *s1, const char *s2);
3838
int t_strncasecmp(const char *s1, const char *s2, size_t n);
39-
39+
40+
bool is_number(const string& string, const bool allow_dot = 1);
41+
4042
inline int t_isdigit(int c)
4143
{
4244
return (c >= '0' && c <= '9');

include/litehtml/html_tag.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ namespace litehtml
119119
string get_list_marker_text(int index);
120120
element::ptr get_element_before(const style& style, bool create);
121121
element::ptr get_element_after(const style& style, bool create);
122+
123+
private:
124+
void handle_counter_properties();
125+
122126
};
123127

124128
/************************************************************************/

include/litehtml/string_id.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,10 @@ STRING_ID(
288288
_flex_shrink_,
289289
_flex_basis_,
290290

291-
_caption_side_,
291+
_counter_reset_,
292+
_counter_increment_,
293+
294+
_caption_side_,
292295
);
293296
#undef STRING_ID
294297
extern const string_id empty_id; // _id("")

src/el_before_after.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ void litehtml::el_before_after_base::add_text( const string& txt )
133133

134134
void litehtml::el_before_after_base::add_function( const string& fnc, const string& params )
135135
{
136-
int idx = value_index(fnc, "attr;counter;url");
136+
int idx = value_index(fnc, "attr;counter;counters;url");
137137
switch(idx)
138138
{
139139
// attr
@@ -155,9 +155,18 @@ void litehtml::el_before_after_base::add_function( const string& fnc, const stri
155155
break;
156156
// counter
157157
case 1:
158+
add_text(get_counter_value(params));
158159
break;
159-
// url
160+
// counters
160161
case 2:
162+
{
163+
string_vector tokens;
164+
split_string(params, tokens, ",");
165+
add_text(get_counters_value(tokens));
166+
}
167+
break;
168+
// url
169+
case 3:
161170
{
162171
string p_url = params;
163172
trim(p_url);

src/element.cpp

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,122 @@ bool element::is_block_formatting_context() const
288288
return false;
289289
}
290290

291+
litehtml::string litehtml::element::get_counter_value(const string& counter_name)
292+
{
293+
std::map<string_id, int>::iterator i;
294+
if (find_counter(_id(counter_name), i))
295+
{
296+
return std::to_string(i->second);
297+
}
298+
return "0";
299+
}
300+
301+
string litehtml::element::get_counters_value(const string_vector& parameters)
302+
{
303+
string result = "";
304+
if (parameters.size() >= 2) {
305+
const string_id counter_name_id = _id(parameters[0]);
306+
string delims = parameters[1];
307+
litehtml::trim(delims, "\"'");
308+
309+
string_vector values;
310+
311+
element::ptr current = shared_from_this();
312+
while (current != nullptr)
313+
{
314+
auto map_iterator = current->m_counter_values.find(counter_name_id);
315+
if (map_iterator != current->m_counter_values.end()) {
316+
values.push_back(std::to_string(map_iterator->second));
317+
}
318+
current = current->parent();
319+
}
320+
if (values.empty()) {
321+
// if no counter is found, instanciate one with value '0'
322+
shared_from_this()->m_counter_values[counter_name_id] = 0;
323+
result = "0";
324+
}
325+
else {
326+
std::reverse(values.begin(), values.end());
327+
litehtml::join_string(result, values, delims);
328+
}
329+
}
330+
return result;
331+
}
332+
333+
334+
bool litehtml::element::find_counter(const string_id& counter_name_id, std::map<string_id, int>::iterator& map_iterator) {
335+
element::ptr current = shared_from_this();
336+
337+
while (current != nullptr)
338+
{
339+
map_iterator = current->m_counter_values.find(counter_name_id);
340+
if (map_iterator != current->m_counter_values.end()) {
341+
return true;
342+
}
343+
344+
// on each level, search previous siblings too
345+
std::vector<element::ptr> siblings = current->get_siblings_before();
346+
std::reverse(siblings.begin(), siblings.end());
347+
for (const element::ptr& sibling : siblings) {
348+
map_iterator = sibling->m_counter_values.find(counter_name_id);
349+
if (map_iterator != sibling->m_counter_values.end()) {
350+
return true;
351+
}
352+
}
353+
current = current->parent();
354+
}
355+
356+
return false;
357+
}
358+
359+
std::vector<element::ptr> litehtml::element::get_siblings_before() const
360+
{
361+
std::vector<element::ptr> siblings;
362+
if (parent() != nullptr) {
363+
for (const element::ptr& sybling : parent()->children()) {
364+
if (sybling == shared_from_this()) {
365+
break;
366+
}
367+
siblings.push_back(sybling);
368+
}
369+
}
370+
return siblings;
371+
}
372+
373+
374+
void litehtml::element::parse_counter_tokens(const string_vector& tokens, const int default_value, std::function<void(const string_id&, const int)> handler) const {
375+
int pos = 0;
376+
while (pos < tokens.size()) {
377+
string name = tokens[pos];
378+
int value = default_value;
379+
if (pos < tokens.size() - 1 && litehtml::is_number(tokens[pos + 1], 0)) {
380+
value = atoi(tokens[pos + 1].c_str());
381+
pos += 2;
382+
}
383+
else {
384+
pos += 1;
385+
}
386+
handler(_id(name), value);
387+
}
388+
}
389+
390+
void litehtml::element::increment_counter(const string_id& counter_name_id, const int increment)
391+
{
392+
std::map<string_id, int>::iterator i;
393+
if (find_counter(counter_name_id, i)) {
394+
i->second = i->second + increment;
395+
}
396+
else {
397+
// if counter is not found, initialize one on this element
398+
m_counter_values[counter_name_id] = increment;
399+
}
400+
}
401+
402+
void litehtml::element::reset_counter(const string_id& counter_name_id, const int value)
403+
{
404+
m_counter_values[counter_name_id] = value;
405+
}
406+
291407
const background* element::get_background(bool own_only) LITEHTML_RETURN_FUNC(nullptr)
292408
void element::add_style( const style& style) LITEHTML_EMPTY_FUNC
293409
void element::select_all(const css_selector& selector, elements_list& res) LITEHTML_EMPTY_FUNC

src/html.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
#include "types.h"
33
#include "utf8_strings.h"
44

5-
void litehtml::trim(string &s)
5+
void litehtml::trim(string &s, const string& chars_to_trim)
66
{
7-
string::size_type pos = s.find_first_not_of(" \n\r\t");
7+
string::size_type pos = s.find_first_not_of(chars_to_trim);
88
if(pos != string::npos)
99
{
1010
s.erase(s.begin(), s.begin() + pos);
@@ -14,7 +14,7 @@ void litehtml::trim(string &s)
1414
s = "";
1515
return;
1616
}
17-
pos = s.find_last_not_of(" \n\r\t");
17+
pos = s.find_last_not_of(chars_to_trim);
1818
if(pos != string::npos)
1919
{
2020
s.erase(s.begin() + pos + 1, s.end());
@@ -277,3 +277,14 @@ litehtml::string litehtml::get_escaped_string(const string& in_str)
277277
}
278278
return ret;
279279
}
280+
281+
bool litehtml::is_number(const string& string, const bool allow_dot) {
282+
for (auto ch : string)
283+
{
284+
if (!(t_isdigit(ch) || (allow_dot && ch == '.')))
285+
{
286+
return false;
287+
}
288+
}
289+
return true;
290+
}

src/html_tag.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1548,9 +1548,33 @@ litehtml::element::ptr litehtml::html_tag::get_element_after(const style& style,
15481548
return nullptr;
15491549
}
15501550

1551+
1552+
void litehtml::html_tag::handle_counter_properties()
1553+
{
1554+
const auto& reset_property = m_style.get_property(string_id::_counter_reset_);
1555+
if (reset_property.m_type == prop_type_string_vector) {
1556+
auto reset_function = [&](const string_id&name_id, const int value) {
1557+
reset_counter(name_id, value);
1558+
};
1559+
parse_counter_tokens(reset_property.m_string_vector, 0, reset_function);
1560+
return;
1561+
}
1562+
1563+
const auto& inc_property = m_style.get_property(string_id::_counter_increment_);
1564+
if (inc_property.m_type == prop_type_string_vector) {
1565+
auto inc_function = [&](const string_id&name_id, const int value) {
1566+
increment_counter(name_id, value);
1567+
};
1568+
parse_counter_tokens(inc_property.m_string_vector, 1, inc_function);
1569+
return;
1570+
}
1571+
}
1572+
1573+
15511574
void litehtml::html_tag::add_style(const style& style)
15521575
{
15531576
m_style.combine(style);
1577+
handle_counter_properties();
15541578
}
15551579

15561580
void litehtml::html_tag::refresh_styles()

src/style.cpp

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,15 @@ void style::add_property(string_id name, const string& val, const string& baseur
536536
add_parsed_property(_flex_basis_, property_value(length, important));
537537
break;
538538

539+
case _counter_increment_:
540+
case _counter_reset_:
541+
{
542+
string_vector tokens;
543+
split_string(val, tokens, " ");
544+
add_parsed_property(name, property_value(tokens, important));
545+
break;
546+
}
547+
539548
default:
540549
add_parsed_property(name, property_value(val, important));
541550
}
@@ -992,18 +1001,6 @@ void style::parse_font(const string& val, bool important)
9921001

9931002
void style::parse_flex(const string& val, bool important)
9941003
{
995-
auto is_number = [](const string& val)
996-
{
997-
for (auto ch : val)
998-
{
999-
if ((ch < '0' || ch > '9') && ch != '.')
1000-
{
1001-
return false;
1002-
}
1003-
}
1004-
return true;
1005-
};
1006-
10071004
css_length _auto = css_length::predef_value(flex_basis_auto);
10081005

10091006
if (val == "initial")
@@ -1046,7 +1043,7 @@ void style::parse_flex(const string& val, bool important)
10461043
float grow = t_strtof(tokens[0]);
10471044
add_parsed_property(_flex_grow_, property_value(grow, important));
10481045

1049-
if (is_number(tokens[1]))
1046+
if (litehtml::is_number(tokens[1]))
10501047
{
10511048
float shrink = t_strtof(tokens[1]);
10521049
add_parsed_property(_flex_shrink_, property_value(shrink, important));

0 commit comments

Comments
 (0)