webview: vh units, long child selectors, img height %, grid fr fit
- css: support vh (window height, restyle on height change) - css_match: selectors of up to 16 parts; "a > b > c > d > e" matched nothing - img "height: 100%; width: auto" in a box of definite height - grid: fr columns narrower than their items' width wrap into fewer columns (theguardian.com carousel), virtualbox.org header fixed - engine, http and start/error page updates, tests/gridfit.htm Assisted-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4ec4f286a7
commit
5a7ba5c952
11 files changed
+1045
-121
No files matched your search
@@ -173,7 +173,7 @@ dword tag_flags(dword _id)
|
||||
// nodes //
|
||||
//===================================================//
|
||||
|
||||
#define NODE_SIZE 420
|
||||
#define NODE_SIZE 440
|
||||
#define BODY_MARGIN 6 // UA margin of <body> (the page margin)
|
||||
|
||||
// tree
|
||||
@@ -252,9 +252,10 @@ dword tag_flags(dword _id)
|
||||
#define S_MAXH 276 // max-height in px (0 is a real value: a collapsed menu), POS_AUTO none
|
||||
#define S_ORDER 280 // flex item "order", signed
|
||||
#define S_GRIDN 284 // grid-template-columns: number of tracks, 0 none; < 0 "repeat(auto-fill, ...)" of tracks at least that many px wide
|
||||
#define S_GRIDT 288 // the tracks, GRID_TRACKS of them: px > 0, 0 auto, < 0 fr in tenths (-10 = 1fr), GT_PCT + n = n%
|
||||
#define S_GRIDT 288 // the tracks, GRID_TRACKS of them: px > 0, 0 auto, < 0 fr in tenths (-10 = 1fr), GT_PCT + n = n%, GT_CAP + px
|
||||
#define GRID_TRACKS 8
|
||||
#define GT_PCT 0x20000000
|
||||
#define GT_CAP 0x10000000 // GT_CAP + px: "minmax(0, 96rem)", at most that wide, narrower when the row is short of room
|
||||
#define N_TAG 320 // T_UNKNOWN: pool offset of the real tag name ("overflow-menu"), 0 = none
|
||||
#define N_OPEN 324 // <details> the user opened (1) or closed (2); 0: as its "open" attribute says
|
||||
#define S_Z 328 // z-index of a positioned box, POS_AUTO = auto
|
||||
@@ -282,6 +283,11 @@ dword tag_flags(dword _id)
|
||||
#define S_MRPCT 408 // margin-right in percent like S_MLPCT; past style_init[]: style_compute() clears it
|
||||
#define S_AREAS 412 // grid-template-areas: its place + 1 in style.h css_areas, 0 none; cleared like S_MRPCT
|
||||
#define S_GAREA 416 // grid-area: css_hash() of the name of the item's area, 0 none; cleared like S_MRPCT
|
||||
#define S_OUTW 420 // outline: its width in px, 0 = none; cleared like S_MRPCT
|
||||
#define S_OUTC 424 // outline-color, NO_COLOR = the text colour (currentColor)
|
||||
#define S_OUTOFF 428 // outline-offset in px
|
||||
#define S_GCOL 432 // grid-column-start: the line the item starts at (1, 2...), 0 = auto; cleared like S_MRPCT
|
||||
#define S_BGA 436 // a translucent background: alpha << 24 + its own RGB (S_BG is it blended over the backdrop), 0 = opaque or none; cleared like S_MRPCT
|
||||
#define OBJPOS_CENTER 12850 // 50% 50%, the initial value
|
||||
#define GRID_ALL 1000 // S_COLSPAN of "grid-column: 1 / -1": the whole row
|
||||
|
||||
@@ -322,6 +328,7 @@ enum { VALIGN_TOP, VALIGN_MIDDLE, VALIGN_BOTTOM, VALIGN_SUPER, VALIGN_SUB }; //
|
||||
#define SF_BUTTON 32
|
||||
#define SF_SRONLY 64 // hidden for the eye only (sr-only, parked off-screen): display:none, but its words still name a link (layout.h link_label_text); not inherited
|
||||
#define SF_ITALIC 128 // no italic font: paint.h shears the drawn run (cv_slant)
|
||||
#define SF_VMID 256 // vertical-align: middle set on the element itself (S_VALIGN is inherited, for the cells); not inherited
|
||||
#define SF_COLORSET 1024 // a stylesheet rule set the color on this very node (not inherited)
|
||||
#define SF_INLBLOCK 4096 // display:inline-block (S_DISPLAY stays D_INLINE), not inherited
|
||||
#define SF_CLEAR 16384 // clear: left/right/both - starts below the floats, not inherited
|
||||
|
||||
+334
-23
@@ -10,7 +10,7 @@
|
||||
// fragments //
|
||||
//===================================================//
|
||||
|
||||
#define FRAG_SIZE 84
|
||||
#define FRAG_SIZE 88
|
||||
#define F_NODE 0 // node the fragment belongs to (text node for text runs)
|
||||
#define F_KIND 4
|
||||
#define F_X 8
|
||||
@@ -34,6 +34,7 @@
|
||||
#define F_P 72
|
||||
#define F_IMGY 76 // FR_IMG: first row shown (a picture cut at the top by overflow)
|
||||
#define F_IMGW 80 // FR_IMG: the width the picture is scaled to (width in %, max-width), 0 = as it is
|
||||
#define F_AMID 84 // inside an inline-block with "vertical-align: middle": 1, it is centred in its line
|
||||
|
||||
enum { FR_TEXT = 1, FR_IMG, FR_BOX, FR_RECT, FR_HLINE, FR_ICON };
|
||||
// F_TEXT of an FR_ICON that is a checkbox or radio button (form.h draws it)
|
||||
@@ -89,6 +90,7 @@ dword FRAGS::add()
|
||||
|
||||
int lay_measuring; // > 0 while measuring content widths: fragments are discarded
|
||||
int lay_max_x; // rightmost edge of content emitted so far
|
||||
int lay_float_depth; // > 0 while a float without a width is measured (float_width)
|
||||
dword lay_line_id;
|
||||
bool lay_skip_width; // set by the float row: the width was resolved by the caller
|
||||
dword lay_page_w;
|
||||
@@ -348,7 +350,12 @@ int flt_place(int _y, _w, _lo, _hi, dword _side)
|
||||
void line_wrap_edges()
|
||||
{
|
||||
int k;
|
||||
IF (lay_measuring) RETURN;
|
||||
// while measuring the lines ignore the floats (a table cell's min-content is the
|
||||
// widest of them and its words, not the sum), but not in the measure of a float
|
||||
// without a width: its text goes beside the floats inside it, so its width is
|
||||
// theirs plus the line (korrespondent.net: "USD" floated left, the rate beside it
|
||||
// in the floated .index-block went to a line of its own)
|
||||
IF (lay_measuring) && (!lay_float_depth) RETURN;
|
||||
IF (flt_n <= flt_base) RETURN;
|
||||
k = blk_x + blk_w;
|
||||
flt_room(ln_y, 1, blk_x, k);
|
||||
@@ -466,6 +473,16 @@ int line_sup_room(int _sink)
|
||||
RETURN need;
|
||||
}
|
||||
|
||||
// how far down a fragment of an inline-block goes in a line of ln_h (line_close)
|
||||
int atom_dy(dword _f)
|
||||
{
|
||||
int dy;
|
||||
dy = ln_h - DSDWORD[_f + F_ATOMH];
|
||||
IF (!DSDWORD[_f + F_AMID]) RETURN dy;
|
||||
IF (dy < 0) RETURN dy;
|
||||
RETURN dy / 2;
|
||||
}
|
||||
|
||||
void line_close()
|
||||
{
|
||||
dword i, f, last, fw, p;
|
||||
@@ -518,8 +535,14 @@ void line_close()
|
||||
IF (frag_is_float(f)) && (!DSDWORD[f + F_ATOMH]) continue;
|
||||
// a float:right atom stays at the right end whatever the alignment
|
||||
IF (DSDWORD[f + F_X] < right) DSDWORD[f + F_X] += dx;
|
||||
// an inline-block moves as one piece: its bottom on the bottom of the line
|
||||
IF (DSDWORD[f + F_ATOMH]) { dy = ln_h - DSDWORD[f + F_ATOMH]; DSDWORD[f + F_Y] += dy; continue; }
|
||||
// an inline-block moves as one piece: its bottom on the bottom of the line, or
|
||||
// with "vertical-align: middle" in the middle of it (korrespondent.net: the
|
||||
// label "Пошук" and its 30px icon, both middle, sat on the icon's bottom)
|
||||
if (DSDWORD[f + F_ATOMH]) {
|
||||
dy = atom_dy(f);
|
||||
DSDWORD[f + F_Y] += dy;
|
||||
continue;
|
||||
}
|
||||
dy = ln_h - DSDWORD[f + F_H];
|
||||
dy -= sink;
|
||||
IF (dy < 0) dy = 0;
|
||||
@@ -890,6 +913,36 @@ void place_string(dword _tnode, _stnode, _str)
|
||||
place_run(_tnode, _stnode, off, strlen(_str), false);
|
||||
}
|
||||
|
||||
// _str as page text: a word at a time, the lines break between them (the "[alt]" of a
|
||||
// picture that did not come: in one piece, a long alt was the narrowest a table column
|
||||
// could get)
|
||||
void place_words(dword _tnode, _stnode, _str)
|
||||
{
|
||||
dword off = dpool.add(_str);
|
||||
dword len = strlen(_str);
|
||||
dword s, p, i, we, fw;
|
||||
int right;
|
||||
fw = font_w(node_font(_stnode));
|
||||
i = 0;
|
||||
while (i < len) {
|
||||
s = dpool.get(off); // dpool may move while the runs are placed
|
||||
p = s + i;
|
||||
IF (DSBYTE[p] == ' ') {
|
||||
ln_soft = true;
|
||||
IF (ln_open) && (ln_has) && (ln_x != ln_flt_x) {
|
||||
right = ln_right();
|
||||
IF (ln_x + fw <= right) place_run(_tnode, _stnode, off + i, 1, true);
|
||||
}
|
||||
i++;
|
||||
CONTINUE;
|
||||
}
|
||||
we = i;
|
||||
WHILE (we < len) { p = s + we; IF (DSBYTE[p] == ' ') BREAK; we++; }
|
||||
place_run(_tnode, _stnode, off + i, we - i, false);
|
||||
i = we;
|
||||
}
|
||||
}
|
||||
|
||||
void inline_text(dword _idx, _stnode)
|
||||
{
|
||||
dword node = dom_n(_idx);
|
||||
@@ -1090,6 +1143,30 @@ bool img_cover(dword _node, int _w, _h, _fit)
|
||||
RETURN true;
|
||||
}
|
||||
|
||||
// "height: 100%; width: auto" on a picture in the flow of a box of definite height
|
||||
// (lay_def_h): that height, the width by its aspect. virtualbox.org's 2208x552 logo in a
|
||||
// "height: 5vh" bar took the window. In an absolute box img_cover() does it.
|
||||
bool img_pct_h(dword _node, int _w, _h)
|
||||
{
|
||||
int k, t;
|
||||
IF (img_cb_h >= 0) RETURN false;
|
||||
IF (DSDWORD[_node + S_WIDTH]) RETURN false;
|
||||
k = DSDWORD[_node + S_HPCT];
|
||||
IF (!k) RETURN false;
|
||||
IF (lay_def_h <= 0) RETURN false;
|
||||
IF (_h < 1) RETURN false;
|
||||
t = k * lay_def_h;
|
||||
t /= 100;
|
||||
IF (t < 1) RETURN false;
|
||||
IF (t == _h) RETURN false;
|
||||
img_fit_h = t;
|
||||
k = _w * t;
|
||||
k /= _h;
|
||||
IF (k < 1) k = 1;
|
||||
img_fit_w = k;
|
||||
RETURN true;
|
||||
}
|
||||
|
||||
// the picture just emitted shows the part img_cover() cut
|
||||
void img_mark_cut()
|
||||
{
|
||||
@@ -1213,6 +1290,12 @@ void inline_image(dword _idx)
|
||||
}
|
||||
// the painter scales the picture to the size it gets here (F_IMGW)
|
||||
fit = 0;
|
||||
// measured at that size too: the flex row of virtualbox.org gave the logo 689px
|
||||
if (img_pct_h(node, w, h)) {
|
||||
w = img_fit_w;
|
||||
h = img_fit_h;
|
||||
IF (!lay_measuring) fit = w;
|
||||
}
|
||||
IF (!lay_measuring) {
|
||||
IF (img_fit(node, w, h)) { w = img_fit_w; h = img_fit_h; fit = w; }
|
||||
IF (img_cover(node, w, h, fit)) {
|
||||
@@ -1278,7 +1361,7 @@ void inline_image(dword _idx)
|
||||
}
|
||||
DSDWORD[node + S_BG] = IMG_PLACEHOLDER_BG;
|
||||
DSDWORD[node + S_COLOR] = IMG_PLACEHOLDER_TEXT;
|
||||
place_string(_idx, node, #label);
|
||||
place_words(_idx, node, #label);
|
||||
}
|
||||
|
||||
void inline_icon(dword _idx)
|
||||
@@ -1559,7 +1642,7 @@ void inline_control(dword _idx)
|
||||
IF (css_kw(v, "checkbox|radio")) {
|
||||
inline_check(_idx);
|
||||
return;
|
||||
} else IF (css_kw(v, "submit|button|reset|image")) {
|
||||
} else IF (css_kw(v, "submit|button|reset")) {
|
||||
v = dom_attr(_idx, "value");
|
||||
IF (!v) v = dom_attr(_idx, "alt");
|
||||
IF (!v) v = "Submit";
|
||||
@@ -1683,8 +1766,13 @@ void inline_block(dword _idx)
|
||||
ln_below = s_below;
|
||||
ln_flt_x = s_flt;
|
||||
|
||||
FOR (i = fs; i < frags.count; i++) { f = frags.f(i); DSDWORD[f + F_ATOMH] = h; }
|
||||
v = DSDWORD[node + S_FLAGS] & SF_VMID;
|
||||
FOR (i = fs; i < frags.count; i++) { f = frags.f(i); DSDWORD[f + F_ATOMH] = h; DSDWORD[f + F_AMID] = v; }
|
||||
IF (DSDWORD[node + S_FLOAT] == 2) ln_rw += w; ELSE ln_x += w;
|
||||
// the measure takes the box in: an empty one draws only its background, which
|
||||
// widens nothing (korrespondent.net: the 30px icon <span> after "Пошук" in a
|
||||
// float went to a line of its own)
|
||||
IF (ln_x > lay_max_x) lay_max_x = ln_x;
|
||||
IF (h > ln_atom) { ln_atom = h; ln_below = below; }
|
||||
// a float is out of the flow: a space after it still starts the line and is dropped
|
||||
// (Slashdot: "<form float:left 50%>\n<ul float:right 50%>" went onto two lines)
|
||||
@@ -1749,7 +1837,11 @@ void inline_node(dword _idx)
|
||||
// on a line of its own, as the page asked for
|
||||
v = DSDWORD[node + S_FLAGS] & SF_OWNLINE;
|
||||
IF (v) && (ln_has) line_new();
|
||||
inline_control(_idx);
|
||||
// <input type=image> is its picture, or "[alt]" that wraps like the one of
|
||||
// a missing <img>: as a one-line button the long alt of a PayPal button
|
||||
// that did not come stretched its table cell (publicdomaintorrents.info)
|
||||
IF (t == T_INPUT) && (is_image_input(_idx)) inline_image(_idx);
|
||||
ELSE inline_control(_idx);
|
||||
IF (v) line_new();
|
||||
return;
|
||||
case T_Q:
|
||||
@@ -1947,7 +2039,6 @@ void grid_abs(dword _idx, _top);
|
||||
void cols_log(dword _idx, _ncols, _total);
|
||||
#endif
|
||||
|
||||
int lay_float_depth;
|
||||
|
||||
// what a CSS width does not cover: by default it is the content alone, so the padding
|
||||
// and the border of the box come on top of it ("box-sizing: border-box" puts them in)
|
||||
@@ -1966,7 +2057,7 @@ int box_extra(dword _node, _base)
|
||||
// and leaves here its outer width, which negative margins can bring to 0 or below
|
||||
int float_outer;
|
||||
|
||||
void margin_pct(dword _node, int _avail);
|
||||
int margin_pct(dword _node, int _avail);
|
||||
|
||||
int float_width(dword _idx, _avail)
|
||||
{
|
||||
@@ -1987,6 +2078,10 @@ int float_width(dword _idx, _avail)
|
||||
w = measure_block(_idx, _avail);
|
||||
lay_float_depth--;
|
||||
IF (w <= 0) return 0; // margins, padding and border are in the measure
|
||||
// but a margin in percent was 0 in it (pct_margin_px while measuring): the room
|
||||
// is known here. hackaday.com's menu, "li { float: left; margin-right: 3.5% }",
|
||||
// had its items run together
|
||||
IF (!lay_measuring) w += margin_pct(_node, _avail);
|
||||
IF (w > _avail) w = _avail;
|
||||
IF (w < 8) w = 8;
|
||||
float_outer = w;
|
||||
@@ -2253,14 +2348,19 @@ bool lay_new_ctx(dword _in_flow, _own)
|
||||
// that paints its box is put beside it (beside_floats).
|
||||
// the margin-bottom that came out of the bottom of the block just laid out
|
||||
int lay_tail;
|
||||
// the collapsed margin above the block flow_children() lays out next, read (and reset)
|
||||
// by layout_block: the margin-top of its first child joins it. NO_TOP_M for a block
|
||||
// placed by anything else, which drops that margin as before
|
||||
#define NO_TOP_M 0x80000000
|
||||
int lay_top_m = NO_TOP_M;
|
||||
|
||||
void flow_children(dword _idx, _collapse_through)
|
||||
void flow_children(dword _idx, _collapse_through, _top_m)
|
||||
{
|
||||
bool had_fl;
|
||||
bool had_in;
|
||||
dword c = dom_nfirst(_idx);
|
||||
dword cn, disp, t, p, side;
|
||||
int m, prev_margin = 0;
|
||||
int m, top, prev_margin = 0;
|
||||
int fl_w, x, y, fy, k;
|
||||
bool first = true;
|
||||
|
||||
@@ -2331,17 +2431,34 @@ void flow_children(dword _idx, _collapse_through)
|
||||
continue;
|
||||
}
|
||||
m = collapse_margin(DSDWORD[cn + S_MTOP], prev_margin);
|
||||
IF (first) && (_collapse_through) m = 0;
|
||||
top = m;
|
||||
if (first) && (_collapse_through) {
|
||||
// the margin-top of the first child comes out through the top of a block
|
||||
// without a border or padding: it joins the margin above that block, only
|
||||
// what it adds moves the child down (rbc.ua: "#app > .main-menu" 64px under
|
||||
// the fixed header, laid at 0 when the margin was dropped)
|
||||
IF (_top_m == NO_TOP_M) {
|
||||
// dropped, and so is the margin of its own first child: a wrapper <div>
|
||||
// must not bring back the margin of the <h3> in it
|
||||
m = 0;
|
||||
top = NO_TOP_M;
|
||||
} ELSE {
|
||||
top = collapse_margin(DSDWORD[cn + S_MTOP], _top_m);
|
||||
m = top - _top_m;
|
||||
}
|
||||
}
|
||||
y = blk_y + m;
|
||||
IF (y < 0) y = 0;
|
||||
had_in = true;
|
||||
IF (block_may_carry(c)) {
|
||||
// a plain block: the floats reach into its lines, its own floats may reach out
|
||||
lay_in_flow = true;
|
||||
lay_top_m = top;
|
||||
k = layout_block(c, blk_x, y, blk_w);
|
||||
} ELSE {
|
||||
y = beside_floats(c, y);
|
||||
lay_in_flow = true;
|
||||
lay_top_m = top;
|
||||
IF (beside_w) {
|
||||
IF (beside_fix) lay_skip_width = true;
|
||||
k = layout_block(c, beside_x, y, beside_w);
|
||||
@@ -2532,14 +2649,25 @@ int pct_margin_px(int _pct, _avail)
|
||||
RETURN k;
|
||||
}
|
||||
|
||||
// margin-left / margin-right in percent (S_MLPCT, S_MRPCT) into px of the room _avail
|
||||
void margin_pct(dword _node, int _avail)
|
||||
// margin-left / margin-right in percent (S_MLPCT, S_MRPCT) into px of the room _avail:
|
||||
// their sum in px (float_width adds it to the measure, where they were 0)
|
||||
int margin_pct(dword _node, int _avail)
|
||||
{
|
||||
int v;
|
||||
int v, sum;
|
||||
sum = 0;
|
||||
v = DSDWORD[_node + S_MLPCT];
|
||||
IF (v) DSDWORD[_node + S_MLEFT] = pct_margin_px(v, _avail);
|
||||
if (v) {
|
||||
v = pct_margin_px(v, _avail);
|
||||
DSDWORD[_node + S_MLEFT] = v;
|
||||
sum += v;
|
||||
}
|
||||
v = DSDWORD[_node + S_MRPCT];
|
||||
IF (v) DSDWORD[_node + S_MRIGHT] = pct_margin_px(v, _avail);
|
||||
if (v) {
|
||||
v = pct_margin_px(v, _avail);
|
||||
DSDWORD[_node + S_MRIGHT] = v;
|
||||
sum += v;
|
||||
}
|
||||
RETURN sum;
|
||||
}
|
||||
|
||||
// What frag_clip() cuts. By default every fragment: a picture or a box is cut at the
|
||||
@@ -2579,7 +2707,18 @@ void frag_clip(dword _from, int _l, _t, _r, _b, dword _mode)
|
||||
IF (m) continue;
|
||||
d = h / 2;
|
||||
d += y;
|
||||
IF (d < _t) || (d >= _b) { DSDWORD[f + F_KIND] = 0; continue; }
|
||||
IF (d >= _b) { DSDWORD[f + F_KIND] = 0; continue; }
|
||||
// text across the top edge is cut to the pixel (paint.h keeps the rows above
|
||||
// F_Y), as a browser does: the title of a card pulled up under the edge of
|
||||
// its overflow:hidden box (tailwindcss.com, "Browse properties")
|
||||
if (y < _t) && (k == FR_TEXT) {
|
||||
d = _t - y;
|
||||
DSDWORD[f + F_IMGY] += d;
|
||||
y = _t;
|
||||
h -= d;
|
||||
DSDWORD[f + F_Y] = y;
|
||||
DSDWORD[f + F_H] = h;
|
||||
} else IF (d < _t) { DSDWORD[f + F_KIND] = 0; continue; }
|
||||
IF (k == FR_ICON) {
|
||||
d = w / 2;
|
||||
d += x;
|
||||
@@ -2782,6 +2921,45 @@ void col_flex_spread(dword _idx, _from, int _left)
|
||||
}
|
||||
}
|
||||
|
||||
// one side of an outline: a bar in the outline colour (the text colour for none)
|
||||
void outline_bar(dword _idx, _node, int _x, _y, _w, _h)
|
||||
{
|
||||
dword f;
|
||||
f = frag_emit(_idx, FR_HLINE, _x, _y, _w, _h, _node);
|
||||
f = frags.f(f);
|
||||
IF (DSDWORD[_node + S_OUTC] != NO_COLOR) DSDWORD[f + F_COLOR] = DSDWORD[_node + S_OUTC];
|
||||
}
|
||||
|
||||
// "outline": four bars around the box _x, _y, _w, _h (its border box), outline-offset
|
||||
// away from it, drawn after its content and taking no room. Only a block or an
|
||||
// inline-block has one: an inline element broken over lines goes without
|
||||
void lay_outline(dword _idx, _node, int _x, _y, _w, _h)
|
||||
{
|
||||
int t, o, x, y, w, h, k;
|
||||
t = DSDWORD[_node + S_OUTW];
|
||||
IF (!t) return;
|
||||
// "outline: 2px solid transparent" (Tailwind's .outline-none): nothing to draw
|
||||
IF (DSDWORD[_node + S_OUTC] == CLEAR_COLOR) return;
|
||||
o = DSDWORD[_node + S_OUTOFF];
|
||||
o += t;
|
||||
x = _x - o;
|
||||
y = _y - o;
|
||||
w = o * 2;
|
||||
w += _w;
|
||||
h = o * 2;
|
||||
h += _h;
|
||||
k = t * 2;
|
||||
IF (w <= k) || (h <= k) return;
|
||||
outline_bar(_idx, _node, x, y, w, t);
|
||||
k = y + h;
|
||||
k -= t;
|
||||
outline_bar(_idx, _node, x, k, w, t);
|
||||
outline_bar(_idx, _node, x, y, t, h);
|
||||
k = x + w;
|
||||
k -= t;
|
||||
outline_bar(_idx, _node, k, y, t, h);
|
||||
}
|
||||
|
||||
dword layout_block(dword _idx, _x, _y, _w)
|
||||
{
|
||||
dword node = dom_n(_idx);
|
||||
@@ -2803,9 +2981,12 @@ dword layout_block(dword _idx, _x, _y, _w)
|
||||
int ps_b, ps_a, cw;
|
||||
int pct_h, s_def_h;
|
||||
int tail = 0;
|
||||
int top_m;
|
||||
|
||||
in_flow = lay_in_flow;
|
||||
lay_in_flow = false;
|
||||
top_m = lay_top_m;
|
||||
lay_top_m = NO_TOP_M;
|
||||
lay_tail = 0;
|
||||
IF (disp == D_NONE) return 0;
|
||||
only_floats = false;
|
||||
@@ -3014,7 +3195,7 @@ dword layout_block(dword _idx, _x, _y, _w)
|
||||
ELSE IF (DSDWORD[node + S_FLEX] & FLEX_ON) k = flex_children(_idx);
|
||||
}
|
||||
lay_only_floats = false;
|
||||
IF (!k) { flow_children(_idx, collapse_through); IF (!DSDWORD[node + S_HEIGHT]) tail = lay_tail; }
|
||||
IF (!k) { flow_children(_idx, collapse_through, top_m); IF (!DSDWORD[node + S_HEIGHT]) tail = lay_tail; }
|
||||
only_floats = lay_only_floats;
|
||||
}
|
||||
blk_y += ps_a;
|
||||
@@ -3110,11 +3291,16 @@ dword layout_block(dword _idx, _x, _y, _w)
|
||||
IF (DSDWORD[node + S_FLAGS] & SF_CLIP) && (!lay_measuring) {
|
||||
k = _y + height;
|
||||
frag_clip(clip_f, CLIP_NEAR, CLIP_NEAR, CLIP_FAR, k, CLIP_BOXES);
|
||||
// and at its top: a card pulled up by a negative margin (tailwindcss.com "-mt-26"
|
||||
// in the CSS grid demo) is cut there, not drawn over the text above the box
|
||||
k = _y + DSDWORD[node + S_BORDT];
|
||||
frag_clip(clip_f, CLIP_NEAR, k, CLIP_FAR, CLIP_FAR, 0);
|
||||
}
|
||||
k = height;
|
||||
IF (only_floats) && (!own_ctx) { v = edge_t + edge_b; k = floats_only_h(_idx, node, height, v); }
|
||||
frag_set_h(box_f, k);
|
||||
frag_set_h(rect_f, k);
|
||||
IF (DSDWORD[node + S_OUTW]) && (!lay_measuring) lay_outline(_idx, node, _x, _y, _w, k);
|
||||
DSDWORD[node + L_H] = height;
|
||||
// position:relative offsets and transform:translate() move the box as drawn,
|
||||
// with everything inside it; the flow around keeps its place
|
||||
@@ -3228,6 +3414,28 @@ void abs_clip(dword _cb, _from, int _cy, _ch)
|
||||
frag_clip(_from, l, t, r, b, 0);
|
||||
}
|
||||
|
||||
// a box from _cb up cuts what overflows it at the bottom: an absolute box inside does
|
||||
// not make the page longer, the clipping box is in the page's height itself.
|
||||
// abs_clip() cuts at the bottom of _cb alone, the others are not laid out yet
|
||||
// (rbc.ua: the times of 400 news, "position:absolute" in their items, in a 1500px
|
||||
// "overflow:hidden" scroller left meters of empty page under the footer)
|
||||
bool abs_in_clip(dword _cb)
|
||||
{
|
||||
dword p, pn, k;
|
||||
p = _cb;
|
||||
while (p) {
|
||||
// the overflow of <html> and <body> is that of the window: the page scrolls
|
||||
IF (p == dom.body) || (p == dom.root) RETURN false;
|
||||
pn = dom_n(p);
|
||||
IF (DSDWORD[pn + N_TYPE] == T_HTML) RETURN false;
|
||||
k = DSDWORD[pn + S_FLAGS];
|
||||
IF (DSDWORD[pn + S_DISPLAY] == D_INLINE) && (!(k & SF_INLBLOCK)) k = 0;
|
||||
IF (k & SF_CLIP) RETURN true;
|
||||
p = dom_nparent(p);
|
||||
}
|
||||
RETURN false;
|
||||
}
|
||||
|
||||
// the page scrolls down to the lowest fragment of an absolute box
|
||||
void abs_reach(dword _from)
|
||||
{
|
||||
@@ -3471,7 +3679,9 @@ void layout_abs_box(dword _idx, int _sx, _sy, dword _cb, int _cx, _cy, _cw, _ch)
|
||||
abs_clip(_cb, fs, _cy, _ch);
|
||||
// out of the flow: it widens nothing, a fixed box does not make the page longer
|
||||
lay_max_x = s_max;
|
||||
IF (!(DSDWORD[node + S_FLAGS] & SF_FIXED)) abs_reach(fs);
|
||||
IF (!(DSDWORD[node + S_FLAGS] & SF_FIXED)) {
|
||||
IF (!abs_in_clip(_cb)) abs_reach(fs);
|
||||
}
|
||||
}
|
||||
|
||||
// Place the absolute boxes waiting in the queue from _from on against the containing
|
||||
@@ -3939,6 +4149,77 @@ bool flex_children(dword _idx)
|
||||
#define GRID_ITEMS 2048
|
||||
#define GRID_COLS 16
|
||||
|
||||
// The widest width in px an item of a grid asks for itself: its own "width", else that
|
||||
// of its one element child (through display:contents and plain wrappers, a few levels
|
||||
// down). 0: none. The cards of theguardian.com's carousel are "width: 280px" inside an
|
||||
// <li> and a display:contents <div>.
|
||||
int grid_item_own_w(dword _c)
|
||||
{
|
||||
dword c, cn, ch, one;
|
||||
int d, w, most, k;
|
||||
most = 0;
|
||||
c = _c;
|
||||
FOR (d = 0; d < 4; d++) {
|
||||
cn = dom_n(c);
|
||||
w = DSDWORD[cn + S_WIDTH];
|
||||
IF (w > 0) {
|
||||
w += box_extra(cn, 0);
|
||||
k = DSDWORD[cn + S_MLEFT];
|
||||
IF (k > 0) w += k;
|
||||
k = DSDWORD[cn + S_MRIGHT];
|
||||
IF (k > 0) w += k;
|
||||
RETURN w;
|
||||
}
|
||||
// the one child in the flow that is an element
|
||||
one = 0;
|
||||
ch = dom_nfirst(c);
|
||||
WHILE (ch) {
|
||||
IF (dom_ntype(ch) != T_TEXT) {
|
||||
cn = dom_n(ch);
|
||||
IF (DSDWORD[cn + S_DISPLAY] != D_NONE) && (DSDWORD[cn + S_POS] != POS_ABSOLUTE) {
|
||||
IF (one) RETURN 0;
|
||||
one = ch;
|
||||
}
|
||||
}
|
||||
ch = dom_nnext(ch);
|
||||
}
|
||||
IF (!one) RETURN 0;
|
||||
c = one;
|
||||
}
|
||||
RETURN 0;
|
||||
}
|
||||
|
||||
// "repeat(6, 1fr)" whose items are wider than a sixth of the row: in browsers the fr
|
||||
// columns grow to their items (an fr column is at least its min-content) and the row
|
||||
// scrolls sideways - theguardian.com's highlights carousel. There is no horizontal
|
||||
// scrolling here: fewer columns, as many as hold the widest item, and the rest wraps
|
||||
// to new rows. Before, six 100px columns squeezed each card's headline to one letter
|
||||
// per line beside its picture. The new column count, or _ncols when it stays.
|
||||
int grid_fit_cols(dword _items, int _n, _ncols, _avail, _gap, dword _trk)
|
||||
{
|
||||
int i, j, w, most, k, v;
|
||||
dword p;
|
||||
// every column an fr one (in an int: a dword is never below 0)
|
||||
FOR (j = 0; j < _ncols; j++) {
|
||||
p = j * 4;
|
||||
p += _trk;
|
||||
v = DSDWORD[p];
|
||||
IF (v >= 0) RETURN _ncols;
|
||||
}
|
||||
most = 0;
|
||||
FOR (i = 0; i < _n; i++) {
|
||||
w = grid_item_own_w(gget(_items, i));
|
||||
IF (w > most) most = w;
|
||||
}
|
||||
IF (most <= 0) RETURN _ncols;
|
||||
k = _avail + _gap;
|
||||
v = most + _gap;
|
||||
k /= v;
|
||||
IF (k < 1) k = 1;
|
||||
IF (k >= _ncols) RETURN _ncols;
|
||||
RETURN k;
|
||||
}
|
||||
|
||||
dword gget(dword _a, _i)
|
||||
{
|
||||
dword p;
|
||||
@@ -4115,6 +4396,8 @@ bool grid_children(dword _idx)
|
||||
IF (!n) { free(items); grid_abs(_idx, blk_y); return true; }
|
||||
if (areas) {
|
||||
IF (!grid_areas_key(areas, items, n)) { free(items); return false; }
|
||||
} else IF (DSDWORD[node + S_GRIDN] > 1) {
|
||||
ncols = grid_fit_cols(items, n, ncols, avail, gap, #trk);
|
||||
}
|
||||
flex_sort_order(items, n);
|
||||
buf = malloc(n * 16);
|
||||
@@ -4143,8 +4426,22 @@ bool grid_children(dword _idx)
|
||||
cs = DSDWORD[cn + S_COLSPAN];
|
||||
cs = math.min(cs, ncols);
|
||||
cs = math.max(cs, 1);
|
||||
k = col + cs;
|
||||
IF (k > ncols) { col = 0; row++; }
|
||||
k = DSDWORD[cn + S_GCOL];
|
||||
if (k > 0) {
|
||||
// "grid-column-start: 2": that column, in the next row when the cursor
|
||||
// is past it (tailwindcss.com: the footer in the middle column between
|
||||
// two narrow gutters, not in the left gutter)
|
||||
k--;
|
||||
v = ncols - 1;
|
||||
k = math.min(k, v);
|
||||
IF (k < col) row++;
|
||||
col = k;
|
||||
k = ncols - col;
|
||||
cs = math.min(cs, k);
|
||||
} else {
|
||||
k = col + cs;
|
||||
IF (k > ncols) { col = 0; row++; }
|
||||
}
|
||||
}
|
||||
gset(icol, i, col);
|
||||
gset(ispan, i, cs);
|
||||
@@ -4178,6 +4475,8 @@ bool grid_children(dword _idx)
|
||||
for (j = 0; j < ncols; j++) {
|
||||
k = trk[j];
|
||||
IF (cmax[j] < cmin[j]) cmax[j] = cmin[j];
|
||||
// "minmax(0, 96rem)" starts as 96rem, the end of this loop narrows it
|
||||
IF (k >= GT_CAP) && (k < GT_PCT) k -= GT_CAP;
|
||||
IF (k >= GT_PCT) {
|
||||
v = k - GT_PCT;
|
||||
v *= room;
|
||||
@@ -4259,6 +4558,18 @@ bool grid_children(dword _idx)
|
||||
FOR (j = 0; j < ncols; j++) IF (frz[j] == 2) cw[j] += v;
|
||||
}
|
||||
}
|
||||
// a "minmax(0, 96rem)" column gives back what the row lacks
|
||||
used = 0;
|
||||
FOR (j = 0; j < ncols; j++) used += cw[j];
|
||||
need = used - room;
|
||||
for (j = 0; j < ncols; j++) {
|
||||
IF (need <= 0) BREAK;
|
||||
k = trk[j];
|
||||
IF (k < GT_CAP) || (k >= GT_PCT) CONTINUE;
|
||||
v = math.min(need, cw[j]);
|
||||
cw[j] -= v;
|
||||
need -= v;
|
||||
}
|
||||
|
||||
// ---- rows ----
|
||||
rowy = blk_y;
|
||||
|
||||
+111
-2
@@ -44,6 +44,112 @@ void cv_bar(int _x, _y, _w, _h, dword _color)
|
||||
canvas.DrawBar(_x, _y, x2 - _x, y2 - _y, _color);
|
||||
}
|
||||
|
||||
// a bar of the color _argb (alpha << 24 + RGB) mixed into what the canvas holds
|
||||
void cv_blend_bar(int _x, _y, _w, _h, dword _argb)
|
||||
{
|
||||
int x2, y2, y;
|
||||
dword a, na, p, e, row, n, stride, d, s, v, k, sr, sg, sb;
|
||||
x2 = _x + _w;
|
||||
y2 = _y + _h;
|
||||
IF (_x < 0) _x = 0;
|
||||
IF (_y < 0) _y = 0;
|
||||
IF (x2 > canvas.bufw) x2 = canvas.bufw;
|
||||
IF (y2 >= canvas.bufh) y2 = canvas.bufh - 1;
|
||||
IF (x2 <= _x) || (y2 <= _y) return;
|
||||
a = _argb >> 24;
|
||||
na = 255 - a;
|
||||
sr = _argb >> 16; sr &= 0xFF; sr *= a;
|
||||
sg = _argb >> 8; sg &= 0xFF; sg *= a;
|
||||
sb = _argb & 0xFF; sb *= a;
|
||||
stride = canvas.bufw * 4;
|
||||
row = _y * stride;
|
||||
row += buf_data;
|
||||
row += 8;
|
||||
k = _x * 4;
|
||||
row += k;
|
||||
n = x2 - _x;
|
||||
n *= 4;
|
||||
// a full-window backdrop is blended on every band painted: no division per pixel,
|
||||
// "x / 255" is (x + 1 + (x >> 8)) >> 8, exact up to 255 * 255
|
||||
for (y = _y; y < y2; y++) {
|
||||
p = row;
|
||||
e = row + n;
|
||||
while (p < e) {
|
||||
d = DSDWORD[p];
|
||||
s = d >> 16 & 0xFF; s *= na; s += sr;
|
||||
k = s >> 8; s += k; s++; s >>= 8;
|
||||
v = s << 16;
|
||||
s = d >> 8 & 0xFF; s *= na; s += sg;
|
||||
k = s >> 8; s += k; s++; s >>= 8;
|
||||
s <<= 8;
|
||||
v |= s;
|
||||
s = d & 0xFF; s *= na; s += sb;
|
||||
k = s >> 8; s += k; s++; s >>= 8;
|
||||
v |= s;
|
||||
v |= 0xFF000000;
|
||||
DSDWORD[p] = v;
|
||||
p += 4;
|
||||
}
|
||||
row += stride;
|
||||
}
|
||||
}
|
||||
|
||||
// a background box: a translucent one of a positioned box (F_P) is mixed into what is
|
||||
// painted under it, as a browser does; any other is a plain bar of its blended color
|
||||
void paint_box(dword _f, int _x, _y, _w, _h)
|
||||
{
|
||||
dword c, n;
|
||||
c = DSDWORD[_f + F_BG];
|
||||
if (c != NO_COLOR) {
|
||||
n = dom_n(DSDWORD[_f + F_NODE]);
|
||||
n = DSDWORD[n + S_BGA];
|
||||
IF (n) && (DSDWORD[_f + F_P]) cv_blend_bar(_x, _y, _w, _h, n);
|
||||
ELSE cv_bar(_x, _y, _w, _h, c);
|
||||
}
|
||||
paint_bg_image(DSDWORD[_f + F_NODE], _x, _y, _w, _h);
|
||||
}
|
||||
|
||||
// canvas rows [_y1, _y2) saved by cv_rows_keep(), put back by cv_rows_back(). One
|
||||
// buffer for all, it only grows: no malloc per text run on every repaint
|
||||
dword cv_keep_buf, cv_keep_cap;
|
||||
int cv_keep_y, cv_keep_n;
|
||||
|
||||
void cv_rows_keep(int _y1, _y2)
|
||||
{
|
||||
dword a;
|
||||
IF (_y1 < 0) _y1 = 0;
|
||||
IF (_y2 > canvas.bufh) _y2 = canvas.bufh;
|
||||
cv_keep_n = _y2 - _y1;
|
||||
IF (cv_keep_n <= 0) { cv_keep_n = 0; return; }
|
||||
cv_keep_n *= canvas.bufw;
|
||||
cv_keep_n *= 4;
|
||||
cv_keep_y = _y1;
|
||||
if (cv_keep_n > cv_keep_cap) {
|
||||
IF (cv_keep_buf) free(cv_keep_buf);
|
||||
cv_keep_cap = 0;
|
||||
cv_keep_buf = malloc(cv_keep_n);
|
||||
IF (!cv_keep_buf) { cv_keep_n = 0; return; }
|
||||
cv_keep_cap = cv_keep_n;
|
||||
}
|
||||
a = _y1 * canvas.bufw;
|
||||
a *= 4;
|
||||
a += buf_data;
|
||||
a += 8;
|
||||
memmov(cv_keep_buf, a, cv_keep_n);
|
||||
}
|
||||
|
||||
void cv_rows_back()
|
||||
{
|
||||
dword a;
|
||||
IF (!cv_keep_n) return;
|
||||
a = cv_keep_y * canvas.bufw;
|
||||
a *= 4;
|
||||
a += buf_data;
|
||||
a += 8;
|
||||
memmov(a, cv_keep_buf, cv_keep_n);
|
||||
cv_keep_n = 0;
|
||||
}
|
||||
|
||||
void cv_rect(int _x, _y, _w, _h, _t, dword _color)
|
||||
{
|
||||
cv_bar(_x, _y, _w, _t, _color);
|
||||
@@ -610,12 +716,16 @@ void paint_text_frag(dword _i, _f, int _x, _y)
|
||||
}
|
||||
IF (DSDWORD[_f + F_LINK]) && (DSDWORD[_f + F_LINK] == hover_link) color = body_link_active;
|
||||
|
||||
// a run cut at the top by an overflow (layout.h frag_clip): its glyphs start above
|
||||
// _y, the rows there are kept as they are
|
||||
IF (DSDWORD[_f + F_IMGY]) cv_rows_keep(ty, _y);
|
||||
cv_text(_x, ty, font, color, str, len);
|
||||
IF (flags & SF_BOLD) cv_text(_x + 1, ty, font, color, str, len);
|
||||
IF (flags & SF_ITALIC) { k = len * fw; cv_slant(_x, ty, k, fh); }
|
||||
IF (flags & SF_UNDER) cv_bar(_x, ty + fh, w, zoom, color);
|
||||
IF (flags & SF_STRIKE) { k = fh / 2; cv_bar(_x, ty + k, w, zoom, color); }
|
||||
IF (caret >= 0) { k = caret * fw; cv_bar(_x + k, ty, zoom + 1, fh, color); }
|
||||
cv_rows_back();
|
||||
}
|
||||
|
||||
// ---- page -------------------------------------------------------------------------
|
||||
@@ -662,8 +772,7 @@ void paint_area(int _scroll, _view_h)
|
||||
kind = DSDWORD[f + F_KIND];
|
||||
switch (kind) {
|
||||
CASE FR_BOX:
|
||||
IF (DSDWORD[f + F_BG] != NO_COLOR) cv_bar(fx, fy, fw, fh, DSDWORD[f + F_BG]);
|
||||
paint_bg_image(DSDWORD[f + F_NODE], fx, fy, fw, fh);
|
||||
paint_box(f, fx, fy, fw, fh);
|
||||
break;
|
||||
CASE FR_RECT:
|
||||
paint_border(DSDWORD[f + F_NODE], fx, fy, fw, fh, DSDWORD[f + F_COLOR], DSDWORD[f + F_TEXT]);
|
||||
|
||||
+351
-40
@@ -2,7 +2,7 @@
|
||||
// Order of precedence (low -> high): inherited values, UA defaults by tag,
|
||||
// presentational attributes, <style> rules, style="" attribute.
|
||||
|
||||
#define DEFAULT_BG_COL 0xFBE5E4 // peach
|
||||
#define DEFAULT_BG_COL 0xFFFFFF
|
||||
#define DEFAULT_TEXT_COL 0x000000
|
||||
#define DEFAULT_LINK_COL 0x0000FF
|
||||
#define DEFAULT_LINK_ACTIVE 0xFF0000
|
||||
@@ -65,7 +65,7 @@ dword line_h(dword f) { RETURN font_hs[f] + LINE_GAP; }
|
||||
// of its text is bigger: a page set in 10px all over keeps the font the user chose.
|
||||
// style_compute counts the letters, style_tree decides.
|
||||
#define TINY_FPX 100
|
||||
dword text_all, text_tiny;
|
||||
dword text_all, text_tiny, text_big;
|
||||
|
||||
bool node_tiny(dword _node)
|
||||
{
|
||||
@@ -74,33 +74,89 @@ bool node_tiny(dword _node)
|
||||
RETURN true;
|
||||
}
|
||||
|
||||
// Text a page sets in 16px and more gets the medium (from 16px) or the big (from 22px)
|
||||
// font, but only while most of the page is smaller. When most of its letters are in
|
||||
// them, the size most of those are in is the body text of the page (rbc.ua: 16px,
|
||||
// browser.engineering: "html { font-size: 24px }"): it gets the default font, and the
|
||||
// bigger text steps down against it, so the headings still stand out.
|
||||
// The headings of the UA style (<h1>..<h3>) keep the em of their parent: not counted.
|
||||
#define BIG_FPX 160
|
||||
#define BIG_HIST 101 // letters per px of font-size, 100px and more in the last one
|
||||
dword text_big_px[BIG_HIST];
|
||||
bool node_big(dword _node)
|
||||
{
|
||||
dword f = DSDWORD[_node + S_FONT];
|
||||
IF (f != FONT_MEDIUM) && (f != FONT_BIG) RETURN false;
|
||||
IF (DSDWORD[_node + S_FPX] < BIG_FPX) RETURN false;
|
||||
RETURN true;
|
||||
}
|
||||
|
||||
void text_big_clear()
|
||||
{
|
||||
dword i;
|
||||
FOR (i = 0; i < BIG_HIST; i++) DSDWORD[i * 4 + #text_big_px] = 0;
|
||||
}
|
||||
|
||||
// the px of font-size of _node as an index of text_big_px
|
||||
dword node_big_px(dword _node)
|
||||
{
|
||||
dword px = DSDWORD[_node + S_FPX] / 10;
|
||||
IF (px >= BIG_HIST) px = BIG_HIST - 1;
|
||||
RETURN px;
|
||||
}
|
||||
|
||||
// the letters (not the spaces) of text node _idx, a child of the element node _node
|
||||
void text_count(dword _idx, _node)
|
||||
{
|
||||
dword s;
|
||||
bool tiny;
|
||||
dword s, n, h;
|
||||
IF (dom_ntype(_idx) != T_TEXT) RETURN;
|
||||
tiny = node_tiny(_node);
|
||||
s = dom_ntext(_idx);
|
||||
IF (!s) RETURN;
|
||||
n = 0;
|
||||
WHILE (DSBYTE[s]) {
|
||||
IF (DSBYTE[s] > ' ') {
|
||||
text_all++;
|
||||
IF (tiny) text_tiny++;
|
||||
}
|
||||
IF (DSBYTE[s] > ' ') n++;
|
||||
s++;
|
||||
}
|
||||
text_all += n;
|
||||
IF (node_tiny(_node)) text_tiny += n;
|
||||
if (node_big(_node)) {
|
||||
text_big += n;
|
||||
h = node_big_px(_node) * 4;
|
||||
h += #text_big_px;
|
||||
DSDWORD[h] += n;
|
||||
}
|
||||
}
|
||||
|
||||
void style_small_print()
|
||||
{
|
||||
dword i, node;
|
||||
dword i, node, n, most, body, r;
|
||||
i = text_tiny * 2;
|
||||
IF (i >= text_all) RETURN;
|
||||
if (i < text_all) {
|
||||
FOR (i = 1; i < dom.count; i++) {
|
||||
IF (dom_ntype(i) == T_TEXT) CONTINUE;
|
||||
node = dom_n(i);
|
||||
IF (node_tiny(node)) DSDWORD[node + S_FONT] = FONT_SMALL;
|
||||
}
|
||||
}
|
||||
i = text_big * 2;
|
||||
IF (i < text_all) return;
|
||||
// the size of the body text: the one with the most letters
|
||||
body = BIG_FPX / 10;
|
||||
most = 0;
|
||||
FOR (i = 0; i < BIG_HIST; i++) {
|
||||
n = DSDWORD[i * 4 + #text_big_px];
|
||||
IF (n > most) { most = n; body = i; }
|
||||
}
|
||||
FOR (i = 1; i < dom.count; i++) {
|
||||
IF (dom_ntype(i) == T_TEXT) CONTINUE;
|
||||
node = dom_n(i);
|
||||
IF (node_tiny(node)) DSDWORD[node + S_FONT] = FONT_SMALL;
|
||||
IF (!node_big(node)) CONTINUE;
|
||||
// up to a quarter bigger than the body text: the default font; up to 3/4
|
||||
// bigger: the medium one at most
|
||||
r = DSDWORD[node + S_FPX] * 10;
|
||||
r /= body;
|
||||
IF (r < 125) DSDWORD[node + S_FONT] = FONT_NORMAL;
|
||||
ELSE IF (r < 175) DSDWORD[node + S_FONT] = FONT_MEDIUM;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1076,13 +1132,51 @@ void css_import(dword _p, _end)
|
||||
css_import_depth--;
|
||||
}
|
||||
|
||||
// "@supports not (...)" between _p and _end: the block is a fallback for what we lack
|
||||
bool css_supports_not(dword _p, _end)
|
||||
// a color function css_color() cannot read, between _p and _end (0: the string end).
|
||||
// Tailwind v4 gives every color as hex, then again as lab() / color-mix() under
|
||||
// "@supports (color:lab(0% 0 0))": the hex is the value for a browser without them.
|
||||
// css_apply_decl() runs it on every value it applies: only the name before each "("
|
||||
// is looked at, most values have none
|
||||
bool css_fn_unknown(dword _s, _q)
|
||||
{
|
||||
dword p, c;
|
||||
p = _q;
|
||||
WHILE (p > _s) {
|
||||
c = DSBYTE[p - 1];
|
||||
IF (c >= 'a') && (c <= 'z') { p--; CONTINUE; }
|
||||
IF (c != '-') BREAK;
|
||||
p--;
|
||||
}
|
||||
c = _q - p;
|
||||
IF (!c) RETURN false;
|
||||
IF (css_name_idx(p, c, "lab|oklab|lch|oklch|hwb|hsl|hsla|color|color-mix")) RETURN true;
|
||||
RETURN false;
|
||||
}
|
||||
|
||||
bool css_unknown_color(dword _p, _end)
|
||||
{
|
||||
dword p;
|
||||
p = _p;
|
||||
WHILE (DSBYTE[p]) {
|
||||
IF (_end) && (p >= _end) BREAK;
|
||||
IF (DSBYTE[p] == 0x28) && (css_fn_unknown(_p, p)) RETURN true;
|
||||
p++;
|
||||
}
|
||||
RETURN false;
|
||||
}
|
||||
|
||||
// "@supports ..." between _p and _end holds: "not (...)" is a fallback for what we
|
||||
// lack (it holds when that is a color we cannot read), a test of such a color fails.
|
||||
// Anything else is taken to hold.
|
||||
bool css_supports(dword _p, _end)
|
||||
{
|
||||
dword p = _p;
|
||||
bool bad;
|
||||
WHILE (p < _end) && (__isWhite(DSBYTE[p])) p++;
|
||||
IF (!strncmp(p, "not", 3)) RETURN true;
|
||||
RETURN false;
|
||||
bad = css_unknown_color(p, _end);
|
||||
IF (!strncmp(p, "not", 3)) RETURN bad;
|
||||
IF (bad) RETURN false;
|
||||
RETURN true;
|
||||
}
|
||||
|
||||
// parse a stylesheet text: "sel, sel { decls } ..."
|
||||
@@ -1387,9 +1481,9 @@ void css_parse_sheet(dword _text)
|
||||
IF (!s) return;
|
||||
IF (!strncmp(p, "@media", 6)) && (css_media_applies(p + 6, s - p - 6)) { p = s + 1; continue; }
|
||||
// cascade layers hold whole sheets (Tailwind v4): walk in, the order of the
|
||||
// layers is not kept. "@supports (...)" is taken to hold, "@supports not" not.
|
||||
// layers is not kept.
|
||||
IF (!strncmp(p, "@layer", 6)) { p = s + 1; continue; }
|
||||
IF (!strncmp(p, "@supports", 9)) && (!css_supports_not(p + 9, s)) { p = s + 1; continue; }
|
||||
IF (!strncmp(p, "@supports", 9)) && (css_supports(p + 9, s)) { p = s + 1; continue; }
|
||||
// find the matching brace
|
||||
n = 0; p = s;
|
||||
WHILE (DSBYTE[p]) {
|
||||
@@ -1727,21 +1821,27 @@ dword css_prev_element(dword _idx)
|
||||
// that. Between two parts ">" means the direct parent, "+" the element right before,
|
||||
// "~" any element before (same parent). "li > span" must not reach a span nested in a
|
||||
// link; ".wrapper + .static" hides a fallback picture only after its wrapper.
|
||||
// A combinator is a part of its own: "#ctxtnav > ul > li.first > a > img" of
|
||||
// virtualbox.org is 9 parts. Over 16 the first ones are dropped and the rest must match;
|
||||
// a chain that then starts with a combinator matches once its parts up to there do
|
||||
// (with 8 parts that selector never matched: its icon took its own 232px height).
|
||||
bool css_match(dword _idx, _sel)
|
||||
{
|
||||
dword parts[8];
|
||||
dword lens[8];
|
||||
dword parts[16];
|
||||
dword lens[16];
|
||||
int np, i, comb;
|
||||
dword p, s, cur;
|
||||
bool cut = false;
|
||||
np = 0;
|
||||
p = _sel;
|
||||
s = _sel;
|
||||
WHILE (1) {
|
||||
IF (DSBYTE[p] == ' ') || (DSBYTE[p] == 0) {
|
||||
// over 8 parts: keep the last 8, the subject must stay the real last part
|
||||
IF (np == 8) {
|
||||
FOR (i = 0; i < 7; i++) { parts[i] = parts[i+1]; lens[i] = lens[i+1]; }
|
||||
np = 7;
|
||||
// over 16 parts: keep the last 16, the subject must stay the real last part
|
||||
IF (np == 16) {
|
||||
FOR (i = 0; i < 15; i++) { parts[i] = parts[i+1]; lens[i] = lens[i+1]; }
|
||||
np = 15;
|
||||
cut = true;
|
||||
}
|
||||
parts[np] = s; lens[np] = p - s; np++;
|
||||
IF (!DSBYTE[p]) BREAK;
|
||||
@@ -1761,7 +1861,7 @@ bool css_match(dword _idx, _sel)
|
||||
IF (DSBYTE[p] == '>') || (DSBYTE[p] == 0x2B) || (DSBYTE[p] == 0x7E) {
|
||||
comb = DSBYTE[p];
|
||||
i--;
|
||||
IF (i < 0) return false;
|
||||
IF (i < 0) return cut;
|
||||
}
|
||||
}
|
||||
IF (comb == 0x2B) {
|
||||
@@ -1886,10 +1986,20 @@ dword css_rgb_first(dword _v)
|
||||
RETURN css_skip_ws(p + 1);
|
||||
}
|
||||
|
||||
// the alpha and the own RGB of the color css_color() read last: 255 for an opaque one
|
||||
int css_alpha_last;
|
||||
dword css_rgb_last;
|
||||
|
||||
// _r, _g, _b with the alpha _a (1..255) as it looks over the backdrop, as 0xRRGGBB
|
||||
dword css_blend_rgb(dword _r, _g, _b, _a)
|
||||
{
|
||||
dword br, bg, bb, c;
|
||||
css_alpha_last = _a;
|
||||
c = _r << 16;
|
||||
br = _g << 8;
|
||||
c += br;
|
||||
c += _b;
|
||||
css_rgb_last = c;
|
||||
IF (_a < 255) {
|
||||
br = css_blend_base >> 16;
|
||||
br &= 0xFF;
|
||||
@@ -1912,6 +2022,7 @@ dword css_color(dword _v)
|
||||
char tmp[32];
|
||||
dword r, g, b, p, c;
|
||||
int ii, a;
|
||||
css_alpha_last = 255;
|
||||
if (!strncmp(_v, "rgb", 3)) {
|
||||
// "rgb(1, 2, 3)" or the modern "rgb(1 2 3 / 50%)" (lobste.rs)
|
||||
p = css_rgb_first(_v);
|
||||
@@ -2011,7 +2122,7 @@ signed css_em_tenths(dword _v)
|
||||
}
|
||||
|
||||
// "12px" -> 12, "50%" -> -50, "2em" -> 2*CSS_EM_PX, other -> 0.
|
||||
// "20vw" is a share of the window width (css_vw). Heights in vh have no equivalent.
|
||||
// "20vw" is a share of the window width (css_vw), "5vh" of its height (css_vh).
|
||||
// A number with no unit is not a length: browsers drop such a declaration. A
|
||||
// Neocities page with ".buttons img { width: 88; height: 31 }" would otherwise
|
||||
// stretch every picture it reaches to 88x31 (cobradile.neocities.org).
|
||||
@@ -2044,6 +2155,19 @@ signed css_vw(signed _n)
|
||||
RETURN v;
|
||||
}
|
||||
|
||||
// "5vh": a share of the window height (list.h), restyled when the height changes as for
|
||||
// height queries. 0 while the height is not known. The bar of virtualbox.org is "height:
|
||||
// 5vh", its logo "height: 100%" of that: without it the 2208x552 logo filled the window.
|
||||
signed css_vh(signed _n)
|
||||
{
|
||||
int v;
|
||||
IF (css_view_h <= 0) RETURN 0;
|
||||
css_media_h = true;
|
||||
v = _n * css_view_h;
|
||||
v /= 100;
|
||||
RETURN v;
|
||||
}
|
||||
|
||||
// the hundredths of a percentage that css_length() drops: "16.66%" -> 66, "12.5%" -> 50,
|
||||
// anything else 0 (S_WFRAC: layout.h width_px). Six columns of 16.66% were 16% each and
|
||||
// lost 4% of the row; a strip of six slides drifted 4% more per slide.
|
||||
@@ -2071,7 +2195,8 @@ signed css_length(dword _v)
|
||||
IF (css_unitless(_v)) return 0; // "88" instead of "88px": no length at all
|
||||
IF (strstr(_v, "%")) return -n;
|
||||
IF (strstr(_v, "vw")) return css_vw(n);
|
||||
IF (strstr(_v, "vh")) || (strstr(_v, "vmin")) || (strstr(_v, "vmax")) RETURN 0;
|
||||
IF (strstr(_v, "vmin")) || (strstr(_v, "vmax")) RETURN 0;
|
||||
IF (strstr(_v, "vh")) return css_vh(n);
|
||||
IF (strstr(_v, "em")) RETURN css_em(_v);
|
||||
// "60ch", "2ex": a glyph width / x-height, roughly half an em
|
||||
IF (strstr(_v, "ch")) || (strstr(_v, "ex")) {
|
||||
@@ -2479,11 +2604,15 @@ signed css_grid_track(dword _t)
|
||||
dword p;
|
||||
int n;
|
||||
IF (!strncmp(_t, "minmax", 6)) {
|
||||
// the upper bound decides: "minmax(0, 1fr)" is a share, "minmax(100px, auto)" content
|
||||
// the upper bound decides: "minmax(0, 1fr)" is a share, "minmax(100px, auto)" content,
|
||||
// "minmax(0, 96rem)" at most 96rem (tailwindcss.com: the page column between two
|
||||
// gutters, wider than the window as a plain 96rem)
|
||||
p = strchr(_t, ',');
|
||||
IF (!p) return 0;
|
||||
p = css_skip_sp(p + 1);
|
||||
RETURN css_grid_track(p);
|
||||
n = css_grid_track(p);
|
||||
IF (n > 0) && (n < GT_CAP) RETURN GT_CAP + n;
|
||||
RETURN n;
|
||||
}
|
||||
IF (strstr(_t, "fr")) {
|
||||
n = css_flex_num(_t);
|
||||
@@ -2617,6 +2746,17 @@ int css_grid_span(dword _v)
|
||||
RETURN b;
|
||||
}
|
||||
|
||||
// the line an item starts at: "2" of grid-column-start, "2 / 4" or "1 / -1" of
|
||||
// grid-column; 0 (auto placement) for "auto", "span 2" and a line counted from the end
|
||||
int css_grid_line(dword _v)
|
||||
{
|
||||
int a;
|
||||
IF (DSBYTE[_v] < '1') || (DSBYTE[_v] > '9') RETURN 0;
|
||||
a = atoi(_v);
|
||||
IF (a > GRID_TRACKS) RETURN 0;
|
||||
RETURN a;
|
||||
}
|
||||
|
||||
// grid-template: "min-content 1fr / 12.25rem minmax(0,1fr)" - rows / columns; only
|
||||
// the columns are used
|
||||
// grid-template-areas as the page wrote them ('"logo about" "copyright copyright"'),
|
||||
@@ -2988,7 +3128,7 @@ void css_bg_offsets(dword _node, _val)
|
||||
char w[32];
|
||||
dword p;
|
||||
int n, k, v, pc;
|
||||
bool one;
|
||||
bool one, open, shut;
|
||||
DSDWORD[_node + S_BGX] = 0;
|
||||
DSDWORD[_node + S_BGY] = 0;
|
||||
css_bg_ppos = 0;
|
||||
@@ -2998,12 +3138,23 @@ void css_bg_offsets(dword _node, _val)
|
||||
while (DSBYTE[p]) && (k < 2) {
|
||||
p = css_skip_sp(p);
|
||||
n = 0;
|
||||
WHILE (DSBYTE[p]) && (DSBYTE[p] != ' ') { IF (n < 31) { w[n] = DSBYTE[p]; n++; } p++; }
|
||||
open = false;
|
||||
shut = false;
|
||||
// the brackets of the whole word: a long one is cut in w, and the ")" of an
|
||||
// absolute "url(https://host/i/icon-sprite.png)" (the urls of a stylesheet file
|
||||
// are made absolute) was cut off with it, so the position after it was skipped
|
||||
// as the rest of a function (korrespondent.net: every sprite icon at 0 0)
|
||||
WHILE (DSBYTE[p]) && (DSBYTE[p] != ' ') {
|
||||
IF (DSBYTE[p] == 0x28) open = true;
|
||||
IF (DSBYTE[p] == 0x29) shut = true;
|
||||
IF (n < 31) { w[n] = DSBYTE[p]; n++; }
|
||||
p++;
|
||||
}
|
||||
w[n] = 0;
|
||||
IF (!n) break;
|
||||
IF (w[0] == '/') break; // "center / contain": the size of the shorthand
|
||||
// "rgb(255, 255, 255)": the rest of a function with spaces is not a position
|
||||
IF (strchr(#w, '(')) && (!strchr(#w, ')')) {
|
||||
IF (open) && (!shut) {
|
||||
WHILE (DSBYTE[p]) && (DSBYTE[p] != ')') p++;
|
||||
IF (DSBYTE[p]) p++;
|
||||
continue;
|
||||
@@ -3232,19 +3383,85 @@ dword css_calc_word(dword _p)
|
||||
RETURN _p;
|
||||
}
|
||||
|
||||
// an unsigned number in thousandths: "0.25" -> 250, ".875" -> 875, "3" -> 3000
|
||||
int css_milli(dword _p)
|
||||
{
|
||||
int n, k, d;
|
||||
dword p;
|
||||
p = _p;
|
||||
n = atoi(p);
|
||||
n *= 1000;
|
||||
WHILE (DSBYTE[p] >= '0') && (DSBYTE[p] <= '9') p++;
|
||||
IF (DSBYTE[p] != '.') RETURN n;
|
||||
p++;
|
||||
k = 100;
|
||||
WHILE (k) && (DSBYTE[p] >= '0') && (DSBYTE[p] <= '9') {
|
||||
d = DSBYTE[p];
|
||||
d -= '0';
|
||||
d *= k;
|
||||
n += d;
|
||||
k /= 10;
|
||||
p++;
|
||||
}
|
||||
RETURN n;
|
||||
}
|
||||
|
||||
// a length term of calc() in hundredths of px, unsigned: an em, rem or px keeps its
|
||||
// fraction. Tailwind v4 builds every size as "calc(var(--spacing) * 6)" with
|
||||
// --spacing .25rem: in whole px that was 2 * 6 = 12px for 21px, all its paddings,
|
||||
// margins and gaps came out little more than half as big.
|
||||
int css_calc_len_abs(dword _t)
|
||||
{
|
||||
int n, b;
|
||||
IF (strstr(_t, "em")) {
|
||||
IF (strstr(_t, "rem")) b = CSS_EM_PX * 10; ELSE b = css_em10;
|
||||
n = css_milli(_t);
|
||||
n *= b;
|
||||
n /= 100;
|
||||
RETURN n;
|
||||
}
|
||||
IF (strstr(_t, "px")) {
|
||||
n = css_milli(_t);
|
||||
n /= 10;
|
||||
RETURN n;
|
||||
}
|
||||
n = css_length(_t);
|
||||
n *= 100;
|
||||
RETURN n;
|
||||
}
|
||||
|
||||
// a term of calc() without its sign: a factor ("1.5" in thousandths) or a length in
|
||||
// hundredths of px, calc_term_den set to match
|
||||
int css_calc_abs(dword _p)
|
||||
{
|
||||
if (css_unitless(_p)) {
|
||||
calc_term_den = 1;
|
||||
IF (!strchr(_p, '.')) RETURN atoi(_p);
|
||||
calc_term_den = 1000;
|
||||
RETURN css_milli(_p);
|
||||
}
|
||||
calc_term_den = 100;
|
||||
RETURN css_calc_len_abs(_p);
|
||||
}
|
||||
|
||||
// a term in px, a plain number as it is (a factor), a percentage as its number with
|
||||
// calc_term_pct set; CALC_BAD for a word (min(, var(, env()
|
||||
// calc_term_pct set; CALC_BAD for a word (min(, var(, env(). A length comes in
|
||||
// hundredths (calc_term_den 100), summed as px when its product is closed.
|
||||
signed css_calc_value(dword _t)
|
||||
{
|
||||
dword c;
|
||||
dword c, p;
|
||||
int n;
|
||||
calc_term_pct = false;
|
||||
calc_term_den = 1;
|
||||
c = DSBYTE[_t];
|
||||
IF (!c) RETURN CALC_BAD;
|
||||
IF (c >= 'a') && (c <= 'z') RETURN CALC_BAD;
|
||||
IF (strchr(_t, '%')) { calc_term_pct = true; RETURN atoi(_t); }
|
||||
IF (css_unitless(_t)) RETURN atoi(_t);
|
||||
RETURN css_length(_t);
|
||||
// the sign apart, as css_em() does (a stale pointer kept the minus)
|
||||
IF (c != '-') RETURN css_calc_abs(_t);
|
||||
p = _t + 1;
|
||||
n = css_calc_abs(p);
|
||||
RETURN 0 - n;
|
||||
}
|
||||
|
||||
// a fraction that grows too big for 32 bits is divided out (precision goes, not the
|
||||
@@ -3639,7 +3856,7 @@ void css_overflow_x(dword _node, _val)
|
||||
|
||||
// The properties css_apply_decl() knows: it looks the name up once, CP_* is its place
|
||||
// in the list. Looked up by css_hash(), then compared: faster than a strcmp per name.
|
||||
char css_prop_names[] = "background|background-image|background-repeat|background-position|background-size|color|background-color|font-weight|font-style|text-decoration|text-decoration-line|text-align|vertical-align|width|clear|float|height|min-height|max-height|display|grid-template-columns|grid-template-areas|grid-column|grid-template|flex-direction|flex-wrap|flex-flow|justify-content|align-items|flex-grow|flex-basis|flex|grid-template-rows|order|appearance|-webkit-appearance|-moz-appearance|gap|column-gap|grid-gap|grid-column-gap|position|top|left|right|bottom|inset-block-start|inset-inline-start|inset-inline-end|inset-block-end|inset|inset-inline|inset-block|z-index|transform|translate|opacity|visibility|-wv-clearfix|overflow|overflow-y|overflow-x|overflow-wrap|word-wrap|word-break|white-space|line-height|font-size|margin-top|margin-bottom|margin-left|margin-right|max-width|min-width|box-sizing|margin|padding|padding-top|padding-right|padding-bottom|padding-left|border|border-width|border-color|border-style|border-top|border-right|border-bottom|border-left|border-top-width|border-right-width|border-bottom-width|border-left-width|border-top-color|border-right-color|border-bottom-color|border-left-color|list-style|list-style-type|clip|aspect-ratio|object-position|text-indent|grid-area|flex-shrink|mask-image|-webkit-mask-image";
|
||||
char css_prop_names[] = "background|background-image|background-repeat|background-position|background-size|color|background-color|font-weight|font-style|text-decoration|text-decoration-line|text-align|vertical-align|width|clear|float|height|min-height|max-height|display|grid-template-columns|grid-template-areas|grid-column|grid-template|flex-direction|flex-wrap|flex-flow|justify-content|align-items|flex-grow|flex-basis|flex|grid-template-rows|order|appearance|-webkit-appearance|-moz-appearance|gap|column-gap|grid-gap|grid-column-gap|position|top|left|right|bottom|inset-block-start|inset-inline-start|inset-inline-end|inset-block-end|inset|inset-inline|inset-block|z-index|transform|translate|opacity|visibility|-wv-clearfix|overflow|overflow-y|overflow-x|overflow-wrap|word-wrap|word-break|white-space|line-height|font-size|margin-top|margin-bottom|margin-left|margin-right|max-width|min-width|box-sizing|margin|padding|padding-top|padding-right|padding-bottom|padding-left|border|border-width|border-color|border-style|border-top|border-right|border-bottom|border-left|border-top-width|border-right-width|border-bottom-width|border-left-width|border-top-color|border-right-color|border-bottom-color|border-left-color|list-style|list-style-type|clip|aspect-ratio|object-position|text-indent|grid-area|flex-shrink|mask-image|-webkit-mask-image|outline|outline-width|outline-color|outline-style|outline-offset|grid-column-start";
|
||||
#define CP_BACKGROUND 1
|
||||
#define CP_BACKGROUND_IMAGE 2
|
||||
#define CP_BACKGROUND_REPEAT 3
|
||||
@@ -3749,7 +3966,13 @@ char css_prop_names[] = "background|background-image|background-repeat|backgroun
|
||||
#define CP_FLEX_SHRINK 105
|
||||
#define CP_MASK_IMAGE 106
|
||||
#define CP_WEBKIT_MASK_IMAGE 107
|
||||
#define CP_N 107
|
||||
#define CP_OUTLINE 108
|
||||
#define CP_OUTLINE_WIDTH 109
|
||||
#define CP_OUTLINE_COLOR 110
|
||||
#define CP_OUTLINE_STYLE 111
|
||||
#define CP_OUTLINE_OFFSET 112
|
||||
#define CP_GRID_COLUMN_START 113
|
||||
#define CP_N 113
|
||||
dword css_prop_h[CP_N + 1]; // css_hash() of each name of the list, filled on the first call
|
||||
dword css_prop_at[CP_N + 1]; // where the name starts in css_prop_names
|
||||
bool css_prop_ready;
|
||||
@@ -4054,6 +4277,9 @@ void css_apply_decl(dword _idx, _prop, _val)
|
||||
IF (!css_var_expand(_val, #resolved, sizeof(resolved))) return;
|
||||
_val = #resolved;
|
||||
}
|
||||
// a value we cannot read is dropped as by an old browser, the one before it stays:
|
||||
// "background: #ccc; background: color-mix(in oklab, ...)" keeps #ccc
|
||||
IF (css_unknown_color(_val, 0)) return;
|
||||
IF (css_ps) {
|
||||
strlwr(_val);
|
||||
css_pseudo_decl(node, _prop, pid, _val);
|
||||
@@ -4088,6 +4314,15 @@ void css_apply_decl(dword _idx, _prop, _val)
|
||||
{
|
||||
// background: may hold "url(...) #fff repeat": the first word that is a color
|
||||
DSDWORD[node + S_BG] = css_bg_color(_val);
|
||||
// a translucent one keeps its alpha: a positioned box paints it over what is
|
||||
// under it (paint.h cv_blend_bar), the "Plus" link of tailwindcss.com under
|
||||
// its 10% sky tint, not hidden by the tint blended over white
|
||||
DSDWORD[node + S_BGA] = 0;
|
||||
IF (DSDWORD[node + S_BG] != NO_COLOR) && (css_alpha_last < 255) && (!strstr(_val, "gradient(")) {
|
||||
c = css_alpha_last << 24;
|
||||
c |= css_rgb_last;
|
||||
DSDWORD[node + S_BGA] = c;
|
||||
}
|
||||
}
|
||||
break;
|
||||
CASE CP_BACKGROUND_IMAGE:
|
||||
@@ -4095,7 +4330,8 @@ void css_apply_decl(dword _idx, _prop, _val)
|
||||
// a gradient over the background color shows its own color instead
|
||||
IF (strstr(_val, "gradient(")) {
|
||||
c = css_bg_color(_val);
|
||||
IF (c != NO_COLOR) DSDWORD[node + S_BG] = c;
|
||||
// opaque: an rgba() of background-color before it is not blended
|
||||
IF (c != NO_COLOR) { DSDWORD[node + S_BG] = c; DSDWORD[node + S_BGA] = 0; }
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -4114,6 +4350,19 @@ void css_apply_decl(dword _idx, _prop, _val)
|
||||
IF (strstr(_val, "underline")) DSDWORD[node + S_FLAGS] |= SF_UNDER;
|
||||
IF (strstr(_val, "line-through")) DSDWORD[node + S_FLAGS] |= SF_STRIKE;
|
||||
IF (streq(_val, "none")) sf_clear(node, SF_UNDER);
|
||||
// "a { text-decoration: inherit }" of Tailwind's reset: a link is underlined
|
||||
// only when the text around it is (the menu of tailwindcss.com was)
|
||||
if (streq(_val, "inherit")) {
|
||||
sf_clear(node, SF_UNDER);
|
||||
sf_clear(node, SF_STRIKE);
|
||||
c = dom_nparent(_idx);
|
||||
IF (c) {
|
||||
c = dom_n(c);
|
||||
c = DSDWORD[c + S_FLAGS];
|
||||
c &= SF_UNDER | SF_STRIKE;
|
||||
DSDWORD[node + S_FLAGS] |= c;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
// the places in the lists are ALIGN_* and VALIGN_* + 1
|
||||
@@ -4129,6 +4378,8 @@ void css_apply_decl(dword _idx, _prop, _val)
|
||||
// baseline undoes the super / sub a <sup> passes down
|
||||
IF (c == 6) c = 1;
|
||||
IF (c) DSDWORD[node + S_VALIGN] = c - 1;
|
||||
sf_clear(node, SF_VMID);
|
||||
IF (c == 2) DSDWORD[node + S_FLAGS] |= SF_VMID;
|
||||
}
|
||||
break;
|
||||
CASE CP_WIDTH:
|
||||
@@ -4247,6 +4498,10 @@ void css_apply_decl(dword _idx, _prop, _val)
|
||||
break;
|
||||
CASE CP_GRID_COLUMN:
|
||||
DSDWORD[node + S_COLSPAN] = css_grid_span(_val);
|
||||
DSDWORD[node + S_GCOL] = css_grid_line(_val);
|
||||
break;
|
||||
CASE CP_GRID_COLUMN_START:
|
||||
DSDWORD[node + S_GCOL] = css_grid_line(_val);
|
||||
break;
|
||||
CASE CP_GRID_TEMPLATE:
|
||||
css_grid_template(node, _val);
|
||||
@@ -4555,6 +4810,41 @@ void css_apply_decl(dword _idx, _prop, _val)
|
||||
}
|
||||
}
|
||||
break;
|
||||
// outline: a frame drawn outside the box, over what is around, taking no room.
|
||||
// Solid whatever its style; a focus ring (":focus") never gets here
|
||||
CASE CP_OUTLINE:
|
||||
CASE CP_OUTLINE_WIDTH:
|
||||
{
|
||||
len = css_border_width(_val);
|
||||
IF (len >= 0) DSDWORD[node + S_OUTW] = math.min(len, 8);
|
||||
IF (pid == CP_OUTLINE) {
|
||||
c = css_border_color(_val);
|
||||
IF (c != NO_COLOR) DSDWORD[node + S_OUTC] = c;
|
||||
}
|
||||
}
|
||||
break;
|
||||
CASE CP_OUTLINE_COLOR:
|
||||
{
|
||||
c = css_border_color(_val);
|
||||
IF (c != NO_COLOR) DSDWORD[node + S_OUTC] = c;
|
||||
}
|
||||
break;
|
||||
CASE CP_OUTLINE_STYLE:
|
||||
{
|
||||
IF (strstr(_val, "none")) || (strstr(_val, "hidden")) DSDWORD[node + S_OUTW] = 0;
|
||||
ELSE IF (!DSDWORD[node + S_OUTW]) DSDWORD[node + S_OUTW] = 1;
|
||||
}
|
||||
break;
|
||||
CASE CP_OUTLINE_OFFSET:
|
||||
{
|
||||
IF (!strchr(_val, '%')) {
|
||||
len = css_length(_val);
|
||||
IF (len > 16) len = 16;
|
||||
IF (len < -16) len = -16;
|
||||
DSDWORD[node + S_OUTOFF] = len;
|
||||
}
|
||||
}
|
||||
break;
|
||||
CASE CP_BORDER_COLOR:
|
||||
{
|
||||
c = css_border_color(_val);
|
||||
@@ -5240,7 +5530,20 @@ bool is_push_button(dword _idx, _t)
|
||||
// TYPE=SUBMIT: form.h lowercases it too
|
||||
strlcpy(#t, v, 15);
|
||||
strlwr(#t);
|
||||
IF (css_kw(#t, "submit|button|reset|image")) RETURN true;
|
||||
IF (css_kw(#t, "submit|button|reset")) RETURN true;
|
||||
RETURN false;
|
||||
}
|
||||
|
||||
// <input type=image>: a picture that submits its form, drawn like an <img>
|
||||
bool is_image_input(dword _idx)
|
||||
{
|
||||
dword v;
|
||||
char t[16];
|
||||
v = dom_attr(_idx, "type");
|
||||
IF (!v) RETURN false;
|
||||
strlcpy(#t, v, 15);
|
||||
strlwr(#t);
|
||||
IF (streq(#t, "image")) RETURN true;
|
||||
RETURN false;
|
||||
}
|
||||
|
||||
@@ -5444,6 +5747,11 @@ void style_compute(dword _idx, _parent)
|
||||
DSDWORD[node + S_MRPCT] = 0;
|
||||
DSDWORD[node + S_AREAS] = 0;
|
||||
DSDWORD[node + S_GAREA] = 0;
|
||||
DSDWORD[node + S_OUTW] = 0;
|
||||
DSDWORD[node + S_OUTC] = NO_COLOR;
|
||||
DSDWORD[node + S_OUTOFF] = 0;
|
||||
DSDWORD[node + S_GCOL] = 0;
|
||||
DSDWORD[node + S_BGA] = 0;
|
||||
|
||||
// inherited
|
||||
DSDWORD[node + S_COLOR] = DSDWORD[pn + S_COLOR];
|
||||
@@ -5614,6 +5922,7 @@ void style_compute(dword _idx, _parent)
|
||||
DSDWORD[node + S_FLAGS] |= SF_BUTTON;
|
||||
v = dom_attr(_idx, "type");
|
||||
IF (v) && (streq(v, "hidden")) DSDWORD[node + S_DISPLAY] = D_NONE;
|
||||
IF (t == T_INPUT) && (is_image_input(_idx)) sf_clear(node, SF_BUTTON);
|
||||
break;
|
||||
case T_OPTION:
|
||||
DSDWORD[node + S_DISPLAY] = D_NONE;
|
||||
@@ -6067,6 +6376,8 @@ void style_tree()
|
||||
DSDWORD[node + S_DISPLAY] = D_BLOCK;
|
||||
text_all = 0;
|
||||
text_tiny = 0;
|
||||
text_big = 0;
|
||||
text_big_clear();
|
||||
css_areas_used = 0;
|
||||
c = dom_nfirst(dom.root);
|
||||
WHILE (c) {
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
<pre>By the way,
|
||||
<font color="#555555">• You can check for browser updates from the main menu.
|
||||
<font color="#555">• You can check for browser updates from the main menu.
|
||||
• Pressing F6 moves the text cursor to the omnibox.
|
||||
• You can manually change the encoding of a page by clicking on the label in the bottom right corner.
|
||||
• Need better web support? Use Netsurf or WebKit.
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<font bg=#F8F15B> web <font bg=#FF5A7E color=#fff> 2.0 <font bg=#47D018> compatible
|
||||
<font bg=#3CE7FF> </font></font></font></font>
|
||||
|
||||
<kosicon n=6><a href="//ftp.kolibrios.org/users/Leency/test_files/showroom.htm">Escaparate</a>
|
||||
<kosicon n=6><a href=//ftp.kolibrios.org/users/Leency/test_files/showroom.htm>Escaparate</a>
|
||||
<kosicon n=73><a href=//builds.kolibrios.org/ci/latest/>Compilaciones nocturnas</a>
|
||||
|
||||
<td><pre>
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
<pre>Por cierto,
|
||||
<font color="#555555">• Puede buscar actualizaciones del navegador desde el menu principal.
|
||||
<font color="#555">• Puede buscar actualizaciones del navegador desde el menu principal.
|
||||
• La tecla F6 mueve el cursor de texto a la barra de direcciones.
|
||||
• La codificacion de la pagina se cambia pulsando la etiqueta de la esquina inferior derecha.
|
||||
• Necesita mejor soporte web? Use Netsurf o WebKit.
|
||||
@@ -40,7 +40,7 @@
|
||||
' |
|
||||
() .-.,="``"=. - o -
|
||||
'=/_ \ |
|
||||
* | '=.<a href=//ftp.kolibrios.org/users/Leency/test_files/links.htm> </a> |
|
||||
* | '=._ |
|
||||
\ `=./`, '
|
||||
. '=.__.=' `=' *
|
||||
+ +
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
Кстати,
|
||||
<font color="#555555">• Из главного меню можно проверить наличие обновлений.
|
||||
<font color="#555">• Из главного меню можно проверить наличие обновлений.
|
||||
• Клавиша F6 перемещает текстовый курсор в омнибокс.
|
||||
• Кодировку страницы можно изменить в правом нижнем углу окна.
|
||||
• Нужна лучшая поддержка вэба? Используйте Netsurf или WebKit.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<title>Page not found</title>
|
||||
<body bgcolor=#ccc>
|
||||
<br>
|
||||
<table border=2 cellpadding=20 width=80% align=center bgcolor=#fff><tr><td>
|
||||
<table border=2 cellpadding=20 width=80% align=center bgcolor=#fff>
|
||||
<h3><kosicon n=67> Webpage Is Not Available</h3>
|
||||
<pre>
|
||||
If there is no Internet connection, the easiest way to get one is to plug a phone into this PC/laptop over a USB cable and enable USB tethering (modem mode) on the phone.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<title>Pagina no encontrada</title>
|
||||
<body bgcolor=#ccc>
|
||||
<br>
|
||||
<table border=2 cellpadding=20 width=80% align=center bgcolor=#fff><tr><td>
|
||||
<table border=2 cellpadding=20 width=80% align=center bgcolor=#fff>
|
||||
<h3><kosicon n=67> La pagina no se puede mostrar</h3>
|
||||
<pre>
|
||||
Si no hay conexion a Internet, puede conectar un telefono a este PC por USB y activar en el telefono el anclaje a red (modo modem).
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<html><head><title>gridfit</title>
|
||||
<!-- needs a picture at /hd0/1/pic.png (like noscript.htm) -->
|
||||
<style>
|
||||
body { margin:0; background:#eee; }
|
||||
/* virtualbox.org: a "5vh" bar, the logo "height: 100%" of it, an icon picked by a
|
||||
selector of four child combinators ("height: 2vw") */
|
||||
#bar { height:5vh; display:flex; align-items:center; justify-content:space-between; background:#fff; border-bottom:1px solid #bbb; }
|
||||
#bar li { height:100%; list-style-type:none; margin:0 1vw; }
|
||||
#bar > ul { display:flex; align-items:center; margin:0 4vw 0 0; padding:0; }
|
||||
#logo, #logo img { display:block; height:100%; width:auto; }
|
||||
#bar > ul > li.first > a > img { height:2vw; }
|
||||
/* theguardian.com: six 280px cards in "repeat(6, 1fr)" of a narrower row */
|
||||
.wrap { max-width:740px; margin:12px auto; padding:8px 20px; background:#fff; }
|
||||
.car { display:grid; gap:20px; padding:0; margin:0; list-style:none; grid-auto-flow:column; overflow-x:auto; grid-template-columns:repeat(6, 1fr); }
|
||||
.card { display:flex; flex-direction:row; justify-content:space-between; column-gap:8px; width:280px; padding:10px 10px 0 10px; background:#fdecea; }
|
||||
.txt { display:flex; flex-direction:column; padding-bottom:10px; }
|
||||
.hl { font-size:17px; margin:0; font-weight:normal; }
|
||||
.media { flex-shrink:0; width:60px; height:60px; margin-bottom:10px; background:#c77a3c; }
|
||||
</style></head><body>
|
||||
<div id="bar"><li><a id="logo" href="#"><img alt="logo" src="/hd0/1/pic.png"></a></li>
|
||||
<ul><li><a href="#">Home</a></li><li><a href="#">Download</a></li><li><a href="#">Documentation</a></li>
|
||||
<li class="first"><a href="#"><img alt="user" src="/hd0/1/pic.png"></a></li></ul></div>
|
||||
<p>The bar above is one line: a short blue logo, the links, a small icon.</p>
|
||||
<div class="wrap"><ul class="car">
|
||||
<li><div style="display:contents"><div class="card"><div class="txt"><h3 class="hl">One: the headline reads across the card</h3></div><div class="media"></div></div></div></li>
|
||||
<li><div style="display:contents"><div class="card"><div class="txt"><h3 class="hl">Two: the headline reads across the card</h3></div><div class="media"></div></div></div></li>
|
||||
<li><div style="display:contents"><div class="card"><div class="txt"><h3 class="hl">Three: the headline reads across the card</h3></div><div class="media"></div></div></div></li>
|
||||
<li><div style="display:contents"><div class="card"><div class="txt"><h3 class="hl">Four: the headline reads across the card</h3></div><div class="media"></div></div></div></li>
|
||||
<li><div style="display:contents"><div class="card"><div class="txt"><h3 class="hl">Five: the headline reads across the card</h3></div><div class="media"></div></div></div></li>
|
||||
<li><div style="display:contents"><div class="card"><div class="txt"><h3 class="hl">Six: the headline reads across the card</h3></div><div class="media"></div></div></div></li>
|
||||
</ul></div>
|
||||
<p>Cards above: as many 280px columns as fit (two in a 740px row), not six letter columns.</p>
|
||||
</body></html>
|
||||
+200
-47
@@ -325,68 +325,221 @@ void _http::receive()
|
||||
ESBYTE[d] = 0;
|
||||
}
|
||||
|
||||
:int check_is_the_url_absolute(dword _in)
|
||||
:bool url_is_alpha(dword _c)
|
||||
{
|
||||
if(!strncmp(_in,"file:",5)) return true;
|
||||
if(!strncmp(_in,"ftp:",4)) return true;
|
||||
if(!strncmp(_in,"http:",5)) return true;
|
||||
if(!strncmp(_in,"https:",6)) return true;
|
||||
if(!strncmp(_in,"mailto:",7)) return true;
|
||||
if(!strncmp(_in,"tel:",4)) return true;
|
||||
if(!strncmp(_in,"#",1)) return true;
|
||||
if(!strncmp(_in,"WebView:",8)) return true;
|
||||
if(check_is_the_adress_local(_in)) return true;
|
||||
if (_c >= 'a') && (_c <= 'z') return true;
|
||||
if (_c >= 'A') && (_c <= 'Z') return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// the length of "scheme:" at the start of _u (RFC 3986: a letter, then letters,
|
||||
// digits, "+", "-" or "."), 0 when there is none. Any case: "HTTPS://" is absolute too
|
||||
:dword url_scheme_len(dword _u)
|
||||
{
|
||||
dword p, c;
|
||||
if (!url_is_alpha(ESBYTE[_u])) return 0;
|
||||
p = _u + 1;
|
||||
loop() {
|
||||
c = ESBYTE[p];
|
||||
if (c == ':') break;
|
||||
if (!url_is_alpha(c)) {
|
||||
if (c < '0') && (c != '+') && (c != '-') && (c != '.') return 0;
|
||||
if (c > '9') return 0;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
p++;
|
||||
return p - _u;
|
||||
}
|
||||
|
||||
// the length of "scheme:" of an address on the net ("scheme://host"), 0 for any other
|
||||
:dword url_net_scheme_len(dword _u)
|
||||
{
|
||||
dword i, p;
|
||||
i = url_scheme_len(_u);
|
||||
if (!i) return 0;
|
||||
p = _u + i;
|
||||
if (ESBYTE[p] != '/') return 0;
|
||||
if (ESBYTE[p+1] != '/') return 0;
|
||||
return i;
|
||||
}
|
||||
|
||||
// where the path of _u starts: after "scheme://host:port", after "scheme:" for one
|
||||
// without a host (mailto:), 0 for a path of its own ("/hd0/1/x.htm")
|
||||
:dword url_path_start(dword _u)
|
||||
{
|
||||
dword p, c;
|
||||
p = url_scheme_len(_u);
|
||||
if (!p) return 0;
|
||||
p += _u;
|
||||
if (ESBYTE[p] != '/') return p - _u;
|
||||
if (ESBYTE[p+1] != '/') return p - _u;
|
||||
p += 2;
|
||||
loop() {
|
||||
c = ESBYTE[p];
|
||||
if (!c) break;
|
||||
if (c == '/') || (c == '?') || (c == '#') break;
|
||||
p++;
|
||||
}
|
||||
return p - _u;
|
||||
}
|
||||
|
||||
// cut the query and the fragment off _u, looking from offset _from on
|
||||
:void url_cut_query(dword _u, _from)
|
||||
{
|
||||
dword p, c;
|
||||
p = _u + _from;
|
||||
loop() {
|
||||
c = ESBYTE[p];
|
||||
if (!c) return;
|
||||
if (c == '?') || (c == '#') { ESBYTE[p] = 0; return; }
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
// "&" -> "&" in place, in one pass
|
||||
:void url_unamp(dword _u)
|
||||
{
|
||||
dword s, d;
|
||||
if (!strstr(_u, "&")) return;
|
||||
s = _u;
|
||||
d = _u;
|
||||
while (ESBYTE[s]) {
|
||||
ESBYTE[d] = ESBYTE[s];
|
||||
d++;
|
||||
if (ESBYTE[s] == '&') && (ESBYTE[s+1] == 'a') && (ESBYTE[s+2] == 'm') && (ESBYTE[s+3] == 'p') && (ESBYTE[s+4] == ';') s += 5;
|
||||
else s++;
|
||||
}
|
||||
ESBYTE[d] = 0;
|
||||
}
|
||||
|
||||
// a path segment of _n chars at _s: 1 for ".", 2 for "..", 0 for a name
|
||||
:int url_dot_seg(dword _s, _n)
|
||||
{
|
||||
if (ESBYTE[_s] != '.') return 0;
|
||||
if (_n == 1) return 1;
|
||||
if (_n != 2) return 0;
|
||||
if (ESBYTE[_s+1] != '.') return 0;
|
||||
return 2;
|
||||
}
|
||||
|
||||
// the "." and ".." segments of the path of _u (from offset _from to the query) resolved
|
||||
// (RFC 3986 5.2.4): "a/./b" is "a/b", "a/b/../c" is "a/c", and ".." never climbs above
|
||||
// the root ("http://a.com/../x" is "http://a.com/x", not "http:/x")
|
||||
:void url_remove_dots(dword _u, _from)
|
||||
{
|
||||
char out[URL_SIZE+1];
|
||||
dword r, w, e, root, c, n;
|
||||
int dots;
|
||||
bool slash;
|
||||
// root: where ".." stops. Not "root = w" after "if (..) w++": c-- took the old w
|
||||
r = _u + _from;
|
||||
w = #out;
|
||||
root = #out;
|
||||
if (ESBYTE[r] == '/') {
|
||||
out[0] = '/';
|
||||
root = #out + 1;
|
||||
w = #out + 1;
|
||||
r = _u + _from + 1;
|
||||
}
|
||||
loop() {
|
||||
c = ESBYTE[r];
|
||||
if (!c) break;
|
||||
if (c == '?') || (c == '#') break;
|
||||
e = r;
|
||||
loop() {
|
||||
c = ESBYTE[e];
|
||||
if (!c) break;
|
||||
if (c == '/') || (c == '?') || (c == '#') break;
|
||||
e++;
|
||||
}
|
||||
n = e - r;
|
||||
slash = false;
|
||||
if (ESBYTE[e] == '/') slash = true;
|
||||
// a helper, not "if (n == 1) ... else if (n == 2)": c-- dropped the second test
|
||||
dots = url_dot_seg(r, n);
|
||||
if (dots == 1) {
|
||||
r = e;
|
||||
} else if (dots == 2) {
|
||||
// drop the segment written last, with its slash
|
||||
if (w > root) {
|
||||
w--;
|
||||
while (w > root) && (ESBYTE[w-1] != '/') w--;
|
||||
}
|
||||
r = e;
|
||||
} else {
|
||||
while (r < e) { ESBYTE[w] = ESBYTE[r]; w++; r++; }
|
||||
if (slash) { ESBYTE[w] = '/'; w++; }
|
||||
}
|
||||
if (slash) r++;
|
||||
}
|
||||
// the query and the fragment as they are
|
||||
while (ESBYTE[r]) { ESBYTE[w] = ESBYTE[r]; w++; r++; }
|
||||
ESBYTE[w] = 0;
|
||||
w = _u + _from;
|
||||
strcpy(w, #out);
|
||||
}
|
||||
|
||||
:int check_is_the_url_absolute(dword _in)
|
||||
{
|
||||
if (url_scheme_len(_in)) return true;
|
||||
if (ESBYTE[_in] == '#') return true;
|
||||
if (check_is_the_adress_local(_in)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// new_URL (a buffer of URL_SIZE+1 bytes) made absolute against base_URL, in place:
|
||||
// RFC 3986 5.2 for "//host/x", "/x", "?q" and "x", with the dot segments resolved.
|
||||
// A scheme ("HTTP:", "data:"), a local path ("/hd0/1/x") or "#frag" stays as it is.
|
||||
:dword get_absolute_url(dword new_URL, base_URL)
|
||||
{
|
||||
int i;
|
||||
dword orig_URL = new_URL;
|
||||
char newurl[URL_SIZE+1];
|
||||
strcpy(#newurl, base_URL);
|
||||
dword ps, i, c;
|
||||
|
||||
while (i=strstr(new_URL, "&")) strcpy(i+1, i+5);
|
||||
url_unamp(new_URL);
|
||||
strip_file_scheme(new_URL);
|
||||
url_escape_spaces(new_URL);
|
||||
|
||||
if (check_is_the_url_absolute(new_URL)) return new_URL;
|
||||
|
||||
switch(ESBYTE[new_URL]) {
|
||||
case '.':
|
||||
if (ESBYTE[new_URL+1] == '/') {
|
||||
new_URL+=2;
|
||||
}
|
||||
else if (ESBYTE[new_URL+1] == '.') && (ESBYTE[new_URL+2] == '/') {
|
||||
_GO_UP:
|
||||
new_URL+=3;
|
||||
newurl[strrchr(#newurl, '/')-1] = '\0';
|
||||
}
|
||||
goto _DEFAULT;
|
||||
case '?':
|
||||
strchr(#newurl+8, '?'); //returns EAX
|
||||
if (EAX) ESBYTE[EAX] = '\0';
|
||||
strchr(#newurl+8, '/'); //returns EAX
|
||||
if (!EAX) chrcat(#newurl, '/');
|
||||
break;
|
||||
case '/':
|
||||
new_URL++;
|
||||
if (ESBYTE[new_URL] == '/') {
|
||||
strcpy(#newurl, "http:/");
|
||||
break;
|
||||
}
|
||||
EAX = strchr(#newurl+8, '/');
|
||||
if (EAX) ESBYTE[EAX] = '\0';
|
||||
default:
|
||||
_DEFAULT:
|
||||
EAX = strrchr(#newurl, '/');
|
||||
if (newurl[EAX-2]!='/') newurl[EAX] = '\0';
|
||||
if (!strncmp(new_URL,"../",3)) goto _GO_UP;
|
||||
if (newurl[strlen(#newurl)-1]!='/') strncat(#newurl, "/", URL_SIZE);
|
||||
strncpy(#newurl, base_URL, URL_SIZE);
|
||||
ps = url_path_start(#newurl);
|
||||
if (!ESBYTE[new_URL]) {
|
||||
// href="": the page itself, without its fragment
|
||||
i = strchr(#newurl, '#');
|
||||
if (i) ESBYTE[i] = 0;
|
||||
strncpy(new_URL, #newurl, URL_SIZE);
|
||||
return new_URL;
|
||||
}
|
||||
url_cut_query(#newurl, ps);
|
||||
|
||||
c = ESBYTE[new_URL];
|
||||
if (c == '/') && (ESBYTE[new_URL+1] == '/') {
|
||||
// "//host/x": the scheme of a page on the net (https stays https). An internal
|
||||
// page ("WebView:home") or a local one has no host: http, as it always was
|
||||
i = url_net_scheme_len(#newurl);
|
||||
if (i) newurl[i] = 0;
|
||||
else strcpy(#newurl, "http:");
|
||||
} else if (c == '/') {
|
||||
newurl[ps] = 0;
|
||||
} else if (c == '?') {
|
||||
// "http://a.com" + "?q" is "http://a.com/?q"
|
||||
if (ps) && (!newurl[ps]) chrcat(#newurl, '/');
|
||||
} else {
|
||||
// the directory of the page: up to its last slash
|
||||
i = strlen(#newurl);
|
||||
while (i > ps) {
|
||||
if (newurl[i-1] == '/') break;
|
||||
i--;
|
||||
}
|
||||
newurl[i] = 0;
|
||||
if (i == ps) && (ps) chrcat(#newurl, '/');
|
||||
}
|
||||
strncat(#newurl, new_URL, URL_SIZE);
|
||||
strncpy(orig_URL, #newurl, URL_SIZE);
|
||||
return orig_URL;
|
||||
ps = url_path_start(#newurl);
|
||||
url_remove_dots(#newurl, ps);
|
||||
strncpy(new_URL, #newurl, URL_SIZE);
|
||||
return new_URL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user