@react-x11/components 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/dist/html/controls.d.ts +41 -5
  2. package/dist/html/controls.d.ts.map +1 -1
  3. package/dist/html/controls.js +85 -57
  4. package/dist/html/controls.js.map +1 -1
  5. package/dist/html/css/cascade.d.ts +134 -20
  6. package/dist/html/css/cascade.d.ts.map +1 -1
  7. package/dist/html/css/cascade.js +545 -126
  8. package/dist/html/css/cascade.js.map +1 -1
  9. package/dist/html/css/color.d.ts +18 -0
  10. package/dist/html/css/color.d.ts.map +1 -1
  11. package/dist/html/css/color.js +27 -0
  12. package/dist/html/css/color.js.map +1 -1
  13. package/dist/html/css/parse.d.ts +47 -6
  14. package/dist/html/css/parse.d.ts.map +1 -1
  15. package/dist/html/css/parse.js +336 -30
  16. package/dist/html/css/parse.js.map +1 -1
  17. package/dist/html/css/style.d.ts +142 -15
  18. package/dist/html/css/style.d.ts.map +1 -1
  19. package/dist/html/css/style.js +563 -147
  20. package/dist/html/css/style.js.map +1 -1
  21. package/dist/html/css/transform.d.ts +85 -0
  22. package/dist/html/css/transform.d.ts.map +1 -0
  23. package/dist/html/css/transform.js +425 -0
  24. package/dist/html/css/transform.js.map +1 -0
  25. package/dist/html/css/ua.d.ts +11 -1
  26. package/dist/html/css/ua.d.ts.map +1 -1
  27. package/dist/html/css/ua.js +73 -9
  28. package/dist/html/css/ua.js.map +1 -1
  29. package/dist/html/css/values.d.ts +4 -0
  30. package/dist/html/css/values.d.ts.map +1 -1
  31. package/dist/html/css/values.js.map +1 -1
  32. package/dist/html/dom.d.ts.map +1 -1
  33. package/dist/html/dom.js +3 -1
  34. package/dist/html/dom.js.map +1 -1
  35. package/dist/html/fonts.d.ts +78 -4
  36. package/dist/html/fonts.d.ts.map +1 -1
  37. package/dist/html/fonts.js +373 -19
  38. package/dist/html/fonts.js.map +1 -1
  39. package/dist/html/form.d.ts +200 -0
  40. package/dist/html/form.d.ts.map +1 -0
  41. package/dist/html/form.js +801 -0
  42. package/dist/html/form.js.map +1 -0
  43. package/dist/html/index.d.ts +12 -0
  44. package/dist/html/index.d.ts.map +1 -1
  45. package/dist/html/index.js +30 -301
  46. package/dist/html/index.js.map +1 -1
  47. package/dist/html/layout/axes.d.ts +14 -0
  48. package/dist/html/layout/axes.d.ts.map +1 -0
  49. package/dist/html/layout/axes.js +28 -0
  50. package/dist/html/layout/axes.js.map +1 -0
  51. package/dist/html/layout/block.d.ts +93 -12
  52. package/dist/html/layout/block.d.ts.map +1 -1
  53. package/dist/html/layout/block.js +393 -39
  54. package/dist/html/layout/block.js.map +1 -1
  55. package/dist/html/layout/boxes.d.ts +90 -2
  56. package/dist/html/layout/boxes.d.ts.map +1 -1
  57. package/dist/html/layout/boxes.js +74 -10
  58. package/dist/html/layout/boxes.js.map +1 -1
  59. package/dist/html/layout/cache.d.ts +1 -1
  60. package/dist/html/layout/cache.d.ts.map +1 -1
  61. package/dist/html/layout/cache.js +6 -0
  62. package/dist/html/layout/cache.js.map +1 -1
  63. package/dist/html/layout/css-grid.d.ts.map +1 -1
  64. package/dist/html/layout/css-grid.js +90 -18
  65. package/dist/html/layout/css-grid.js.map +1 -1
  66. package/dist/html/layout/flex.d.ts.map +1 -1
  67. package/dist/html/layout/flex.js +574 -84
  68. package/dist/html/layout/flex.js.map +1 -1
  69. package/dist/html/layout/inline.d.ts +3 -0
  70. package/dist/html/layout/inline.d.ts.map +1 -1
  71. package/dist/html/layout/inline.js +154 -54
  72. package/dist/html/layout/inline.js.map +1 -1
  73. package/dist/html/layout/multicol.d.ts +42 -0
  74. package/dist/html/layout/multicol.d.ts.map +1 -0
  75. package/dist/html/layout/multicol.js +897 -0
  76. package/dist/html/layout/multicol.js.map +1 -0
  77. package/dist/html/layout/shaping.d.ts +7 -0
  78. package/dist/html/layout/shaping.d.ts.map +1 -1
  79. package/dist/html/layout/shaping.js +25 -20
  80. package/dist/html/layout/shaping.js.map +1 -1
  81. package/dist/html/layout/table.d.ts.map +1 -1
  82. package/dist/html/layout/table.js +62 -18
  83. package/dist/html/layout/table.js.map +1 -1
  84. package/dist/html/node.d.ts +90 -15
  85. package/dist/html/node.d.ts.map +1 -1
  86. package/dist/html/node.js +771 -186
  87. package/dist/html/node.js.map +1 -1
  88. package/dist/html/paint.d.ts +101 -5
  89. package/dist/html/paint.d.ts.map +1 -1
  90. package/dist/html/paint.js +1502 -160
  91. package/dist/html/paint.js.map +1 -1
  92. package/dist/html/svg.d.ts +15 -2
  93. package/dist/html/svg.d.ts.map +1 -1
  94. package/dist/html/svg.js +249 -17
  95. package/dist/html/svg.js.map +1 -1
  96. package/dist/html/widgets.d.ts +29 -0
  97. package/dist/html/widgets.d.ts.map +1 -0
  98. package/dist/html/widgets.js +700 -0
  99. package/dist/html/widgets.js.map +1 -0
  100. package/dist/html/woff2.d.ts +14 -0
  101. package/dist/html/woff2.d.ts.map +1 -0
  102. package/dist/html/woff2.js +608 -0
  103. package/dist/html/woff2.js.map +1 -0
  104. package/dist/index.d.ts +1 -1
  105. package/dist/index.d.ts.map +1 -1
  106. package/dist/index.js.map +1 -1
  107. package/dist/maps/controller.d.ts +17 -1
  108. package/dist/maps/controller.d.ts.map +1 -1
  109. package/dist/maps/controller.js +27 -7
  110. package/dist/maps/controller.js.map +1 -1
  111. package/dist/richtext/node.d.ts +8 -1
  112. package/dist/richtext/node.d.ts.map +1 -1
  113. package/dist/richtext/node.js +5 -1
  114. package/dist/richtext/node.js.map +1 -1
  115. package/dist/richtext/runs.d.ts +25 -6
  116. package/dist/richtext/runs.d.ts.map +1 -1
  117. package/dist/richtext/runs.js +240 -24
  118. package/dist/richtext/runs.js.map +1 -1
  119. package/package.json +2 -2
  120. package/src/html/controls.ts +114 -51
  121. package/src/html/css/cascade.ts +614 -140
  122. package/src/html/css/color.ts +29 -0
  123. package/src/html/css/parse.ts +388 -33
  124. package/src/html/css/style.ts +688 -150
  125. package/src/html/css/transform.ts +459 -0
  126. package/src/html/css/ua.ts +77 -10
  127. package/src/html/css/values.ts +4 -0
  128. package/src/html/dom.ts +3 -1
  129. package/src/html/fonts.ts +417 -24
  130. package/src/html/form.ts +962 -0
  131. package/src/html/index.ts +49 -338
  132. package/src/html/layout/axes.ts +62 -0
  133. package/src/html/layout/block.ts +447 -37
  134. package/src/html/layout/boxes.ts +143 -14
  135. package/src/html/layout/cache.ts +6 -0
  136. package/src/html/layout/css-grid.ts +95 -22
  137. package/src/html/layout/flex.ts +675 -78
  138. package/src/html/layout/inline.ts +159 -55
  139. package/src/html/layout/multicol.ts +977 -0
  140. package/src/html/layout/shaping.ts +27 -18
  141. package/src/html/layout/table.ts +62 -19
  142. package/src/html/node.ts +798 -172
  143. package/src/html/paint.ts +1742 -241
  144. package/src/html/svg.ts +277 -19
  145. package/src/html/widgets.ts +821 -0
  146. package/src/html/woff2.ts +612 -0
  147. package/src/index.ts +1 -0
  148. package/src/maps/controller.ts +28 -7
  149. package/src/richtext/node.ts +13 -2
  150. package/src/richtext/runs.ts +313 -35
@@ -0,0 +1,897 @@
1
+ /**
2
+ * Multi-column layout (CSS Multi-column 1), over the layout the block pass
3
+ * makes of a container's content as one column: a strip a column wide and
4
+ * as tall as it all comes to. What is here decides where the strip breaks,
5
+ * and moves each piece of it to its column.
6
+ *
7
+ * Moved rather than laid out again, because a break changes nothing about a
8
+ * box but where it is: every column is the same width, so a paragraph's
9
+ * lines are the ones the strip gave it, and a box that a break misses is the
10
+ * same box in another place. A box a break falls inside keeps one rect,
11
+ * which takes in all its pieces as a browser's bounding rect of it does, and
12
+ * its pieces beside it (`COLUMN_PIECES`); the lines of a paragraph go each to
13
+ * its column (`COLUMN_LINES`).
14
+ *
15
+ * Where the breaks go is CSS Fragmentation 3's: between the lines of a block
16
+ * and between sibling boxes, never inside a line, a replaced element, a
17
+ * table, a flex box or a grid, a box that clips or scrolls, or one that
18
+ * says `break-inside: avoid` (4.1); `orphans` and `widows` lines of a block
19
+ * kept together at either side of one (4.3), and a margin next to one
20
+ * dropped where the break was not forced (5.2). A box with a height and
21
+ * nothing in it has no line or box for a break to fall inside, and is cut
22
+ * wherever a column ends; so is the room clearance leaves above a box or a
23
+ * line, which is no margin. How tall the columns are is left to the user
24
+ * agent where they balance (Multi-column 1, 7), and this does as Blink
25
+ * does: the content's height over the number of columns, no less than the
26
+ * tallest thing that cannot break, and then, while that many columns do not
27
+ * hold it, taller by the least amount that lets one more thing into a
28
+ * column — the smallest "space shortage" any break left
29
+ * (`NGColumnLayoutAlgorithm`, `StretchColumnBlockSize`).
30
+ *
31
+ * What this cannot do for having laid the content out once: a float that
32
+ * cannot be cut stays with the lines that were set beside it, where a
33
+ * browser takes it to the next column and sets those lines again, wider;
34
+ * and a table, a flex box and a grid go to a column whole, where a browser
35
+ * breaks them between their rows.
36
+ */
37
+ import { CONTAIN_SIZE, scrolls } from '../css/style.js';
38
+ import { gapOf } from '../css/values.js';
39
+ import { contained, establishesBFC, moveTo, transformed } from './block.js';
40
+ import { CLEARED_FROM, COLUMN_LINES, COLUMN_PIECES, COLUMN_ROWS, columned, } from './boxes.js';
41
+ /** Slack for sums of fractions of a pixel that should have come out even. */
42
+ const EPS = 1 / 128;
43
+ /** How far past the rows of its column a text's layout is left to draw at
44
+ * an end no other column's rows are beyond: a glyph taller than its line. */
45
+ const REACH = 2048;
46
+ /**
47
+ * The columns a multicol container's content box of a width is set in (CSS
48
+ * Multi-column 1, 3.4): as many as `column-count` says, or as many
49
+ * `column-width` wide as fit, or the fewer of the two, and each as wide as
50
+ * leaves a gap between them. Null for a box that is none, and for one laid
51
+ * out at no width limit, whose content is one column as wide as it likes.
52
+ */
53
+ export function columnsOf(box, available) {
54
+ const style = box.style;
55
+ if (!style.columns || box.kind !== 'block')
56
+ return null;
57
+ if (!Number.isFinite(available))
58
+ return null;
59
+ // `normal` is an em between columns (4.1)
60
+ const gap = style.columnGapNormal
61
+ ? style.fontSize
62
+ : gapOf(style.columnGap, available);
63
+ let count;
64
+ if (style.columnWidth === null)
65
+ count = style.columnCount ?? 1;
66
+ else {
67
+ const fit = Math.max(1, Math.floor((available + gap) / (style.columnWidth + gap)));
68
+ count = style.columnCount === null ? fit : Math.min(style.columnCount, fit);
69
+ }
70
+ return {
71
+ count,
72
+ gap,
73
+ width: Math.max(0, (available - (count - 1) * gap) / count),
74
+ };
75
+ }
76
+ const NO_GROUP = {
77
+ top: 0,
78
+ bottom: 0,
79
+ block: null,
80
+ sliced: false,
81
+ empty: true,
82
+ end: 0,
83
+ column: 0,
84
+ last: 0,
85
+ };
86
+ /** Whether no break may fall inside a box, whatever it holds: it is
87
+ * monolithic, or says so. It goes to a column with all that is in it. */
88
+ function whole(box) {
89
+ if (box.kind !== 'block')
90
+ return true;
91
+ if (box.style.breakInside === 'avoid')
92
+ return true;
93
+ return establishesBFC(box) || transformed(box.style);
94
+ }
95
+ /**
96
+ * Whether a box that holds nothing to break between may be cut anywhere: a
97
+ * block with a height and nothing in it, floated or not, which a browser
98
+ * carries on from one column to the next (CSS Fragmentation 3, 4.1 makes
99
+ * only what cannot be cut monolithic: replaced content, and a box that
100
+ * scrolls or clips).
101
+ */
102
+ function sliceable(box) {
103
+ if (box.kind !== 'block' || box.marker)
104
+ return false;
105
+ const style = box.style;
106
+ if (style.breakInside === 'avoid' || scrolls(style))
107
+ return false;
108
+ if (transformed(style) || style.columns)
109
+ return false;
110
+ // size containment makes a box monolithic (CSS Containment 2, 3.1)
111
+ if (contained(box, CONTAIN_SIZE))
112
+ return false;
113
+ if (box.lines?.length)
114
+ return false;
115
+ for (const child of box.children) {
116
+ if (flows(child))
117
+ return false;
118
+ }
119
+ return true;
120
+ }
121
+ /** Whether a block's child is a box in its flow. */
122
+ function flows(child) {
123
+ switch (child.kind) {
124
+ case 'text':
125
+ case 'break':
126
+ case 'inline':
127
+ case 'marker':
128
+ return false;
129
+ default:
130
+ return !child.outOfFlow && !child.isFloat;
131
+ }
132
+ }
133
+ /** The floats and the out-of-flow boxes in a block's inline content, at
134
+ * any depth of inline box. */
135
+ function eachAside(box, visit) {
136
+ for (const child of box.children) {
137
+ if (child.isFloat || child.outOfFlow)
138
+ visit(child);
139
+ else if (child.kind === 'inline')
140
+ eachAside(child, visit);
141
+ }
142
+ }
143
+ /** How far down a box that goes whole reaches: its own bottom, and the
144
+ * floats that hang out of it where it does not hold them. */
145
+ function reachOf(box) {
146
+ let bottom = box.y + box.height;
147
+ if (box.kind !== 'block' || establishesBFC(box))
148
+ return bottom;
149
+ const walk = (node) => {
150
+ for (const child of node.children) {
151
+ if (child.outOfFlow)
152
+ continue;
153
+ if (child.isFloat) {
154
+ bottom = Math.max(bottom, child.y + child.height + Math.max(0, child.marginBottom));
155
+ }
156
+ else if (child.kind === 'inline' || child.kind === 'block')
157
+ walk(child);
158
+ }
159
+ };
160
+ walk(box);
161
+ return bottom;
162
+ }
163
+ /**
164
+ * A float that goes whole, as a unit with the lines and the boxes beside
165
+ * it, which it is set in a column with. One that can be cut anywhere is
166
+ * none: it stands beside the flow, which breaks as it would without it,
167
+ * and is cut where the columns end (`cutFloat`).
168
+ */
169
+ function floatUnit(float, from, into) {
170
+ if (columned.any)
171
+ COLUMN_PIECES.delete(float);
172
+ into.loose.add(from);
173
+ if (sliceable(float))
174
+ return;
175
+ const unit = {
176
+ top: float.y - Math.max(0, float.marginTop),
177
+ bottom: float.y + float.height + Math.max(0, float.marginBottom),
178
+ block: null,
179
+ sliced: false,
180
+ size: 0,
181
+ end: 0,
182
+ group: NO_GROUP,
183
+ };
184
+ into.units.push(made(unit));
185
+ into.boxes.set(float, unit);
186
+ }
187
+ /**
188
+ * Room in the flow that nothing stands in and no margin makes: what
189
+ * clearance moved a box or a line down by. A break drops a margin beside
190
+ * it (CSS Fragmentation 3, 5.2) and not this, which the columns take as
191
+ * they would a box with nothing in it, cut wherever one ends.
192
+ */
193
+ function space(top, bottom,
194
+ /** The block whose lines it is between. */
195
+ block, into) {
196
+ const unit = {
197
+ top,
198
+ bottom,
199
+ block,
200
+ sliced: true,
201
+ size: 0,
202
+ end: 0,
203
+ group: NO_GROUP,
204
+ };
205
+ into.units.push(made(unit));
206
+ return unit;
207
+ }
208
+ /**
209
+ * The units of a box's content, in the strip, and which box or line each
210
+ * is: a line for each line, a box for each box that goes whole, and the
211
+ * content of any other block in its turn — whose own top edge goes with its
212
+ * first and whose bottom with its last, since no break comes between a box's
213
+ * edge and what it holds. Answers the first and the last in the flow.
214
+ */
215
+ function gather(box, into) {
216
+ let first = null;
217
+ let last = null;
218
+ const lines = box.lines;
219
+ if (lines?.length) {
220
+ let under = box.contentY;
221
+ for (const line of lines) {
222
+ // room a line was set below — a `<br clear>`, a line that did not
223
+ // fit beside a float — is no margin for a break to drop
224
+ if (line.y - under > EPS) {
225
+ const room = space(under, line.y, box, into);
226
+ first ??= room;
227
+ }
228
+ under = line.y + line.height;
229
+ const unit = {
230
+ top: line.y,
231
+ bottom: line.y + line.height,
232
+ block: box,
233
+ sliced: false,
234
+ size: 0,
235
+ end: 0,
236
+ group: NO_GROUP,
237
+ };
238
+ into.units.push(made(unit));
239
+ into.lines.set(line, unit);
240
+ first ??= unit;
241
+ last = unit;
242
+ }
243
+ eachAside(box, (aside) => {
244
+ if (aside.isFloat)
245
+ floatUnit(aside, box, into);
246
+ });
247
+ return first && last ? [first, last] : null;
248
+ }
249
+ for (const child of box.children) {
250
+ if (child.isFloat && !child.outOfFlow) {
251
+ floatUnit(child, box, into);
252
+ continue;
253
+ }
254
+ if (!flows(child))
255
+ continue;
256
+ if (columned.any)
257
+ COLUMN_PIECES.delete(child);
258
+ // the room clearance set it down by, which is none of its margin
259
+ const clear = CLEARED_FROM.get(child);
260
+ if (clear !== undefined && child.y - clear > EPS) {
261
+ const room = space(clear, child.y, null, into);
262
+ first ??= room;
263
+ }
264
+ if (whole(child)) {
265
+ const unit = {
266
+ top: child.y,
267
+ bottom: reachOf(child),
268
+ block: null,
269
+ sliced: sliceable(child),
270
+ size: 0,
271
+ end: 0,
272
+ group: NO_GROUP,
273
+ };
274
+ into.units.push(made(unit));
275
+ into.boxes.set(child, unit);
276
+ first ??= unit;
277
+ last = unit;
278
+ continue;
279
+ }
280
+ const span = gather(child, into);
281
+ if (into.loose.has(child))
282
+ into.loose.add(box);
283
+ if (!span) {
284
+ // nothing in its flow to break between: it goes whole, or is cut
285
+ const unit = {
286
+ top: child.y,
287
+ bottom: child.y + child.height,
288
+ block: null,
289
+ sliced: sliceable(child),
290
+ size: 0,
291
+ end: 0,
292
+ group: NO_GROUP,
293
+ };
294
+ into.units.push(made(unit));
295
+ into.boxes.set(child, unit);
296
+ first ??= unit;
297
+ last = unit;
298
+ continue;
299
+ }
300
+ span[0].top = Math.min(span[0].top, child.y);
301
+ span[1].bottom = Math.max(span[1].bottom, child.y + child.height);
302
+ into.spans.set(child, span);
303
+ first ??= span[0];
304
+ last = span[1];
305
+ }
306
+ return first && last ? [first, last] : null;
307
+ }
308
+ /** The units in groups, down the strip: each group what a break may come
309
+ * before. Units that overlap are one group, which no break divides. */
310
+ /** Note a unit's own extent, as it is made. */
311
+ function made(unit) {
312
+ unit.size = unit.bottom - unit.top;
313
+ unit.end = unit.bottom;
314
+ return unit;
315
+ }
316
+ function groupsOf(units) {
317
+ // in the order of their tops, which is the document's but for a float
318
+ const sorted = units.slice().sort((a, b) => a.top - b.top);
319
+ const groups = [];
320
+ let group = null;
321
+ for (const unit of sorted) {
322
+ if (group && unit.top < group.bottom - EPS) {
323
+ group.bottom = Math.max(group.bottom, unit.bottom);
324
+ group.block = null;
325
+ group.sliced = false;
326
+ group.empty &&= unit.size <= EPS;
327
+ group.end = Math.max(group.end, unit.end);
328
+ }
329
+ else {
330
+ group = {
331
+ top: unit.top,
332
+ bottom: unit.bottom,
333
+ block: unit.block,
334
+ sliced: unit.sliced,
335
+ empty: unit.size <= EPS,
336
+ end: unit.end,
337
+ column: 0,
338
+ last: 0,
339
+ };
340
+ groups.push(group);
341
+ }
342
+ unit.group = group;
343
+ }
344
+ return groups;
345
+ }
346
+ /** The most columns content is set in: past it, the rest stays in the
347
+ * last. A column a fraction of a pixel tall would make one for every such
348
+ * fraction of a box that can be cut anywhere. */
349
+ const MOST_COLUMNS = 2000;
350
+ /**
351
+ * The groups set in columns of a height: each column filled until the next
352
+ * group does not fit, and broken there — or before there, where that would
353
+ * leave fewer lines of a block than `orphans` at the foot of the column, or
354
+ * fewer than `widows` at the head of the next: `orphans` first, and then as
355
+ * many of `widows` as that leaves, which is how Blink weighs the two (a
356
+ * block of four lines with `widows: 3` breaks two and two; CSS says only
357
+ * that both are to be honoured, 4.3). What stands
358
+ * first in a column stays there however tall, and the first `orphans` lines
359
+ * of a block with it. A box that can be cut anywhere is cut at the foot of
360
+ * the column, and goes on at the head of the next.
361
+ */
362
+ function fill(groups, top, height,
363
+ /** Whether the columns can be no taller: the container's own height, or
364
+ * its `max-height`, is what they are. */
365
+ fixed) {
366
+ const starts = [0];
367
+ const tops = [top];
368
+ const cut = [false];
369
+ let shortage = Infinity;
370
+ let over = false;
371
+ let head = 0;
372
+ let columnTop = top;
373
+ /** Whether the group at the column's head is one the column before
374
+ * cut: it goes on from the column's top. */
375
+ let goesOn = false;
376
+ let i = 0;
377
+ const missed = (index) => {
378
+ const by = groups[index].bottom - columnTop - height;
379
+ if (by > EPS && by < shortage)
380
+ shortage = by;
381
+ };
382
+ /** Whether nothing with a height has been set in the column before a
383
+ * group: the top edges of the boxes it starts, at most — or, `blank`,
384
+ * not even those. */
385
+ const bare = (index, blank) => {
386
+ for (let k = head; k < index; k += 1) {
387
+ const before = groups[k];
388
+ if (blank ? before.bottom - before.top > EPS : !before.empty) {
389
+ return false;
390
+ }
391
+ }
392
+ return !goesOn;
393
+ };
394
+ const next = (at, from, inside) => {
395
+ starts.push(at);
396
+ tops.push(from);
397
+ cut.push(inside);
398
+ head = at;
399
+ columnTop = from;
400
+ goesOn = inside;
401
+ i = at;
402
+ };
403
+ while (i < groups.length) {
404
+ const group = groups[i];
405
+ if (group.bottom - columnTop <= height + EPS ||
406
+ // nothing at all, which takes no room wherever it is
407
+ group.bottom - group.top <= EPS ||
408
+ starts.length >= MOST_COLUMNS) {
409
+ i += 1;
410
+ goesOn = false;
411
+ continue;
412
+ }
413
+ missed(i);
414
+ if (group.sliced && height >= 1) {
415
+ // what fits of it stays, and the rest goes on; none of it fits where
416
+ // it starts at the column's foot, and then all of it goes on
417
+ const from = goesOn ? columnTop : group.top;
418
+ const foot = columnTop + height;
419
+ if (foot - from > EPS)
420
+ next(i, foot, true);
421
+ else
422
+ next(i, group.top, false);
423
+ continue;
424
+ }
425
+ if (i === head ||
426
+ bare(i, true) ||
427
+ (group.end - columnTop <= height + EPS && bare(i, false))) {
428
+ // first in its column, or after nothing that takes any room, where
429
+ // it stays however tall; or all of it fits but the bottom edge of a
430
+ // box it ends, and nothing with a height has been set in the column
431
+ // to break before it: the edge hangs out (where Blink breaks before
432
+ // the edge, as a last resort)
433
+ over = true;
434
+ i += 1;
435
+ goesOn = false;
436
+ continue;
437
+ }
438
+ let at = i;
439
+ const block = group.block;
440
+ if (block) {
441
+ // the lines of its block this one is among, and which of them it is
442
+ const run = linesAbout(groups, i);
443
+ const k = run.indexOf(i);
444
+ const count = run.length;
445
+ const orphans = Math.min(block.style.orphans, count);
446
+ const widows = Math.min(block.style.widows, count);
447
+ const a = run[0];
448
+ if (a >= head && k > 0 && k < orphans) {
449
+ if (a === head && !fixed) {
450
+ // at the head of the column already: they stay, and hang out,
451
+ // for a taller column to take them in
452
+ missed(run[orphans - 1]);
453
+ over = true;
454
+ i = run[orphans - 1] + 1;
455
+ goesOn = false;
456
+ continue;
457
+ }
458
+ // too few would stay: the block goes to the next column whole —
459
+ // or, at the head of a column that can be no taller, breaks where
460
+ // it must, with fewer
461
+ missed(run[orphans - 1]);
462
+ if (a !== head)
463
+ at = a;
464
+ }
465
+ else if (k > 0 && count - k < widows) {
466
+ // Too few would go: more go with them, as many as leave `orphans`
467
+ // behind, which may be fewer than `widows` asks for. Of the lines
468
+ // set from this column on, where the block goes on from a column
469
+ // before — and there, where not even all of them are enough, it
470
+ // breaks where it would (as Blink breaks each: seven lines, three
471
+ // to a column, under `widows: 5` are two, three and two).
472
+ const lo = a >= head ? 0 : run.findIndex((line) => line >= head);
473
+ const keep = count - lo - Math.min(block.style.widows, count - lo);
474
+ if (lo === 0 || keep > 0) {
475
+ const stay = Math.max(orphans, keep);
476
+ if (stay < k - lo)
477
+ at = run[lo + stay];
478
+ }
479
+ }
480
+ }
481
+ next(at, groups[at].top, false);
482
+ }
483
+ return { starts, tops, cut, shortage, over };
484
+ }
485
+ /** The lines of a block that a line is among, in order: the groups about
486
+ * it that are lines of its block, past the room between them. */
487
+ function linesAbout(groups, i) {
488
+ const block = groups[i].block;
489
+ const run = [i];
490
+ for (let k = i - 1; k >= 0 && groups[k].block === block; k -= 1) {
491
+ if (!groups[k].sliced)
492
+ run.unshift(k);
493
+ }
494
+ for (let k = i + 1; k < groups.length && groups[k].block === block; k += 1) {
495
+ if (!groups[k].sliced)
496
+ run.push(k);
497
+ }
498
+ return run;
499
+ }
500
+ /**
501
+ * The tallest thing in the strip no break may fall inside, which a column
502
+ * is no shorter than: a box that goes whole, a line, and the lines of a
503
+ * block that `orphans` keeps together at its start and `widows` at its end
504
+ * (as Blink's initial balancing pass takes them,
505
+ * `PropagateTallestUnbreakableBlockSize`).
506
+ */
507
+ function tallestOf(groups) {
508
+ let tallest = 0;
509
+ for (let i = 0; i < groups.length; i += 1) {
510
+ const group = groups[i];
511
+ const block = group.block;
512
+ if (group.sliced)
513
+ continue;
514
+ if (!block) {
515
+ tallest = Math.max(tallest, group.bottom - group.top);
516
+ continue;
517
+ }
518
+ const run = linesAbout(groups, i);
519
+ const count = run.length;
520
+ const orphans = Math.min(block.style.orphans, count);
521
+ const widows = Math.min(block.style.widows, count);
522
+ tallest = Math.max(tallest, groups[run[orphans - 1]].bottom - group.top, groups[run[count - 1]].bottom - groups[run[count - widows]].top);
523
+ i = run[count - 1];
524
+ }
525
+ return tallest;
526
+ }
527
+ /**
528
+ * Set a multicol container's content, laid out as one strip at its content
529
+ * box's top, in its columns, and answer their height — the container's
530
+ * content height, where it has none of its own.
531
+ *
532
+ * `strip` is the strip's height, its first and last margins in it, and
533
+ * `limit` the tallest a column may be: the container's own height, or its
534
+ * `max-height`. Past it the content goes on in columns beyond the
535
+ * container's edge (8.2). Under `column-fill: auto` the columns are that
536
+ * tall, each filled before the next is started, and where nothing limits
537
+ * them the first holds everything (7.1).
538
+ */
539
+ export function layoutColumns(box, columns, strip, limit,
540
+ /** Where the strip starts: the content box's top, or under a box that
541
+ * spans the columns (`spansColumns`). */
542
+ top = box.contentY) {
543
+ const gathered = {
544
+ units: [],
545
+ lines: new Map(),
546
+ boxes: new Map(),
547
+ spans: new Map(),
548
+ loose: new Set(),
549
+ };
550
+ gather(box, gathered);
551
+ const groups = groupsOf(gathered.units);
552
+ // as far down as the content goes, past a box with a height it overflows:
553
+ // what stands out of a box takes room in a column as what is in it does
554
+ for (const group of groups)
555
+ strip = Math.max(strip, group.bottom - top);
556
+ let height;
557
+ let filled;
558
+ if (box.style.columnFill === 'auto') {
559
+ height = Number.isFinite(limit) ? limit : strip;
560
+ filled = fill(groups, top, height, height >= limit);
561
+ // a container with a `max-height` and less than a column of content
562
+ // is as tall as its content
563
+ if (filled.starts.length === 1)
564
+ height = Math.min(height, strip);
565
+ }
566
+ else {
567
+ // balanced: the strip shared out, and no shorter than the tallest
568
+ // thing that cannot break; then as much taller as lets the next thing
569
+ // in, until the columns there are hold it all
570
+ height = Math.min(limit, Math.max(strip / columns.count, tallestOf(groups)));
571
+ filled = fill(groups, top, height, height >= limit);
572
+ for (let pass = 0; pass < 4096; pass += 1) {
573
+ if (filled.starts.length <= columns.count && !filled.over)
574
+ break;
575
+ if (height >= limit || !Number.isFinite(filled.shortage))
576
+ break;
577
+ height = Math.min(limit, height + filled.shortage);
578
+ filled = fill(groups, top, height, height >= limit);
579
+ }
580
+ }
581
+ // all in one column where it started, and nothing to move — but a float
582
+ // that is cut where the column ends, which may reach past it
583
+ if (!gathered.loose.size && (!groups.length || filled.starts.length === 1)) {
584
+ return height;
585
+ }
586
+ // the column each group starts in, and the one it ends in
587
+ const { starts, tops, cut } = filled;
588
+ let c = 0;
589
+ for (let i = 0; i < groups.length; i += 1) {
590
+ while (c + 1 < starts.length &&
591
+ starts[c + 1] <= i &&
592
+ !(starts[c + 1] === i && cut[c + 1])) {
593
+ c += 1;
594
+ }
595
+ groups[i].column = c;
596
+ while (c + 1 < starts.length && starts[c + 1] === i && cut[c + 1])
597
+ c += 1;
598
+ groups[i].last = c;
599
+ }
600
+ const step = columns.width + columns.gap;
601
+ // the first column at the start of the line: the right, right to left
602
+ const rtl = box.style.direction === 'rtl';
603
+ const across = (c) => (rtl ? -c * step : c * step);
604
+ const origin = rtl ? box.contentWidth - columns.width : 0;
605
+ place(box, null, {
606
+ gathered,
607
+ tops,
608
+ height,
609
+ dx: (c) => origin + across(c),
610
+ dy: (c) => top - tops[c],
611
+ });
612
+ return height;
613
+ }
614
+ function shift(box, column, to) {
615
+ moveTo(box, box.x + to.dx(column), box.y + to.dy(column));
616
+ }
617
+ function shiftLine(line, dx, dy) {
618
+ line.x += dx;
619
+ line.y += dy;
620
+ for (const text of line.texts) {
621
+ text.drawX += dx;
622
+ text.drawY += dy;
623
+ }
624
+ for (const placed of line.atomics) {
625
+ placed.x += dx;
626
+ placed.y += dy;
627
+ moveTo(placed.box, placed.box.x + dx, placed.box.y + dy);
628
+ }
629
+ for (const edge of line.edges ?? [])
630
+ edge.x += dx;
631
+ }
632
+ /** The column a point down the strip is in, between boxes: at a break it
633
+ * is in the column the break ends, which is where Blink leaves what is out
634
+ * of the flow and stood before the box the next column starts with. */
635
+ function columnAt(y, tops) {
636
+ let c = 0;
637
+ while (c + 1 < tops.length && tops[c + 1] < y - EPS)
638
+ c += 1;
639
+ return c;
640
+ }
641
+ /**
642
+ * Move what a box holds to its columns: the container's content, with no
643
+ * `span`, or that of a box a break falls inside, which is then its pieces.
644
+ */
645
+ function place(box, span, to) {
646
+ const { gathered } = to;
647
+ const first = span ? span[0].group.column : 0;
648
+ const last = span ? span[1].group.last : to.tops.length - 1;
649
+ if (span && first === last && !gathered.loose.has(box)) {
650
+ shift(box, first, to);
651
+ return;
652
+ }
653
+ const was = { x: box.x, y: box.y, width: box.width, height: box.height };
654
+ const lines = box.lines;
655
+ // what is out of the flow goes from where it would have stood, which is
656
+ // kept from this box's corner: in the column of the line it would have
657
+ // been on, or of the boxes it would have been between
658
+ const asides = [];
659
+ const aside = (child) => {
660
+ const at = child.staticPosition;
661
+ if (!child.outOfFlow || !at || at.from !== box)
662
+ return;
663
+ const y = was.y + at.y;
664
+ let c = columnAt(y, to.tops);
665
+ if (lines?.length) {
666
+ let on = lines[0];
667
+ for (const line of lines) {
668
+ if (line.y > y + EPS)
669
+ break;
670
+ on = line;
671
+ }
672
+ c = gathered.lines.get(on).group.column;
673
+ }
674
+ asides.push([child, Math.min(last, Math.max(first, c))]);
675
+ };
676
+ if (lines?.length)
677
+ eachAside(box, aside);
678
+ else
679
+ for (const child of box.children)
680
+ aside(child);
681
+ if (lines?.length) {
682
+ for (const line of lines) {
683
+ const c = gathered.lines.get(line).group.column;
684
+ shiftLine(line, to.dx(c), to.dy(c));
685
+ }
686
+ if (first !== last) {
687
+ COLUMN_LINES.add(lines);
688
+ rowsOf(lines, gathered);
689
+ columned.any = true;
690
+ }
691
+ eachAside(box, (aside) => {
692
+ const unit = gathered.boxes.get(aside);
693
+ if (unit)
694
+ place(aside, [unit, unit], to);
695
+ else if (aside.isFloat && !aside.outOfFlow)
696
+ cutFloat(aside, to);
697
+ });
698
+ }
699
+ else {
700
+ for (const child of box.children) {
701
+ // a box that goes whole, or is cut where it holds nothing
702
+ const unit = gathered.boxes.get(child);
703
+ const inner = unit ? [unit, unit] : undefined;
704
+ const range = inner ?? gathered.spans.get(child);
705
+ if (range)
706
+ place(child, range, to);
707
+ else if (child.isFloat && !child.outOfFlow)
708
+ cutFloat(child, to);
709
+ }
710
+ }
711
+ if (span && first === last) {
712
+ // in one column, with a float in it that went its own way: the box
713
+ // itself, which what it holds was moved without
714
+ box.x += to.dx(first);
715
+ box.y += to.dy(first);
716
+ if (box.marker) {
717
+ box.marker.x += to.dx(first);
718
+ box.marker.y += to.dy(first);
719
+ }
720
+ }
721
+ else if (span) {
722
+ const pieces = [];
723
+ let x1 = Infinity;
724
+ let y1 = Infinity;
725
+ let x2 = -Infinity;
726
+ let y2 = -Infinity;
727
+ for (let c = first; c <= last; c += 1) {
728
+ const from = c === first ? was.y : to.tops[c];
729
+ const until = c === last ? was.y + was.height : to.tops[c] + to.height;
730
+ const piece = {
731
+ x: was.x + to.dx(c),
732
+ y: from + to.dy(c),
733
+ width: was.width,
734
+ height: Math.max(0, until - from),
735
+ wholeY: was.y + to.dy(c),
736
+ wholeHeight: was.height,
737
+ };
738
+ pieces.push(piece);
739
+ x1 = Math.min(x1, piece.x);
740
+ y1 = Math.min(y1, piece.y);
741
+ x2 = Math.max(x2, piece.x + piece.width);
742
+ y2 = Math.max(y2, piece.y + piece.height);
743
+ }
744
+ COLUMN_PIECES.set(box, pieces);
745
+ columned.any = true;
746
+ if (box.marker) {
747
+ box.marker.x += to.dx(first);
748
+ box.marker.y += to.dy(first);
749
+ }
750
+ box.x = x1;
751
+ box.y = y1;
752
+ box.width = x2 - x1;
753
+ box.height = y2 - y1;
754
+ }
755
+ for (const [child, c] of asides) {
756
+ const at = child.staticPosition;
757
+ const width = at.right - at.x;
758
+ at.x = was.x + at.x + to.dx(c) - box.x;
759
+ at.right = at.x + width;
760
+ at.y = was.y + at.y + to.dy(c) - box.y;
761
+ }
762
+ }
763
+ /**
764
+ * A float that can be cut anywhere, set in the columns it reaches: from
765
+ * where it starts to the foot of that column, and then down each column
766
+ * after it from the top, as much of it as is left. By how much of it each
767
+ * column has taken, and not by where the strip has it: the flow beside it
768
+ * drops a margin at a break, and the float has none there to drop.
769
+ */
770
+ function cutFloat(float, to) {
771
+ const { tops, height } = to;
772
+ let c = 0;
773
+ while (c + 1 < tops.length && tops[c + 1] <= float.y + EPS)
774
+ c += 1;
775
+ // past the foot of its column, in the margin a break dropped: at the
776
+ // head of the next
777
+ let at = float.y - tops[c];
778
+ if (at >= height - EPS && c + 1 < tops.length) {
779
+ c += 1;
780
+ at = 0;
781
+ }
782
+ const was = {
783
+ x: float.x,
784
+ y: float.y,
785
+ width: float.width,
786
+ height: float.height,
787
+ };
788
+ if (at + was.height <= height + EPS || !(height >= 1)) {
789
+ moveTo(float, was.x + to.dx(c), tops[0] + at);
790
+ return;
791
+ }
792
+ const pieces = [];
793
+ let taken = 0;
794
+ let x1 = Infinity;
795
+ let x2 = -Infinity;
796
+ let y2 = -Infinity;
797
+ const head = tops[0];
798
+ for (let k = c; taken < was.height - EPS; k += 1) {
799
+ const from = k === c ? at : 0;
800
+ const part = Math.min(was.height - taken, height - from);
801
+ const x = was.x + to.dx(k);
802
+ pieces.push({
803
+ x,
804
+ y: head + from,
805
+ width: was.width,
806
+ height: part,
807
+ wholeY: head + from - taken,
808
+ wholeHeight: was.height,
809
+ });
810
+ x1 = Math.min(x1, x);
811
+ x2 = Math.max(x2, x + was.width);
812
+ y2 = Math.max(y2, head + from + part);
813
+ taken += part;
814
+ if (pieces.length >= MOST_COLUMNS)
815
+ break;
816
+ }
817
+ COLUMN_PIECES.set(float, pieces);
818
+ columned.any = true;
819
+ float.x = x1;
820
+ float.y = Math.min(head + at, head);
821
+ float.width = x2 - x1;
822
+ float.height = y2 - float.y;
823
+ }
824
+ /**
825
+ * The rows each text's layout has in each column, for the layouts whose
826
+ * lines columns took apart (`COLUMN_ROWS`).
827
+ */
828
+ function rowsOf(lines, gathered) {
829
+ const byLayout = new Map();
830
+ for (const line of lines) {
831
+ const c = gathered.lines.get(line).group.column;
832
+ for (const text of line.texts) {
833
+ let columns = byLayout.get(text.layout);
834
+ if (!columns)
835
+ byLayout.set(text.layout, (columns = new Map()));
836
+ const texts = columns.get(c);
837
+ if (texts)
838
+ texts.push(text);
839
+ else
840
+ columns.set(c, [text]);
841
+ }
842
+ }
843
+ for (const [layout, columns] of byLayout) {
844
+ if (columns.size < 2)
845
+ continue;
846
+ const end = layout.lines.length - 1;
847
+ for (const texts of columns.values()) {
848
+ let top = Infinity;
849
+ let bottom = -Infinity;
850
+ let open = 0;
851
+ for (const text of texts) {
852
+ const natural = layout.lines[text.layoutLine];
853
+ if (!natural)
854
+ continue;
855
+ top = Math.min(top, natural.y);
856
+ bottom = Math.max(bottom, natural.y + natural.height);
857
+ if (text.layoutLine === 0)
858
+ open |= 1;
859
+ if (text.layoutLine === end)
860
+ open |= 2;
861
+ }
862
+ if (top === Infinity)
863
+ continue;
864
+ const rows = {
865
+ top: open & 1 ? top - REACH : top,
866
+ bottom: open & 2 ? bottom + REACH : bottom,
867
+ };
868
+ for (const text of texts)
869
+ COLUMN_ROWS.set(text, rows);
870
+ }
871
+ }
872
+ }
873
+ /**
874
+ * Whether a box is set across all the columns of the multicol container it
875
+ * is a child of (`column-span: all`, CSS Multi-column 1, 6): a block-level
876
+ * box in its flow. One further in, in a box of the container's, is set in its column
877
+ * as it would be without: the box around it is not broken about it here.
878
+ */
879
+ export function spansColumns(box) {
880
+ return (box.style.columnSpan &&
881
+ flows(box) &&
882
+ // block-level: on an inline-block it does nothing
883
+ !box.style.display.startsWith('inline'));
884
+ }
885
+ /** Forget the pieces an earlier layout broke a box's content in, where
886
+ * this one sets it in one column. */
887
+ export function forgetColumns(box) {
888
+ if (!columned.any)
889
+ return;
890
+ for (const child of box.children) {
891
+ if (!flows(child) || child.kind !== 'block')
892
+ continue;
893
+ if (COLUMN_PIECES.delete(child) || !whole(child))
894
+ forgetColumns(child);
895
+ }
896
+ }
897
+ //# sourceMappingURL=multicol.js.map