@react-x11/components 0.8.0 → 0.10.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.
- package/README.md +105 -38
- package/dist/charts/node.d.ts +12 -2
- package/dist/charts/node.d.ts.map +1 -1
- package/dist/charts/node.js +50 -16
- package/dist/charts/node.js.map +1 -1
- package/dist/charts/render.d.ts +12 -3
- package/dist/charts/render.d.ts.map +1 -1
- package/dist/charts/render.js +94 -25
- package/dist/charts/render.js.map +1 -1
- package/dist/code/index.d.ts.map +1 -1
- package/dist/code/index.js +3 -1
- package/dist/code/index.js.map +1 -1
- package/dist/code-editor/completion.d.ts.map +1 -1
- package/dist/code-editor/completion.js +3 -2
- package/dist/code-editor/completion.js.map +1 -1
- package/dist/code-editor/index.d.ts.map +1 -1
- package/dist/code-editor/index.js +19 -0
- package/dist/code-editor/index.js.map +1 -1
- package/dist/code-editor/node.d.ts +177 -7
- package/dist/code-editor/node.d.ts.map +1 -1
- package/dist/code-editor/node.js +1329 -164
- package/dist/code-editor/node.js.map +1 -1
- package/dist/code-language/languages/glsl.d.ts.map +1 -1
- package/dist/code-language/languages/glsl.js +10 -2
- package/dist/code-language/languages/glsl.js.map +1 -1
- package/dist/code-language/languages/javascript.d.ts.map +1 -1
- package/dist/code-language/languages/javascript.js +18 -3
- package/dist/code-language/languages/javascript.js.map +1 -1
- package/dist/code-language/languages/json.d.ts.map +1 -1
- package/dist/code-language/languages/json.js +10 -2
- package/dist/code-language/languages/json.js.map +1 -1
- package/dist/code-language/languages/shell.d.ts.map +1 -1
- package/dist/code-language/languages/shell.js +10 -2
- package/dist/code-language/languages/shell.js.map +1 -1
- package/dist/code-language/languages/sql.d.ts.map +1 -1
- package/dist/code-language/languages/sql.js +20 -2
- package/dist/code-language/languages/sql.js.map +1 -1
- package/dist/code-language/stream.d.ts +22 -0
- package/dist/code-language/stream.d.ts.map +1 -1
- package/dist/code-language/stream.js +259 -9
- package/dist/code-language/stream.js.map +1 -1
- package/dist/code-language/types.d.ts +5 -4
- package/dist/code-language/types.d.ts.map +1 -1
- package/dist/codeblock/index.d.ts +13 -2
- package/dist/codeblock/index.d.ts.map +1 -1
- package/dist/codeblock/index.js +20 -3
- package/dist/codeblock/index.js.map +1 -1
- package/dist/embed/client.d.ts +48 -3
- package/dist/embed/client.d.ts.map +1 -1
- package/dist/embed/client.js +79 -5
- package/dist/embed/client.js.map +1 -1
- package/dist/embed/index.d.ts +1 -1
- package/dist/embed/index.d.ts.map +1 -1
- package/dist/embed/index.js +1 -1
- package/dist/embed/index.js.map +1 -1
- package/dist/flow/draw.d.ts +50 -0
- package/dist/flow/draw.d.ts.map +1 -1
- package/dist/flow/draw.js +132 -33
- package/dist/flow/draw.js.map +1 -1
- package/dist/flow/gl/color.d.ts +13 -0
- package/dist/flow/gl/color.d.ts.map +1 -0
- package/dist/flow/gl/color.js +82 -0
- package/dist/flow/gl/color.js.map +1 -0
- package/dist/flow/gl/index.d.ts +60 -0
- package/dist/flow/gl/index.d.ts.map +1 -0
- package/dist/flow/gl/index.js +283 -0
- package/dist/flow/gl/index.js.map +1 -0
- package/dist/flow/gl/pack.d.ts +133 -0
- package/dist/flow/gl/pack.d.ts.map +1 -0
- package/dist/flow/gl/pack.js +408 -0
- package/dist/flow/gl/pack.js.map +1 -0
- package/dist/flow/gl/renderer.d.ts +95 -0
- package/dist/flow/gl/renderer.d.ts.map +1 -0
- package/dist/flow/gl/renderer.js +459 -0
- package/dist/flow/gl/renderer.js.map +1 -0
- package/dist/flow/gl/shaders.d.ts +79 -0
- package/dist/flow/gl/shaders.d.ts.map +1 -0
- package/dist/flow/gl/shaders.js +346 -0
- package/dist/flow/gl/shaders.js.map +1 -0
- package/dist/flow/gl/text.d.ts +318 -0
- package/dist/flow/gl/text.d.ts.map +1 -0
- package/dist/flow/gl/text.js +1103 -0
- package/dist/flow/gl/text.js.map +1 -0
- package/dist/flow/index.d.ts +1 -1
- package/dist/flow/index.d.ts.map +1 -1
- package/dist/flow/index.js +619 -64
- package/dist/flow/index.js.map +1 -1
- package/dist/flow/node.d.ts +621 -72
- package/dist/flow/node.d.ts.map +1 -1
- package/dist/flow/node.js +2253 -1018
- package/dist/flow/node.js.map +1 -1
- package/dist/flow/paint.d.ts +35 -0
- package/dist/flow/paint.d.ts.map +1 -0
- package/dist/flow/paint.js +393 -0
- package/dist/flow/paint.js.map +1 -0
- package/dist/flow/paths.d.ts +14 -0
- package/dist/flow/paths.d.ts.map +1 -1
- package/dist/flow/paths.js +45 -19
- package/dist/flow/paths.js.map +1 -1
- package/dist/flow/scene.d.ts +584 -0
- package/dist/flow/scene.d.ts.map +1 -0
- package/dist/flow/scene.js +1453 -0
- package/dist/flow/scene.js.map +1 -0
- package/dist/flow/types.d.ts +111 -1
- package/dist/flow/types.d.ts.map +1 -1
- package/dist/html/controls.d.ts +28 -0
- package/dist/html/controls.d.ts.map +1 -1
- package/dist/html/controls.js +52 -2
- package/dist/html/controls.js.map +1 -1
- package/dist/html/css/calc.d.ts +14 -0
- package/dist/html/css/calc.d.ts.map +1 -0
- package/dist/html/css/calc.js +344 -0
- package/dist/html/css/calc.js.map +1 -0
- package/dist/html/css/cascade.d.ts +240 -6
- package/dist/html/css/cascade.d.ts.map +1 -1
- package/dist/html/css/cascade.js +1327 -66
- package/dist/html/css/cascade.js.map +1 -1
- package/dist/html/css/color.d.ts +23 -0
- package/dist/html/css/color.d.ts.map +1 -0
- package/dist/html/css/color.js +740 -0
- package/dist/html/css/color.js.map +1 -0
- package/dist/html/css/content.d.ts +92 -0
- package/dist/html/css/content.d.ts.map +1 -0
- package/dist/html/css/content.js +388 -0
- package/dist/html/css/content.js.map +1 -0
- package/dist/html/css/counter-styles.d.ts +76 -0
- package/dist/html/css/counter-styles.d.ts.map +1 -0
- package/dist/html/css/counter-styles.js +893 -0
- package/dist/html/css/counter-styles.js.map +1 -0
- package/dist/html/css/decode.d.ts +12 -0
- package/dist/html/css/decode.d.ts.map +1 -0
- package/dist/html/css/decode.js +116 -0
- package/dist/html/css/decode.js.map +1 -0
- package/dist/html/css/parse.d.ts +110 -10
- package/dist/html/css/parse.d.ts.map +1 -1
- package/dist/html/css/parse.js +1473 -180
- package/dist/html/css/parse.js.map +1 -1
- package/dist/html/css/style.d.ts +498 -15
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +3698 -206
- package/dist/html/css/style.js.map +1 -1
- package/dist/html/css/ua.d.ts.map +1 -1
- package/dist/html/css/ua.js +57 -11
- package/dist/html/css/ua.js.map +1 -1
- package/dist/html/css/values.d.ts +37 -3
- package/dist/html/css/values.d.ts.map +1 -1
- package/dist/html/css/values.js +122 -48
- package/dist/html/css/values.js.map +1 -1
- package/dist/html/css/vars.d.ts +52 -0
- package/dist/html/css/vars.d.ts.map +1 -0
- package/dist/html/css/vars.js +358 -0
- package/dist/html/css/vars.js.map +1 -0
- package/dist/html/dom.d.ts +30 -4
- package/dist/html/dom.d.ts.map +1 -1
- package/dist/html/dom.js +278 -17
- package/dist/html/dom.js.map +1 -1
- package/dist/html/fonts.d.ts +106 -0
- package/dist/html/fonts.d.ts.map +1 -0
- package/dist/html/fonts.js +565 -0
- package/dist/html/fonts.js.map +1 -0
- package/dist/html/gif.d.ts +15 -0
- package/dist/html/gif.d.ts.map +1 -0
- package/dist/html/gif.js +229 -0
- package/dist/html/gif.js.map +1 -0
- package/dist/html/index.d.ts +50 -11
- package/dist/html/index.d.ts.map +1 -1
- package/dist/html/index.js +115 -9
- package/dist/html/index.js.map +1 -1
- package/dist/html/layout/block.d.ts +226 -3
- package/dist/html/layout/block.d.ts.map +1 -1
- package/dist/html/layout/block.js +2937 -197
- package/dist/html/layout/block.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +340 -17
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +1714 -182
- package/dist/html/layout/boxes.js.map +1 -1
- package/dist/html/layout/cache.d.ts +58 -0
- package/dist/html/layout/cache.d.ts.map +1 -0
- package/dist/html/layout/cache.js +224 -0
- package/dist/html/layout/cache.js.map +1 -0
- package/dist/html/layout/collapse.d.ts +45 -0
- package/dist/html/layout/collapse.d.ts.map +1 -0
- package/dist/html/layout/collapse.js +246 -0
- package/dist/html/layout/collapse.js.map +1 -0
- package/dist/html/layout/css-grid.d.ts +7 -0
- package/dist/html/layout/css-grid.d.ts.map +1 -0
- package/dist/html/layout/css-grid.js +900 -0
- package/dist/html/layout/css-grid.js.map +1 -0
- package/dist/html/layout/flex.d.ts.map +1 -1
- package/dist/html/layout/flex.js +1025 -89
- package/dist/html/layout/flex.js.map +1 -1
- package/dist/html/layout/floats.d.ts +45 -6
- package/dist/html/layout/floats.d.ts.map +1 -1
- package/dist/html/layout/floats.js +113 -14
- package/dist/html/layout/floats.js.map +1 -1
- package/dist/html/layout/grid-lines.d.ts +39 -0
- package/dist/html/layout/grid-lines.d.ts.map +1 -0
- package/dist/html/layout/grid-lines.js +119 -0
- package/dist/html/layout/grid-lines.js.map +1 -0
- package/dist/html/layout/grid.d.ts +53 -0
- package/dist/html/layout/grid.d.ts.map +1 -0
- package/dist/html/layout/grid.js +153 -0
- package/dist/html/layout/grid.js.map +1 -0
- package/dist/html/layout/inline.d.ts +151 -1
- package/dist/html/layout/inline.d.ts.map +1 -1
- package/dist/html/layout/inline.js +3375 -174
- package/dist/html/layout/inline.js.map +1 -1
- package/dist/html/layout/shaping.d.ts +6 -0
- package/dist/html/layout/shaping.d.ts.map +1 -0
- package/dist/html/layout/shaping.js +100 -0
- package/dist/html/layout/shaping.js.map +1 -0
- package/dist/html/layout/table.d.ts +3 -0
- package/dist/html/layout/table.d.ts.map +1 -1
- package/dist/html/layout/table.js +681 -175
- package/dist/html/layout/table.js.map +1 -1
- package/dist/html/layout/tracks.d.ts +44 -0
- package/dist/html/layout/tracks.d.ts.map +1 -0
- package/dist/html/layout/tracks.js +349 -0
- package/dist/html/layout/tracks.js.map +1 -0
- package/dist/html/node.d.ts +172 -5
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +1336 -103
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts +92 -3
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +3503 -141
- package/dist/html/paint.js.map +1 -1
- package/dist/html/resources.d.ts +60 -14
- package/dist/html/resources.d.ts.map +1 -1
- package/dist/html/resources.js +122 -36
- package/dist/html/resources.js.map +1 -1
- package/dist/html/surfaces.d.ts +23 -0
- package/dist/html/surfaces.d.ts.map +1 -0
- package/dist/html/surfaces.js +68 -0
- package/dist/html/surfaces.js.map +1 -0
- package/dist/html/svg.d.ts +80 -0
- package/dist/html/svg.d.ts.map +1 -0
- package/dist/html/svg.js +435 -0
- package/dist/html/svg.js.map +1 -0
- package/dist/html/url.d.ts +23 -0
- package/dist/html/url.d.ts.map +1 -0
- package/dist/html/url.js +80 -0
- package/dist/html/url.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/collate.d.ts +3 -0
- package/dist/internal/collate.d.ts.map +1 -0
- package/dist/internal/collate.js +11 -0
- package/dist/internal/collate.js.map +1 -0
- package/dist/{markdown → internal/markdown}/ast.d.ts +23 -0
- package/dist/internal/markdown/ast.d.ts.map +1 -0
- package/dist/internal/markdown/ast.js +22 -0
- package/dist/internal/markdown/ast.js.map +1 -0
- package/dist/internal/markdown/parse.d.ts.map +1 -0
- package/dist/{markdown → internal/markdown}/parse.js +221 -30
- package/dist/internal/markdown/parse.js.map +1 -0
- package/dist/internal/markdown/stringify.d.ts +4 -0
- package/dist/internal/markdown/stringify.d.ts.map +1 -0
- package/dist/internal/markdown/stringify.js +684 -0
- package/dist/internal/markdown/stringify.js.map +1 -0
- package/dist/internal/markdown/tags.d.ts.map +1 -0
- package/dist/internal/markdown/tags.js.map +1 -0
- package/dist/internal/prewarm.d.ts +18 -0
- package/dist/internal/prewarm.d.ts.map +1 -0
- package/dist/internal/prewarm.js +42 -0
- package/dist/internal/prewarm.js.map +1 -0
- package/dist/internal/scroll.d.ts +3 -2
- package/dist/internal/scroll.d.ts.map +1 -1
- package/dist/internal/scroll.js +1 -0
- package/dist/internal/scroll.js.map +1 -1
- package/dist/internal/sdf.d.ts +20 -0
- package/dist/internal/sdf.d.ts.map +1 -0
- package/dist/internal/sdf.js +216 -0
- package/dist/internal/sdf.js.map +1 -0
- package/dist/internal/splice.d.ts +12 -0
- package/dist/internal/splice.d.ts.map +1 -0
- package/dist/internal/splice.js +23 -0
- package/dist/internal/splice.js.map +1 -0
- package/dist/internal/window.d.ts +12 -0
- package/dist/internal/window.d.ts.map +1 -1
- package/dist/internal/window.js +23 -6
- package/dist/internal/window.js.map +1 -1
- package/dist/maps/anchors.d.ts +103 -0
- package/dist/maps/anchors.d.ts.map +1 -0
- package/dist/maps/anchors.js +790 -0
- package/dist/maps/anchors.js.map +1 -0
- package/dist/maps/controller.d.ts +340 -0
- package/dist/maps/controller.d.ts.map +1 -0
- package/dist/maps/controller.js +822 -0
- package/dist/maps/controller.js.map +1 -0
- package/dist/maps/gl/buckets.d.ts +99 -0
- package/dist/maps/gl/buckets.d.ts.map +1 -0
- package/dist/maps/gl/buckets.js +365 -0
- package/dist/maps/gl/buckets.js.map +1 -0
- package/dist/maps/gl/build-worker.d.ts +15 -0
- package/dist/maps/gl/build-worker.d.ts.map +1 -0
- package/dist/maps/gl/build-worker.js +44 -0
- package/dist/maps/gl/build-worker.js.map +1 -0
- package/dist/maps/gl/color.d.ts +8 -0
- package/dist/maps/gl/color.d.ts.map +1 -0
- package/dist/maps/gl/color.js +57 -0
- package/dist/maps/gl/color.js.map +1 -0
- package/dist/maps/gl/cover.d.ts +70 -0
- package/dist/maps/gl/cover.d.ts.map +1 -0
- package/dist/maps/gl/cover.js +137 -0
- package/dist/maps/gl/cover.js.map +1 -0
- package/dist/maps/gl/index.d.ts +3 -0
- package/dist/maps/gl/index.d.ts.map +1 -0
- package/dist/maps/gl/index.js +20 -0
- package/dist/maps/gl/index.js.map +1 -0
- package/dist/maps/gl/markers.d.ts +28 -0
- package/dist/maps/gl/markers.d.ts.map +1 -0
- package/dist/maps/gl/markers.js +60 -0
- package/dist/maps/gl/markers.js.map +1 -0
- package/dist/maps/gl/overlays.d.ts +68 -0
- package/dist/maps/gl/overlays.d.ts.map +1 -0
- package/dist/maps/gl/overlays.js +300 -0
- package/dist/maps/gl/overlays.js.map +1 -0
- package/dist/maps/gl/pane.d.ts +12 -0
- package/dist/maps/gl/pane.d.ts.map +1 -0
- package/dist/maps/gl/pane.js +37 -0
- package/dist/maps/gl/pane.js.map +1 -0
- package/dist/maps/gl/placement.d.ts +124 -0
- package/dist/maps/gl/placement.d.ts.map +1 -0
- package/dist/maps/gl/placement.js +642 -0
- package/dist/maps/gl/placement.js.map +1 -0
- package/dist/maps/gl/renderer.d.ts +384 -0
- package/dist/maps/gl/renderer.d.ts.map +1 -0
- package/dist/maps/gl/renderer.js +1184 -0
- package/dist/maps/gl/renderer.js.map +1 -0
- package/dist/maps/gl/shaders.d.ts +121 -0
- package/dist/maps/gl/shaders.d.ts.map +1 -0
- package/dist/maps/gl/shaders.js +453 -0
- package/dist/maps/gl/shaders.js.map +1 -0
- package/dist/maps/gl/store.d.ts +125 -0
- package/dist/maps/gl/store.d.ts.map +1 -0
- package/dist/maps/gl/store.js +512 -0
- package/dist/maps/gl/store.js.map +1 -0
- package/dist/maps/gl/text.d.ts +283 -0
- package/dist/maps/gl/text.d.ts.map +1 -0
- package/dist/maps/gl/text.js +698 -0
- package/dist/maps/gl/text.js.map +1 -0
- package/dist/maps/gl/view.d.ts +345 -0
- package/dist/maps/gl/view.d.ts.map +1 -0
- package/dist/maps/gl/view.js +1209 -0
- package/dist/maps/gl/view.js.map +1 -0
- package/dist/maps/icons.d.ts +32 -0
- package/dist/maps/icons.d.ts.map +1 -0
- package/dist/maps/icons.js +151 -0
- package/dist/maps/icons.js.map +1 -0
- package/dist/maps/index.d.ts +3 -1
- package/dist/maps/index.d.ts.map +1 -1
- package/dist/maps/index.js +218 -52
- package/dist/maps/index.js.map +1 -1
- package/dist/maps/labels.d.ts +91 -18
- package/dist/maps/labels.d.ts.map +1 -1
- package/dist/maps/labels.js +376 -140
- package/dist/maps/labels.js.map +1 -1
- package/dist/maps/node.d.ts +179 -56
- package/dist/maps/node.d.ts.map +1 -1
- package/dist/maps/node.js +663 -537
- package/dist/maps/node.js.map +1 -1
- package/dist/maps/overlay.d.ts +72 -9
- package/dist/maps/overlay.d.ts.map +1 -1
- package/dist/maps/overlay.js +108 -28
- package/dist/maps/overlay.js.map +1 -1
- package/dist/maps/paint.d.ts +3 -0
- package/dist/maps/paint.d.ts.map +1 -1
- package/dist/maps/paint.js.map +1 -1
- package/dist/maps/proj.d.ts +62 -0
- package/dist/maps/proj.d.ts.map +1 -1
- package/dist/maps/proj.js +101 -0
- package/dist/maps/proj.js.map +1 -1
- package/dist/maps/renderer.d.ts +69 -0
- package/dist/maps/renderer.d.ts.map +1 -0
- package/dist/maps/renderer.js +66 -0
- package/dist/maps/renderer.js.map +1 -0
- package/dist/maps/sources.d.ts +30 -6
- package/dist/maps/sources.d.ts.map +1 -1
- package/dist/maps/sources.js +22 -3
- package/dist/maps/sources.js.map +1 -1
- package/dist/maps/style.d.ts +35 -0
- package/dist/maps/style.d.ts.map +1 -1
- package/dist/maps/style.js.map +1 -1
- package/dist/maps/styles.d.ts +12 -0
- package/dist/maps/styles.d.ts.map +1 -1
- package/dist/maps/styles.js +120 -6
- package/dist/maps/styles.js.map +1 -1
- package/dist/maps/theme.d.ts +19 -0
- package/dist/maps/theme.d.ts.map +1 -0
- package/dist/maps/theme.js +44 -0
- package/dist/maps/theme.js.map +1 -0
- package/dist/maps/tiles.d.ts +147 -27
- package/dist/maps/tiles.d.ts.map +1 -1
- package/dist/maps/tiles.js +221 -54
- package/dist/maps/tiles.js.map +1 -1
- package/dist/maps/types.d.ts +213 -21
- package/dist/maps/types.d.ts.map +1 -1
- package/dist/markdown/expressions.d.ts +1 -1
- package/dist/markdown/expressions.d.ts.map +1 -1
- package/dist/markdown/expressions.js.map +1 -1
- package/dist/markdown/index.d.ts +7 -6
- package/dist/markdown/index.d.ts.map +1 -1
- package/dist/markdown/index.js +121 -50
- package/dist/markdown/index.js.map +1 -1
- package/dist/markdown/spans.d.ts +1 -1
- package/dist/markdown/spans.d.ts.map +1 -1
- package/dist/markdown/spans.js +6 -0
- package/dist/markdown/spans.js.map +1 -1
- package/dist/media-player/index.d.ts +11 -3
- package/dist/media-player/index.d.ts.map +1 -1
- package/dist/media-player/index.js +13 -2
- package/dist/media-player/index.js.map +1 -1
- package/dist/qml/containers.d.ts.map +1 -1
- package/dist/qml/containers.js +6 -1
- package/dist/qml/containers.js.map +1 -1
- package/dist/qml/interaction.d.ts.map +1 -1
- package/dist/qml/interaction.js +38 -5
- package/dist/qml/interaction.js.map +1 -1
- package/dist/qml/layouts.d.ts.map +1 -1
- package/dist/qml/layouts.js +21 -6
- package/dist/qml/layouts.js.map +1 -1
- package/dist/qml/qtquick.d.ts.map +1 -1
- package/dist/qml/qtquick.js +9 -2
- package/dist/qml/qtquick.js.map +1 -1
- package/dist/qml/react.d.ts.map +1 -1
- package/dist/qml/react.js +9 -7
- package/dist/qml/react.js.map +1 -1
- package/dist/reorder/clock.d.ts +7 -0
- package/dist/reorder/clock.d.ts.map +1 -0
- package/dist/reorder/clock.js +26 -0
- package/dist/reorder/clock.js.map +1 -0
- package/dist/reorder/index.d.ts +25 -3
- package/dist/reorder/index.d.ts.map +1 -1
- package/dist/reorder/index.js +64 -19
- package/dist/reorder/index.js.map +1 -1
- package/dist/rich-text-editor/clipboard.d.ts +23 -0
- package/dist/rich-text-editor/clipboard.d.ts.map +1 -0
- package/dist/rich-text-editor/clipboard.js +307 -0
- package/dist/rich-text-editor/clipboard.js.map +1 -0
- package/dist/rich-text-editor/collab.d.ts +32 -0
- package/dist/rich-text-editor/collab.d.ts.map +1 -0
- package/dist/rich-text-editor/collab.js +55 -0
- package/dist/rich-text-editor/collab.js.map +1 -0
- package/dist/rich-text-editor/commands.d.ts +50 -0
- package/dist/rich-text-editor/commands.d.ts.map +1 -0
- package/dist/rich-text-editor/commands.js +376 -0
- package/dist/rich-text-editor/commands.js.map +1 -0
- package/dist/rich-text-editor/drag.d.ts +68 -0
- package/dist/rich-text-editor/drag.d.ts.map +1 -0
- package/dist/rich-text-editor/drag.js +148 -0
- package/dist/rich-text-editor/drag.js.map +1 -0
- package/dist/rich-text-editor/html.d.ts +145 -0
- package/dist/rich-text-editor/html.d.ts.map +1 -0
- package/dist/rich-text-editor/html.js +516 -0
- package/dist/rich-text-editor/html.js.map +1 -0
- package/dist/rich-text-editor/index.d.ts +252 -0
- package/dist/rich-text-editor/index.d.ts.map +1 -0
- package/dist/rich-text-editor/index.js +856 -0
- package/dist/rich-text-editor/index.js.map +1 -0
- package/dist/rich-text-editor/inline.d.ts +94 -0
- package/dist/rich-text-editor/inline.d.ts.map +1 -0
- package/dist/rich-text-editor/inline.js +252 -0
- package/dist/rich-text-editor/inline.js.map +1 -0
- package/dist/rich-text-editor/keymap.d.ts +71 -0
- package/dist/rich-text-editor/keymap.d.ts.map +1 -0
- package/dist/rich-text-editor/keymap.js +394 -0
- package/dist/rich-text-editor/keymap.js.map +1 -0
- package/dist/rich-text-editor/keys.d.ts +40 -0
- package/dist/rich-text-editor/keys.d.ts.map +1 -0
- package/dist/rich-text-editor/keys.js +202 -0
- package/dist/rich-text-editor/keys.js.map +1 -0
- package/dist/rich-text-editor/look.d.ts +71 -0
- package/dist/rich-text-editor/look.d.ts.map +1 -0
- package/dist/rich-text-editor/look.js +342 -0
- package/dist/rich-text-editor/look.js.map +1 -0
- package/dist/rich-text-editor/markdown.d.ts +18 -0
- package/dist/rich-text-editor/markdown.d.ts.map +1 -0
- package/dist/rich-text-editor/markdown.js +496 -0
- package/dist/rich-text-editor/markdown.js.map +1 -0
- package/dist/rich-text-editor/marks.d.ts +9 -0
- package/dist/rich-text-editor/marks.d.ts.map +1 -0
- package/dist/rich-text-editor/marks.js +83 -0
- package/dist/rich-text-editor/marks.js.map +1 -0
- package/dist/rich-text-editor/nodes.d.ts +135 -0
- package/dist/rich-text-editor/nodes.d.ts.map +1 -0
- package/dist/rich-text-editor/nodes.js +397 -0
- package/dist/rich-text-editor/nodes.js.map +1 -0
- package/dist/rich-text-editor/render.d.ts +68 -0
- package/dist/rich-text-editor/render.d.ts.map +1 -0
- package/dist/rich-text-editor/render.js +518 -0
- package/dist/rich-text-editor/render.js.map +1 -0
- package/dist/rich-text-editor/schema.d.ts +16 -0
- package/dist/rich-text-editor/schema.d.ts.map +1 -0
- package/dist/rich-text-editor/schema.js +386 -0
- package/dist/rich-text-editor/schema.js.map +1 -0
- package/dist/rich-text-editor/store.d.ts +11 -0
- package/dist/rich-text-editor/store.d.ts.map +1 -0
- package/dist/rich-text-editor/store.js +43 -0
- package/dist/rich-text-editor/store.js.map +1 -0
- package/dist/rich-text-editor/suggest.d.ts +127 -0
- package/dist/rich-text-editor/suggest.d.ts.map +1 -0
- package/dist/rich-text-editor/suggest.js +0 -0
- package/dist/rich-text-editor/suggest.js.map +1 -0
- package/dist/rich-text-editor/tables.d.ts +41 -0
- package/dist/rich-text-editor/tables.d.ts.map +1 -0
- package/dist/rich-text-editor/tables.js +204 -0
- package/dist/rich-text-editor/tables.js.map +1 -0
- package/dist/rich-text-editor/toolbar.d.ts +58 -0
- package/dist/rich-text-editor/toolbar.d.ts.map +1 -0
- package/dist/rich-text-editor/toolbar.js +548 -0
- package/dist/rich-text-editor/toolbar.js.map +1 -0
- package/dist/rich-text-editor/view.d.ts +368 -0
- package/dist/rich-text-editor/view.d.ts.map +1 -0
- package/dist/rich-text-editor/view.js +1968 -0
- package/dist/rich-text-editor/view.js.map +1 -0
- package/dist/rich-text-editor/virtual.d.ts +54 -0
- package/dist/rich-text-editor/virtual.d.ts.map +1 -0
- package/dist/rich-text-editor/virtual.js +279 -0
- package/dist/rich-text-editor/virtual.js.map +1 -0
- package/dist/richtext/node.d.ts +71 -4
- package/dist/richtext/node.d.ts.map +1 -1
- package/dist/richtext/node.js +233 -28
- package/dist/richtext/node.js.map +1 -1
- package/dist/richtext/runs.d.ts +13 -8
- package/dist/richtext/runs.d.ts.map +1 -1
- package/dist/richtext/runs.js +18 -8
- package/dist/richtext/runs.js.map +1 -1
- package/dist/table/index.d.ts.map +1 -1
- package/dist/table/index.js +23 -0
- package/dist/table/index.js.map +1 -1
- package/dist/table/rows.d.ts +3 -2
- package/dist/table/rows.d.ts.map +1 -1
- package/dist/table/rows.js +20 -6
- package/dist/table/rows.js.map +1 -1
- package/dist/terminal/backends.d.ts +4 -1
- package/dist/terminal/backends.d.ts.map +1 -1
- package/dist/terminal/backends.js.map +1 -1
- package/dist/terminal/index.d.ts +18 -7
- package/dist/terminal/index.d.ts.map +1 -1
- package/dist/terminal/index.js +32 -11
- package/dist/terminal/index.js.map +1 -1
- package/dist/terminal/vt/pty.d.ts +13 -2
- package/dist/terminal/vt/pty.d.ts.map +1 -1
- package/dist/terminal/vt/pty.js +18 -3
- package/dist/terminal/vt/pty.js.map +1 -1
- package/dist/terminal-output/index.d.ts.map +1 -1
- package/dist/terminal-output/index.js +6 -1
- package/dist/terminal-output/index.js.map +1 -1
- package/dist/three/canvas.d.ts.map +1 -1
- package/dist/three/canvas.js +85 -7
- package/dist/three/canvas.js.map +1 -1
- package/dist/three/events.d.ts +22 -4
- package/dist/three/events.d.ts.map +1 -1
- package/dist/three/events.js +34 -4
- package/dist/three/events.js.map +1 -1
- package/dist/tree/index.d.ts.map +1 -1
- package/dist/tree/index.js +17 -0
- package/dist/tree/index.js.map +1 -1
- package/package.json +59 -31
- package/src/charts/node.ts +68 -28
- package/src/charts/render.ts +120 -30
- package/src/code/index.ts +3 -1
- package/src/code-editor/completion.ts +3 -2
- package/src/code-editor/index.ts +19 -0
- package/src/code-editor/node.ts +1574 -184
- package/src/code-language/languages/glsl.ts +12 -2
- package/src/code-language/languages/javascript.ts +21 -4
- package/src/code-language/languages/json.ts +12 -2
- package/src/code-language/languages/shell.ts +12 -2
- package/src/code-language/languages/sql.ts +22 -2
- package/src/code-language/stream.ts +257 -17
- package/src/code-language/types.ts +5 -4
- package/src/codeblock/index.ts +20 -3
- package/src/embed/client.ts +102 -8
- package/src/embed/index.ts +2 -0
- package/src/flow/draw.ts +167 -41
- package/src/flow/gl/color.ts +85 -0
- package/src/flow/gl/index.ts +342 -0
- package/src/flow/gl/pack.ts +585 -0
- package/src/flow/gl/renderer.ts +679 -0
- package/src/flow/gl/shaders.ts +371 -0
- package/src/flow/gl/text.ts +1295 -0
- package/src/flow/index.ts +794 -67
- package/src/flow/node.ts +2496 -1256
- package/src/flow/paint.ts +511 -0
- package/src/flow/paths.ts +80 -15
- package/src/flow/scene.ts +2199 -0
- package/src/flow/types.ts +113 -1
- package/src/html/controls.ts +73 -2
- package/src/html/css/calc.ts +385 -0
- package/src/html/css/cascade.ts +1403 -62
- package/src/html/css/color.ts +755 -0
- package/src/html/css/content.ts +417 -0
- package/src/html/css/counter-styles.ts +973 -0
- package/src/html/css/decode.ts +130 -0
- package/src/html/css/parse.ts +1514 -160
- package/src/html/css/style.ts +4102 -222
- package/src/html/css/ua.ts +58 -12
- package/src/html/css/values.ts +150 -50
- package/src/html/css/vars.ts +348 -0
- package/src/html/dom.ts +284 -17
- package/src/html/fonts.ts +641 -0
- package/src/html/gif.ts +233 -0
- package/src/html/index.ts +180 -20
- package/src/html/layout/block.ts +3456 -230
- package/src/html/layout/boxes.ts +2100 -190
- package/src/html/layout/cache.ts +289 -0
- package/src/html/layout/collapse.ts +317 -0
- package/src/html/layout/css-grid.ts +1063 -0
- package/src/html/layout/flex.ts +1115 -106
- package/src/html/layout/floats.ts +128 -14
- package/src/html/layout/grid-lines.ts +141 -0
- package/src/html/layout/grid.ts +181 -0
- package/src/html/layout/inline.ts +3943 -192
- package/src/html/layout/shaping.ts +140 -0
- package/src/html/layout/table.ts +696 -186
- package/src/html/layout/tracks.ts +437 -0
- package/src/html/node.ts +1486 -121
- package/src/html/paint.ts +4293 -169
- package/src/html/resources.ts +161 -50
- package/src/html/surfaces.ts +81 -0
- package/src/html/svg.ts +493 -0
- package/src/html/url.ts +86 -0
- package/src/index.ts +4 -0
- package/src/internal/collate.ts +12 -0
- package/src/{markdown → internal/markdown}/ast.ts +35 -7
- package/src/{markdown → internal/markdown}/parse.ts +288 -35
- package/src/internal/markdown/stringify.ts +772 -0
- package/src/internal/prewarm.ts +45 -0
- package/src/internal/scroll.ts +5 -3
- package/src/internal/sdf.ts +227 -0
- package/src/internal/splice.ts +25 -0
- package/src/internal/window.ts +36 -5
- package/src/maps/anchors.ts +913 -0
- package/src/maps/controller.ts +1084 -0
- package/src/maps/gl/buckets.ts +461 -0
- package/src/maps/gl/build-worker.ts +66 -0
- package/src/maps/gl/color.ts +62 -0
- package/src/maps/gl/cover.ts +205 -0
- package/src/maps/gl/index.ts +23 -0
- package/src/maps/gl/markers.ts +83 -0
- package/src/maps/gl/overlays.ts +379 -0
- package/src/maps/gl/pane.ts +42 -0
- package/src/maps/gl/placement.ts +968 -0
- package/src/maps/gl/renderer.ts +1660 -0
- package/src/maps/gl/shaders.ts +489 -0
- package/src/maps/gl/store.ts +615 -0
- package/src/maps/gl/text.ts +868 -0
- package/src/maps/gl/view.ts +1489 -0
- package/src/maps/icons.ts +183 -0
- package/src/maps/index.ts +274 -81
- package/src/maps/labels.ts +457 -144
- package/src/maps/node.ts +832 -665
- package/src/maps/overlay.ts +148 -43
- package/src/maps/paint.ts +3 -0
- package/src/maps/proj.ts +118 -0
- package/src/maps/renderer.ts +155 -0
- package/src/maps/sources.ts +43 -6
- package/src/maps/style.ts +32 -0
- package/src/maps/styles.ts +215 -4
- package/src/maps/theme.ts +51 -0
- package/src/maps/tiles.ts +253 -62
- package/src/maps/types.ts +213 -21
- package/src/markdown/expressions.ts +5 -1
- package/src/markdown/index.ts +163 -78
- package/src/markdown/spans.ts +6 -1
- package/src/media-player/index.ts +18 -4
- package/src/qml/containers.tsx +6 -1
- package/src/qml/interaction.tsx +42 -5
- package/src/qml/layouts.tsx +21 -6
- package/src/qml/qtquick.tsx +9 -2
- package/src/qml/react.tsx +7 -5
- package/src/reorder/clock.ts +27 -0
- package/src/reorder/index.ts +100 -27
- package/src/rich-text-editor/clipboard.ts +439 -0
- package/src/rich-text-editor/collab.ts +91 -0
- package/src/rich-text-editor/commands.ts +448 -0
- package/src/rich-text-editor/dom-globals.d.ts +23 -0
- package/src/rich-text-editor/drag.ts +211 -0
- package/src/rich-text-editor/html.ts +619 -0
- package/src/rich-text-editor/index.ts +1364 -0
- package/src/rich-text-editor/inline.ts +345 -0
- package/src/rich-text-editor/keymap.ts +510 -0
- package/src/rich-text-editor/keys.ts +228 -0
- package/src/rich-text-editor/look.ts +427 -0
- package/src/rich-text-editor/markdown.ts +653 -0
- package/src/rich-text-editor/marks.ts +101 -0
- package/src/rich-text-editor/nodes.ts +497 -0
- package/src/rich-text-editor/render.ts +837 -0
- package/src/rich-text-editor/schema.ts +431 -0
- package/src/rich-text-editor/store.ts +39 -0
- package/src/rich-text-editor/suggest.ts +597 -0
- package/src/rich-text-editor/tables.ts +272 -0
- package/src/rich-text-editor/toolbar.ts +724 -0
- package/src/rich-text-editor/view.ts +2287 -0
- package/src/rich-text-editor/virtual.ts +372 -0
- package/src/richtext/node.ts +294 -44
- package/src/richtext/runs.ts +23 -12
- package/src/table/index.ts +29 -0
- package/src/table/rows.ts +18 -8
- package/src/terminal/backends.ts +4 -1
- package/src/terminal/index.ts +47 -15
- package/src/terminal/vt/pty.ts +16 -2
- package/src/terminal-output/index.ts +6 -1
- package/src/three/canvas.ts +102 -8
- package/src/three/events.ts +49 -9
- package/src/tree/index.ts +19 -0
- package/dist/markdown/ast.d.ts.map +0 -1
- package/dist/markdown/ast.js +0 -17
- package/dist/markdown/ast.js.map +0 -1
- package/dist/markdown/parse.d.ts.map +0 -1
- package/dist/markdown/parse.js.map +0 -1
- package/dist/markdown/tags.d.ts.map +0 -1
- package/dist/markdown/tags.js.map +0 -1
- /package/dist/{markdown → internal/markdown}/parse.d.ts +0 -0
- /package/dist/{markdown → internal/markdown}/tags.d.ts +0 -0
- /package/dist/{markdown → internal/markdown}/tags.js +0 -0
- /package/src/{markdown → internal/markdown}/tags.ts +0 -0
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import { attr, childrenOf, isElement, isText, NON_RENDERED, tagOf, } from '../dom.js';
|
|
1
|
+
import { attr, childrenOf, isElement, isText, inImpliedHead, NON_RENDERED, tagOf, } from '../dom.js';
|
|
2
|
+
import { quoteAt } from '../css/content.js';
|
|
3
|
+
import { CONTAIN_STYLE, FIRST_LINE_INHERITED, copyStyle, firstLineParent, inherit, } from '../css/style.js';
|
|
4
|
+
import { AUTO } from '../css/values.js';
|
|
5
|
+
import { svgIntrinsics } from '../svg.js';
|
|
2
6
|
/**
|
|
3
7
|
* One box. Fields are assigned rather than passed so the shape stays
|
|
4
8
|
* monomorphic — every box has every field, which is what keeps the property
|
|
@@ -16,6 +20,9 @@ export class Box {
|
|
|
16
20
|
y = 0;
|
|
17
21
|
width = 0;
|
|
18
22
|
height = 0;
|
|
23
|
+
/** For an inline box broken around a block in it (CSS 2.1 9.2.1.1), the
|
|
24
|
+
* sides it goes on from, which have no edge: 1 its start, 2 its end. */
|
|
25
|
+
cut = 0;
|
|
19
26
|
/** Resolved edges — border + padding, per side. */
|
|
20
27
|
borderTop = 0;
|
|
21
28
|
borderRight = 0;
|
|
@@ -29,6 +36,34 @@ export class Box {
|
|
|
29
36
|
marginRight = 0;
|
|
30
37
|
marginBottom = 0;
|
|
31
38
|
marginLeft = 0;
|
|
39
|
+
/** The margin that came out through the box's bottom edge from its last
|
|
40
|
+
* children, collapsed with its own bottom margin, which is their sum:
|
|
41
|
+
* kept as a strut for the next sibling's to collapse with. Null where
|
|
42
|
+
* none came out, which is most boxes. Set by the block layout. */
|
|
43
|
+
bottomStrut = null;
|
|
44
|
+
/** An inline box with a background or a border to paint behind its
|
|
45
|
+
* fragments: the ascent and descent of its own face, the height CSS
|
|
46
|
+
* paints them over (10.6.1). Set by the inline layout. */
|
|
47
|
+
decoration = null;
|
|
48
|
+
/** What a percentage `height` resolves against: an absolutely positioned
|
|
49
|
+
* box's containing block's height, which is known before the box is laid
|
|
50
|
+
* out (CSS 2.1 10.5). NaN everywhere else, where that height depends on
|
|
51
|
+
* the content and a percentage is `auto`. */
|
|
52
|
+
percentHeightBase = NaN;
|
|
53
|
+
/** Whether this box last handed its children a percentage base that
|
|
54
|
+
* was a number: until it does, they hold NaN already, and the next
|
|
55
|
+
* pass need not visit them to say so again. */
|
|
56
|
+
gavePercentBase = false;
|
|
57
|
+
/** Whether this box's top margin collapsed through its parent's top edge
|
|
58
|
+
* and was spent placing the parent (CSS 2.1 8.3.1): its own layout puts
|
|
59
|
+
* it at the parent's content top, and applies no margin again. `2` when
|
|
60
|
+
* the box is empty and its bottom margin went the same way, so the next
|
|
61
|
+
* sibling's may have too; `3` for a new formatting context the margin
|
|
62
|
+
* stopped above, separated from the floats it carries, which sits where
|
|
63
|
+
* the margins before it end and applies none of its own. Set by the walk
|
|
64
|
+
* that places the parent, each pass, and read only while every sibling
|
|
65
|
+
* before it was a `2`. */
|
|
66
|
+
topAbsorbed = 0;
|
|
32
67
|
/** Text, for a `text` box: already whitespace-processed and transformed. */
|
|
33
68
|
text = '';
|
|
34
69
|
/** This box's slice of the document text index. */
|
|
@@ -36,11 +71,16 @@ export class Box {
|
|
|
36
71
|
textEnd = 0;
|
|
37
72
|
/** Lines, for a box that established an inline formatting context. */
|
|
38
73
|
lines = null;
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
|
|
74
|
+
/** The widest row of floats its lines placed beside none of them, side
|
|
75
|
+
* by side: what the box is measured with, as a line's floats are
|
|
76
|
+
* (`LineBox.floats`). */
|
|
77
|
+
floatRow = 0;
|
|
78
|
+
/** A replaced box's intrinsic size and ratio; null on every other box,
|
|
79
|
+
* which is nearly all of them, so it is one field rather than four. */
|
|
80
|
+
intrinsic = null;
|
|
42
81
|
/**
|
|
43
|
-
* Cached min-/max-content widths, for a table cell
|
|
82
|
+
* Cached min-/max-content widths, for a table cell and for a box an
|
|
83
|
+
* intrinsic size (`width: fit-content`) sizes. -1 until measured.
|
|
44
84
|
*
|
|
45
85
|
* Intrinsic widths are width-independent by definition, so measuring them
|
|
46
86
|
* per layout pass was this engine breaking its own phase rule — a resize
|
|
@@ -51,17 +91,23 @@ export class Box {
|
|
|
51
91
|
*/
|
|
52
92
|
intrinsicMinContent = -1;
|
|
53
93
|
intrinsicMaxContent = -1;
|
|
94
|
+
/** Which layout of the pass this box last had, counted across all boxes:
|
|
95
|
+
* what tells a layout kept for reuse (`layoutOwn`) from one a probe has
|
|
96
|
+
* laid over since. */
|
|
97
|
+
layoutSerial = 0;
|
|
54
98
|
/** What a replaced box is: the resource seam and the control host both
|
|
55
99
|
* key on this rather than re-reading the tag. */
|
|
56
100
|
replaced = 'none';
|
|
57
|
-
/** The marker
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
101
|
+
/** The marker of a `list-item`, if it generated one. The marker is not a
|
|
102
|
+
* box: it is not in the flow, nothing can select it (CSS spells that
|
|
103
|
+
* `::marker`, and no author styles it here), and giving it one would put
|
|
104
|
+
* a bullet in every copied list. */
|
|
105
|
+
marker = null;
|
|
106
|
+
/** Which of an element's pseudo-elements this box is. Its `el` is null —
|
|
107
|
+
* it is no element — and its text box's is the element it hangs off, so
|
|
108
|
+
* a click on a link's generated text or first letter is a click on the
|
|
109
|
+
* link. */
|
|
110
|
+
pseudo = null;
|
|
65
111
|
/**
|
|
66
112
|
* The bounds of everything this box and its descendants draw, in document
|
|
67
113
|
* coordinates — **ink** bounds, not the border box, because `overflow:
|
|
@@ -86,6 +132,10 @@ export class Box {
|
|
|
86
132
|
/** Out-of-flow children in paint order (z-index, then document order),
|
|
87
133
|
* precomputed so a paint does not filter and sort per box per frame. */
|
|
88
134
|
positionedPaint = null;
|
|
135
|
+
/** Whether a positioned box is somewhere under this one: what the pass
|
|
136
|
+
* that gives each stacking context its positioned boxes walks, so that
|
|
137
|
+
* a subtree with none is not walked at all (`stackLayers`). */
|
|
138
|
+
holdsLayers = false;
|
|
89
139
|
/** The tallest line box under this box — the slack a binary search over
|
|
90
140
|
* the y-sorted lines needs, since a line's bottom is not monotone. */
|
|
91
141
|
maxLineHeight = 0;
|
|
@@ -102,6 +152,20 @@ export class Box {
|
|
|
102
152
|
outOfFlow = false;
|
|
103
153
|
/** Set on a float, for the same reason. */
|
|
104
154
|
isFloat = false;
|
|
155
|
+
/** Where an out-of-flow box would have been in the flow it was taken
|
|
156
|
+
* from — its static position (CSS 2.1 10.3.7, 10.6.4). */
|
|
157
|
+
staticPosition = null;
|
|
158
|
+
/** A table's captions, above and below it: they are in the box's height,
|
|
159
|
+
* and outside the table's own border and background (CSS 2.1 17.4). */
|
|
160
|
+
captionTop = 0;
|
|
161
|
+
captionBottom = 0;
|
|
162
|
+
/** A table whose borders collapse: the border each segment of its grid
|
|
163
|
+
* carries, resolved once per build (`collapseTable`). */
|
|
164
|
+
collapsed = null;
|
|
165
|
+
/** Set on a table whose borders collapse, and on its cells: their border
|
|
166
|
+
* widths are the halves the collapsing model leaves them, and the table
|
|
167
|
+
* paints the borders rather than the boxes. */
|
|
168
|
+
bordersCollapsed = false;
|
|
105
169
|
constructor(kind, el, style) {
|
|
106
170
|
this.kind = kind;
|
|
107
171
|
this.el = el;
|
|
@@ -141,6 +205,65 @@ export class Box {
|
|
|
141
205
|
return this.borderTop + this.borderBottom + this.padTop + this.padBottom;
|
|
142
206
|
}
|
|
143
207
|
}
|
|
208
|
+
/** The `::first-line` style of each block container a rule gives one to,
|
|
209
|
+
* kept beside the boxes: few documents have any (`BoxTree.firstLine`). */
|
|
210
|
+
export const FIRST_LINE = new WeakMap();
|
|
211
|
+
/** The url of an image generated content put in a pseudo-element: it has
|
|
212
|
+
* no element of its own to name it (`imageUrlOf`). */
|
|
213
|
+
export const CONTENT_IMAGES = new WeakMap();
|
|
214
|
+
/** How far `position: relative` moved an inline box (`offsetInline`), how
|
|
215
|
+
* far each text on a line moved with the boxes it is in, and the lines
|
|
216
|
+
* holding one: beside the boxes and the lines, since few documents move an
|
|
217
|
+
* inline box. A moved text's `drawX` and `drawY` are where it is drawn;
|
|
218
|
+
* its box's decorations are worked out from where it was, and moved by
|
|
219
|
+
* their own box's offset. */
|
|
220
|
+
export const INLINE_OFFSETS = new WeakMap();
|
|
221
|
+
export const TEXT_SHIFTS = new WeakMap();
|
|
222
|
+
export const SHIFTED_LINES = new WeakSet();
|
|
223
|
+
/** How far `vertical-align` raised a text above its line's baseline, and an
|
|
224
|
+
* inline box's own baseline, where it did: beside the lines and the boxes
|
|
225
|
+
* for the reason the offsets above are. A line holding a raised text is
|
|
226
|
+
* among `SHIFTED_LINES` too, as its text is off the line's baseline. */
|
|
227
|
+
export const TEXT_RAISES = new WeakMap();
|
|
228
|
+
/** The blocks some of whose text casts a shadow (`text-shadow`), which the
|
|
229
|
+
* paint pass looks for in them and in no other. */
|
|
230
|
+
export const SHADOWED_TEXT = new WeakSet();
|
|
231
|
+
export const BOX_RAISES = new WeakMap();
|
|
232
|
+
/** And the raise, on one line, of each inline box a `top` or `bottom` box
|
|
233
|
+
* holds, the box itself among them: that box's baseline is where the
|
|
234
|
+
* line's edge puts it, which is a different height on every line, so its
|
|
235
|
+
* background, drawn from its baseline, is put there by line. */
|
|
236
|
+
export const LINE_BOX_RAISES = new WeakMap();
|
|
237
|
+
/** The table cells wholly in columns `visibility: collapse` took out of
|
|
238
|
+
* their table (CSS 2.1 17.5.5), which the paint pass leaves out: a cell is
|
|
239
|
+
* no descendant of its column and inherits nothing from it, so its own
|
|
240
|
+
* style still says `visible`. */
|
|
241
|
+
export const COLLAPSED_CELLS = new WeakSet();
|
|
242
|
+
/** And the cells spanning into a column or a row taken out: laid out as
|
|
243
|
+
* they would have been, placed over what is left of their span, and
|
|
244
|
+
* clipped to it. */
|
|
245
|
+
export const CLIPPED_CELLS = new WeakSet();
|
|
246
|
+
/** The boxes after a line-clamp container's clamp point (CSS Overflow 4,
|
|
247
|
+
* 5.3.1): invisible, with all they hold, and no taller than nothing to
|
|
248
|
+
* the blocks around them. Marked by the layout of the flow they are in,
|
|
249
|
+
* which leaves them where the last layout put them, and passed over by
|
|
250
|
+
* paint and the paint bounds. */
|
|
251
|
+
export const CLAMPED = new WeakSet();
|
|
252
|
+
export const GRID_TRACKS = new WeakMap();
|
|
253
|
+
/** A flex box's children in the order it lays them out and paints them,
|
|
254
|
+
* `order` first and the document's after it (CSS Flexbox 5.4), where
|
|
255
|
+
* `order` moves any: an absolutely positioned child's is 0. */
|
|
256
|
+
export const PAINT_ORDER = new WeakMap();
|
|
257
|
+
/** The blocks that broke a relatively positioned inline box in pieces,
|
|
258
|
+
* under its first piece: its offset moves them too (CSS 2.1 9.2.1.1),
|
|
259
|
+
* though they stand outside it (`breakAround`). */
|
|
260
|
+
export const CUT_BLOCKS = new WeakMap();
|
|
261
|
+
/** The blocks that broke an inline box under full opacity in pieces, and
|
|
262
|
+
* the opacity they take from it — from each such inline box around them,
|
|
263
|
+
* multiplied. They stand outside it (CSS 2.1 9.2.1.1) and are still its
|
|
264
|
+
* content, which its opacity fades as a group: a green square in a
|
|
265
|
+
* `<span style="opacity: .5">` was drawn at full strength. */
|
|
266
|
+
export const FADED_BLOCKS = new WeakMap();
|
|
144
267
|
/** Build the box tree for a document. */
|
|
145
268
|
export function buildBoxes(root, options) {
|
|
146
269
|
const builder = new Builder(options);
|
|
@@ -153,66 +276,247 @@ export function buildBoxes(root, options) {
|
|
|
153
276
|
* output, a runaway template) from a stack overflow five phases later.
|
|
154
277
|
* Blink's parser flattens at 512 for the same reason; content past the cap
|
|
155
278
|
* is dropped, which beats the alternative of crashing the application.
|
|
279
|
+
* The parser keeps a document to 256 elements (`dom.ts`), so what reaches
|
|
280
|
+
* this now is the anonymous boxes a table builds round each of them.
|
|
156
281
|
*/
|
|
157
282
|
const MAX_DEPTH = 512;
|
|
283
|
+
/** The sharing key the root box's children share under: one root style per
|
|
284
|
+
* build, and the shared styles last a build (`Cascade.beginSharing`). */
|
|
285
|
+
const ROOT_SHARE_KEY = 0;
|
|
158
286
|
class Builder {
|
|
159
287
|
_options;
|
|
160
|
-
|
|
288
|
+
/** The document text, in the pieces it was pushed in — joined once at the
|
|
289
|
+
* end, so that taking back a line's last space is not a copy of it. */
|
|
290
|
+
_chunks = [];
|
|
291
|
+
_length = 0;
|
|
161
292
|
_textBoxes = [];
|
|
293
|
+
_textStyles = new Set();
|
|
294
|
+
_styles = new Map();
|
|
295
|
+
/** The last text box's style: runs of text share their parent's, so
|
|
296
|
+
* most text boxes repeat it and add nothing to `_textStyles`. */
|
|
297
|
+
_lastTextStyle = null;
|
|
162
298
|
_controls = [];
|
|
163
299
|
_links = [];
|
|
164
|
-
|
|
165
|
-
|
|
300
|
+
_backgrounds = [];
|
|
301
|
+
_contentImages = [];
|
|
302
|
+
_relative = false;
|
|
303
|
+
_negative = false;
|
|
304
|
+
_firstLine = false;
|
|
305
|
+
_nestedOutOfLine = false;
|
|
306
|
+
_movedInline = false;
|
|
307
|
+
_clipText = false;
|
|
308
|
+
/** The CSS counters in scope, for `counter()` in generated content. */
|
|
309
|
+
_scopes = new CounterScopes();
|
|
310
|
+
/** How many quotes generated content has opened and not closed. */
|
|
311
|
+
_quoteDepth = 0;
|
|
312
|
+
/** Where the inline content being built stands, for collapsing white
|
|
313
|
+
* space across element boundaries. */
|
|
314
|
+
_ws = 'start';
|
|
315
|
+
/** Whether the word the inline content being built is in has had its
|
|
316
|
+
* first letter, for `text-transform: capitalize`: a word runs on across
|
|
317
|
+
* element boundaries, as white space collapses across them. */
|
|
318
|
+
_lettered = false;
|
|
166
319
|
_depth = 0;
|
|
320
|
+
/** The `::first-letter` whose letter is still to come, for the block
|
|
321
|
+
* container whose first line has not begun. Null when there is none,
|
|
322
|
+
* and once anything but a letter begins that line. */
|
|
323
|
+
_firstLetter = null;
|
|
167
324
|
constructor(options) {
|
|
168
325
|
this._options = options;
|
|
169
326
|
}
|
|
170
327
|
run(root) {
|
|
171
328
|
const cascade = this._options.cascade;
|
|
172
|
-
|
|
329
|
+
cascade.beginSharing();
|
|
330
|
+
const rootStyle = cascade.rootStyle(hasBody(root), hasHtml(root));
|
|
173
331
|
const rootBox = new Box('block', null, rootStyle);
|
|
332
|
+
// a fragment's root stands in for a `<body>`, counters and all
|
|
333
|
+
this._scopes.open();
|
|
334
|
+
this._counterChanges(rootStyle, {
|
|
335
|
+
node: root,
|
|
336
|
+
style: rootStyle,
|
|
337
|
+
key: ROOT_SHARE_KEY,
|
|
338
|
+
parent: null,
|
|
339
|
+
});
|
|
174
340
|
// The DOM's `<html>`/`<body>` are ordinary elements with ordinary styles;
|
|
175
341
|
// the box above them exists only to be the initial containing block, so
|
|
176
342
|
// it carries no margins of its own and cannot collapse with anything.
|
|
177
|
-
this._children(root, rootBox, rootStyle, false);
|
|
178
|
-
|
|
343
|
+
this._children(root, rootBox, rootStyle, false, null, ROOT_SHARE_KEY);
|
|
344
|
+
this._endLine();
|
|
345
|
+
fixUp(rootBox, anonymousStyles(cascade.initial));
|
|
179
346
|
assignSubtreeRanges(rootBox);
|
|
180
347
|
return {
|
|
181
348
|
root: rootBox,
|
|
182
|
-
text: this.
|
|
349
|
+
text: this._chunks.join(''),
|
|
183
350
|
textBoxes: this._textBoxes,
|
|
351
|
+
textStyles: this._textStyles,
|
|
352
|
+
styles: this._styles,
|
|
184
353
|
controls: this._controls,
|
|
185
354
|
links: this._links,
|
|
355
|
+
backgrounds: this._backgrounds,
|
|
356
|
+
contentImages: this._contentImages,
|
|
357
|
+
relative: this._relative || isRelative(rootStyle),
|
|
358
|
+
negative: this._negative,
|
|
359
|
+
firstLine: this._firstLine,
|
|
360
|
+
firstLineStyler: this._firstLine ? this._firstLineStyler() : null,
|
|
361
|
+
nestedOutOfLine: this._nestedOutOfLine,
|
|
362
|
+
movedInline: this._movedInline,
|
|
363
|
+
clipText: this._clipText,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
/** `BoxTree.firstLineStyler`, over this build's cascade. */
|
|
367
|
+
_firstLineStyler() {
|
|
368
|
+
const cascade = this._options.cascade;
|
|
369
|
+
const styles = this._styles;
|
|
370
|
+
return (box, parent) => {
|
|
371
|
+
const up = box.parent?.style ?? box.style;
|
|
372
|
+
const inherits = firstLineParent(up, parent);
|
|
373
|
+
const el = box.kind === 'text' ? null : box.el;
|
|
374
|
+
const own = el ? styles.get(el) : undefined;
|
|
375
|
+
if (el && own && own.style === box.style) {
|
|
376
|
+
return cascade.styleFor(el, inherits, own.inFlex);
|
|
377
|
+
}
|
|
378
|
+
// text, an anonymous box, a pseudo-element's: no rules of its own to
|
|
379
|
+
// be asked again, so what it took from its parent it takes from the
|
|
380
|
+
// parent's style on the line, and what it set it keeps
|
|
381
|
+
const out = copyStyle(box.style);
|
|
382
|
+
const to = out;
|
|
383
|
+
const mine = box.style;
|
|
384
|
+
const theirs = up;
|
|
385
|
+
const line = inherits;
|
|
386
|
+
for (const name of FIRST_LINE_INHERITED) {
|
|
387
|
+
if (mine[name] === theirs[name])
|
|
388
|
+
to[name] = line[name];
|
|
389
|
+
}
|
|
390
|
+
return out;
|
|
186
391
|
};
|
|
187
392
|
}
|
|
188
393
|
/** Build boxes for a parent's children into `into`. */
|
|
189
|
-
_children(node, into, parentStyle, inFlex, owner
|
|
394
|
+
_children(node, into, parentStyle, inFlex, owner, parentKey) {
|
|
395
|
+
// CSS 2.1 17.2.1: a column group holds columns, and anything else in it
|
|
396
|
+
// is not rendered
|
|
397
|
+
const onlyColumns = parentStyle.display === 'table-column-group';
|
|
398
|
+
// A closed `<details>` shows its first `<summary>` and nothing else:
|
|
399
|
+
// HTML renders the rest into a slot that is out of the box tree until
|
|
400
|
+
// the element is `open`.
|
|
401
|
+
const closed = owner !== null &&
|
|
402
|
+
tagOf(owner) === 'details' &&
|
|
403
|
+
owner.attribs.open === undefined;
|
|
404
|
+
let summarised = false;
|
|
190
405
|
for (const child of childrenOf(node)) {
|
|
406
|
+
if (closed) {
|
|
407
|
+
if (!isElement(child) || summarised || tagOf(child) !== 'summary') {
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
summarised = true;
|
|
411
|
+
}
|
|
191
412
|
if (isText(child)) {
|
|
192
|
-
|
|
413
|
+
if (!onlyColumns)
|
|
414
|
+
this._textNode(child.data, into, parentStyle, owner);
|
|
193
415
|
continue;
|
|
194
416
|
}
|
|
195
417
|
if (!isElement(child))
|
|
196
418
|
continue;
|
|
197
|
-
this._element(child, into, parentStyle, inFlex);
|
|
419
|
+
this._element(child, into, parentStyle, inFlex, parentKey, onlyColumns);
|
|
198
420
|
}
|
|
199
421
|
}
|
|
200
|
-
_element(el, into, parentStyle, inFlex) {
|
|
422
|
+
_element(el, into, parentStyle, inFlex, parentKey, onlyColumns = false) {
|
|
201
423
|
const tag = tagOf(el);
|
|
202
|
-
if (NON_RENDERED.has(tag))
|
|
424
|
+
if (NON_RENDERED.has(tag) || inImpliedHead(el, tag))
|
|
203
425
|
return;
|
|
204
|
-
|
|
426
|
+
// shared with every element that must compute the same style, which in
|
|
427
|
+
// a long document is most of them (`Cascade.sharedStyleFor`)
|
|
428
|
+
const { style, key } = this._options.cascade.sharedStyleFor(el, parentStyle, parentKey, inFlex);
|
|
429
|
+
this._styles.set(el, { style, inFlex });
|
|
205
430
|
if (style.display === 'none')
|
|
206
431
|
return;
|
|
432
|
+
if (onlyColumns && style.display !== 'table-column')
|
|
433
|
+
return;
|
|
434
|
+
// what an element counts is in scope for it and what it holds, and for
|
|
435
|
+
// what follows it where its parent has no such counter
|
|
436
|
+
// (`CounterScopes`)
|
|
437
|
+
this._scopes.open();
|
|
438
|
+
// style containment keeps what the element's subtree does to counters
|
|
439
|
+
// and quotes in it (CSS Containment 2, 3.4)
|
|
440
|
+
const contains = (style.contain & CONTAIN_STYLE) !== 0 && style.display !== 'contents';
|
|
441
|
+
const quoteDepth = this._quoteDepth;
|
|
442
|
+
if (contains)
|
|
443
|
+
this._scopes.contain();
|
|
444
|
+
this._elementIn(el, tag, style, key, into, inFlex, {
|
|
445
|
+
node: el,
|
|
446
|
+
style,
|
|
447
|
+
key,
|
|
448
|
+
parent: { style: parentStyle, key: parentKey, inFlex },
|
|
449
|
+
});
|
|
450
|
+
if (contains)
|
|
451
|
+
this._quoteDepth = quoteDepth;
|
|
452
|
+
this._scopes.close();
|
|
453
|
+
}
|
|
454
|
+
_elementIn(el, tag, style, key, into, inFlex, place) {
|
|
455
|
+
if (style.display === 'contents') {
|
|
456
|
+
// no box of its own (CSS Display 3, 2.5): its `::before`, its
|
|
457
|
+
// children and its `::after` are its parent's, in its style — the
|
|
458
|
+
// items of a flex row still, through a wrapper Tailwind's `contents`
|
|
459
|
+
// takes out — and a replaced element, which has nothing to hand on,
|
|
460
|
+
// is not rendered
|
|
461
|
+
if (tag === 'br' || replacedKind(el, tag) !== 'none')
|
|
462
|
+
return;
|
|
463
|
+
this._counterChanges(style, place);
|
|
464
|
+
this._pseudo(el, 'before', style, into);
|
|
465
|
+
this._children(el, into, style, inFlex, el, key);
|
|
466
|
+
this._pseudo(el, 'after', style, into);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
if (isRelative(style)) {
|
|
470
|
+
this._relative = true;
|
|
471
|
+
if (style.display === 'inline' && isOffset(style)) {
|
|
472
|
+
this._movedInline = true;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
// a translation is moved by the same pass
|
|
476
|
+
if (style.translate || style.transformTranslate)
|
|
477
|
+
this._relative = true;
|
|
478
|
+
if (style.backgroundClipText)
|
|
479
|
+
this._clipText = true;
|
|
480
|
+
if (style.verticalAlign !== 'baseline' && style.display === 'inline') {
|
|
481
|
+
this._movedInline = true;
|
|
482
|
+
}
|
|
483
|
+
if (isNegative(style))
|
|
484
|
+
this._negative = true;
|
|
485
|
+
// before anything else of the element's, including its `::before`,
|
|
486
|
+
// and for the element whatever box it makes (CSS 2.1 12.4)
|
|
487
|
+
this._counterChanges(style, place);
|
|
207
488
|
// `<br>` is a line break rather than a box, and it is the one element
|
|
208
489
|
// whose *absence* of a box still has to reach the inline layout.
|
|
209
490
|
if (tag === 'br') {
|
|
491
|
+
this._endLine();
|
|
492
|
+
// the first line ends with no letter on it (CSS 2.1 5.12.2)
|
|
493
|
+
this._abandonLetter();
|
|
494
|
+
if (style.clear !== 'none') {
|
|
495
|
+
// a break that clears the floats — `<br clear="all">` after a
|
|
496
|
+
// floated image — puts what follows it below them: an empty block
|
|
497
|
+
// that clears them, which the inline content around it is broken
|
|
498
|
+
// for (`breakInlines`), where a line break went on beside them
|
|
499
|
+
const clearing = copyStyle(style);
|
|
500
|
+
clearing.display = 'block';
|
|
501
|
+
into.append(new Box('block', el, clearing));
|
|
502
|
+
this._ws = 'start';
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
210
505
|
const box = new Box('break', el, style);
|
|
211
506
|
into.append(box);
|
|
212
507
|
this._push('\n', box);
|
|
508
|
+
this._ws = 'start';
|
|
213
509
|
return;
|
|
214
510
|
}
|
|
215
|
-
|
|
511
|
+
// an `<object>` is its image once it has one, and its content until;
|
|
512
|
+
// an `<embed>` its image, and a `<video>` its poster, or a frame
|
|
513
|
+
const replaced = tag === 'object'
|
|
514
|
+
? this._options.imageSize(el)
|
|
515
|
+
? 'image'
|
|
516
|
+
: 'none'
|
|
517
|
+
: (tag === 'embed' || tag === 'video') && this._options.imageSize(el)
|
|
518
|
+
? 'image'
|
|
519
|
+
: replacedKind(el, tag);
|
|
216
520
|
if (replaced !== 'none') {
|
|
217
521
|
this._replaced(el, tag, replaced, style, into);
|
|
218
522
|
return;
|
|
@@ -222,51 +526,185 @@ class Builder {
|
|
|
222
526
|
const kind = boxKindFor(style.display);
|
|
223
527
|
const box = new Box(kind, el, style);
|
|
224
528
|
into.append(box);
|
|
529
|
+
if ((kind === 'block' || kind === 'table-cell' || kind === 'table-caption') &&
|
|
530
|
+
this._options.cascade.hasFirstLine) {
|
|
531
|
+
const firstLine = this._options.cascade.firstLineStyle(el, style);
|
|
532
|
+
if (firstLine) {
|
|
533
|
+
FIRST_LINE.set(box, firstLine);
|
|
534
|
+
this._firstLine = true;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
if (style.backgroundImage ||
|
|
538
|
+
style.backgroundImages ||
|
|
539
|
+
typeof style.borderImage.source === 'string') {
|
|
540
|
+
this._backgrounds.push(box);
|
|
541
|
+
}
|
|
225
542
|
if (style.position === 'absolute' || style.position === 'fixed')
|
|
226
543
|
box.outOfFlow = true;
|
|
227
544
|
else if (style.float !== 'none')
|
|
228
545
|
box.isFloat = true;
|
|
546
|
+
if (into.kind === 'inline' && (box.outOfFlow || box.isFloat))
|
|
547
|
+
this._nestedOutOfLine = true;
|
|
229
548
|
if (attr(el, 'href') && (tag === 'a' || tag === 'area'))
|
|
230
549
|
this._links.push(box);
|
|
231
|
-
|
|
232
|
-
|
|
550
|
+
// a column's content is not rendered at all (CSS 2.1 17.2.1)
|
|
551
|
+
if (style.display === 'table-column')
|
|
552
|
+
return;
|
|
553
|
+
let insideMarker = null;
|
|
554
|
+
let markerStyle = null;
|
|
555
|
+
let ownMarker = false;
|
|
556
|
+
let markerImage = null;
|
|
557
|
+
let insideImage = null;
|
|
558
|
+
if (style.display === 'list-item' && style.listStyleImage) {
|
|
559
|
+
// asked for as generated content's images are, and the tree built
|
|
560
|
+
// again when it arrives; until it has, and where it never does, the
|
|
561
|
+
// item's marker is its `list-style-type`'s
|
|
562
|
+
const url = style.listStyleImage;
|
|
563
|
+
const size = this._options.urlSize?.(url) ?? null;
|
|
564
|
+
this._contentImages.push({ url, element: el, sized: !!size });
|
|
565
|
+
if (size)
|
|
566
|
+
markerImage = { url, size };
|
|
233
567
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
const
|
|
237
|
-
|
|
568
|
+
if (style.display === 'list-item') {
|
|
569
|
+
// the item's number, the `list-item` counter it has just counted
|
|
570
|
+
const written = markerFor(style, this._scopes.value('list-item'), this._options.cascade.counterStyles);
|
|
571
|
+
let text = written.text;
|
|
572
|
+
let flush = written.flush;
|
|
573
|
+
markerStyle = this._options.cascade.markerStyle(el, style);
|
|
574
|
+
// a `::marker` with a `content` is set as that, as it is written —
|
|
575
|
+
// its strings and its counters, where one of `none` is no marker
|
|
576
|
+
const content = markerStyle?.content;
|
|
577
|
+
if (content === 'none')
|
|
578
|
+
text = '';
|
|
579
|
+
else if (Array.isArray(content) && markerStyle) {
|
|
580
|
+
text = this._generated(content, markerStyle, el)
|
|
581
|
+
.filter((piece) => typeof piece === 'string')
|
|
582
|
+
.join('');
|
|
583
|
+
ownMarker = true;
|
|
584
|
+
flush = true;
|
|
585
|
+
}
|
|
586
|
+
else if (style.listStyleType.startsWith('"')) {
|
|
587
|
+
ownMarker = true;
|
|
588
|
+
flush = true;
|
|
589
|
+
}
|
|
590
|
+
if (markerImage && !ownMarker) {
|
|
591
|
+
if (style.listStylePosition === 'inside')
|
|
592
|
+
insideImage = markerImage.url;
|
|
593
|
+
else {
|
|
594
|
+
const scale = this._options.scale ?? 1;
|
|
595
|
+
box.marker = {
|
|
596
|
+
text: '',
|
|
597
|
+
layout: null,
|
|
598
|
+
x: 0,
|
|
599
|
+
y: 0,
|
|
600
|
+
style: markerStyle,
|
|
601
|
+
image: {
|
|
602
|
+
url: markerImage.url,
|
|
603
|
+
width: (markerImage.size.width ?? 0) * scale,
|
|
604
|
+
height: (markerImage.size.height ?? 0) * scale,
|
|
605
|
+
},
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
else if (text && style.listStylePosition === 'inside') {
|
|
610
|
+
insideMarker = flush ? text : `${text} `;
|
|
611
|
+
}
|
|
612
|
+
else if (text) {
|
|
613
|
+
box.marker = { text, layout: null, x: 0, y: 0, style: markerStyle };
|
|
614
|
+
if (flush)
|
|
615
|
+
box.marker.flush = true;
|
|
616
|
+
}
|
|
238
617
|
}
|
|
239
618
|
const childInFlex = style.display === 'flex' || style.display === 'inline-flex';
|
|
619
|
+
const flow = flowOf(style, box);
|
|
620
|
+
const around = this._ws;
|
|
621
|
+
const aroundWord = this._lettered;
|
|
622
|
+
if (flow === 'block')
|
|
623
|
+
this._endLine();
|
|
624
|
+
if (flow !== 'inline')
|
|
625
|
+
this._ws = 'start';
|
|
626
|
+
// A first letter is looked for in the first line of a block container,
|
|
627
|
+
// down through its inline content and its first blocks. A float, a
|
|
628
|
+
// positioned box and a flex container are no part of that line, and
|
|
629
|
+
// an atomic inline is something other than a letter at its start
|
|
630
|
+
// (CSS 2.1 5.12.2). A block container with rules of its own takes the
|
|
631
|
+
// search over.
|
|
632
|
+
const outerLetter = this._firstLetter;
|
|
633
|
+
const skipped = flow === 'out' || flow === 'atomic' || kind === 'flex';
|
|
634
|
+
if (skipped)
|
|
635
|
+
this._firstLetter = null;
|
|
636
|
+
// a block starts a line, and punctuation before it was on another
|
|
637
|
+
else if (flow === 'block' && outerLetter)
|
|
638
|
+
giveBack(outerLetter);
|
|
639
|
+
const ownRules = hasFirstLetter(style.display)
|
|
640
|
+
? this._options.cascade.firstLetterRules(el)
|
|
641
|
+
: null;
|
|
642
|
+
const ownLetter = ownRules ? { rules: ownRules, punctuation: [] } : null;
|
|
643
|
+
if (ownLetter)
|
|
644
|
+
this._firstLetter = ownLetter;
|
|
240
645
|
this._depth += 1;
|
|
241
|
-
|
|
646
|
+
if (insideImage) {
|
|
647
|
+
// an inline image at the start of the first line, as a generated
|
|
648
|
+
// image is, and the space a marker's text ends in
|
|
649
|
+
this._contentImage(insideImage, box, style, el);
|
|
650
|
+
this._textNode(' ', box, style, el);
|
|
651
|
+
}
|
|
652
|
+
else if (insideMarker) {
|
|
653
|
+
this._insideMarker(insideMarker, style, markerStyle, box, el);
|
|
654
|
+
}
|
|
655
|
+
this._pseudo(el, 'before', style, box);
|
|
656
|
+
this._children(el, box, style, childInFlex, el, key);
|
|
657
|
+
this._pseudo(el, 'after', style, box);
|
|
242
658
|
this._depth -= 1;
|
|
243
|
-
|
|
244
|
-
|
|
659
|
+
this._letterAfter(flow, skipped, outerLetter, ownLetter);
|
|
660
|
+
if (flow !== 'inline')
|
|
661
|
+
this._endLine();
|
|
662
|
+
this._ws = after(flow, this._ws, around);
|
|
663
|
+
this._lettered = letteredAfterFlow(flow, this._lettered, aroundWord);
|
|
245
664
|
}
|
|
246
665
|
_replaced(el, tag, replaced, style, into) {
|
|
247
666
|
const box = new Box('replaced', el, style);
|
|
248
667
|
box.replaced = replaced;
|
|
249
668
|
into.append(box);
|
|
669
|
+
if (style.backgroundImage ||
|
|
670
|
+
style.backgroundImages ||
|
|
671
|
+
typeof style.borderImage.source === 'string') {
|
|
672
|
+
this._backgrounds.push(box);
|
|
673
|
+
}
|
|
250
674
|
if (style.position === 'absolute' || style.position === 'fixed')
|
|
251
675
|
box.outOfFlow = true;
|
|
252
676
|
else if (style.float !== 'none')
|
|
253
677
|
box.isFloat = true;
|
|
678
|
+
if (into.kind === 'inline' && (box.outOfFlow || box.isFloat))
|
|
679
|
+
this._nestedOutOfLine = true;
|
|
680
|
+
const flow = flowOf(style, box);
|
|
681
|
+
if (flow === 'block')
|
|
682
|
+
this._endLine();
|
|
683
|
+
this._ws = after(flow, this._ws, this._ws);
|
|
684
|
+
this._lettered = letteredAfterFlow(flow, this._lettered, this._lettered);
|
|
685
|
+
if (flow === 'atomic')
|
|
686
|
+
this._abandonLetter();
|
|
254
687
|
if (replaced === 'image') {
|
|
255
688
|
// Both sources are CSS pixels — an image pixel is one, and so is an
|
|
256
689
|
// attribute — and the box is device.
|
|
257
690
|
const scale = this._options.scale ?? 1;
|
|
258
691
|
const loaded = this._options.imageSize(el);
|
|
259
692
|
if (loaded) {
|
|
260
|
-
box
|
|
261
|
-
box.intrinsicHeight = loaded.height * scale;
|
|
693
|
+
setIntrinsics(box, loaded, scale);
|
|
262
694
|
}
|
|
263
695
|
else {
|
|
264
696
|
// An image that has not arrived still needs a box, or the document
|
|
265
697
|
// reflows under the reader when it does. The attributes are the
|
|
266
698
|
// author telling us the size in advance; without them the box is a
|
|
267
699
|
// small placeholder rather than nothing.
|
|
268
|
-
|
|
269
|
-
|
|
700
|
+
const width = numberAttr(el, 'width') ?? 0;
|
|
701
|
+
const height = numberAttr(el, 'height') ?? 0;
|
|
702
|
+
box.intrinsic = {
|
|
703
|
+
width: width * scale,
|
|
704
|
+
height: height * scale,
|
|
705
|
+
missing: 0,
|
|
706
|
+
ratio: width > 0 && height > 0 ? width / height : 0,
|
|
707
|
+
};
|
|
270
708
|
}
|
|
271
709
|
// The alt text joins the document text, so a document read with the
|
|
272
710
|
// images blocked still copies as prose.
|
|
@@ -277,35 +715,494 @@ class Builder {
|
|
|
277
715
|
}
|
|
278
716
|
if (replaced === 'hr')
|
|
279
717
|
return;
|
|
718
|
+
if (replaced === 'svg') {
|
|
719
|
+
// its `em` is its own font size, in CSS pixels like the rest of it
|
|
720
|
+
const scale = this._options.scale ?? 1;
|
|
721
|
+
setIntrinsics(box, svgIntrinsics(el, style.fontSize / scale), scale);
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
if (replaced === 'frame') {
|
|
725
|
+
const scale = this._options.scale ?? 1;
|
|
726
|
+
if (tag === 'canvas') {
|
|
727
|
+
// A canvas is the size of its bitmap, which its `width` and
|
|
728
|
+
// `height` attributes give, 300 by 150 where they do not, and
|
|
729
|
+
// keeps its proportions (HTML 4.12.5): no script draws in it, so it
|
|
730
|
+
// is that box with nothing in it. Taken for an element with no box
|
|
731
|
+
// of its own, it took no room, and one set a height kept no width.
|
|
732
|
+
const width = canvasSize(el, 'width', 300);
|
|
733
|
+
const height = canvasSize(el, 'height', 150);
|
|
734
|
+
box.intrinsic = {
|
|
735
|
+
width: width * scale,
|
|
736
|
+
height: height * scale,
|
|
737
|
+
missing: 0,
|
|
738
|
+
ratio: width > 0 && height > 0 ? width / height : 0,
|
|
739
|
+
};
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
// HTML's default object size, in CSS pixels; `width` and `height`
|
|
743
|
+
// attributes reach the style as presentational hints and win
|
|
744
|
+
box.intrinsic = {
|
|
745
|
+
width: 300 * scale,
|
|
746
|
+
height: 150 * scale,
|
|
747
|
+
missing: 0,
|
|
748
|
+
ratio: 0,
|
|
749
|
+
};
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
280
752
|
const size = this._options.controlSize(el, replaced, style);
|
|
281
|
-
box.
|
|
282
|
-
|
|
753
|
+
box.intrinsic = {
|
|
754
|
+
width: size.width,
|
|
755
|
+
height: size.height,
|
|
756
|
+
missing: 0,
|
|
757
|
+
ratio: 0,
|
|
758
|
+
};
|
|
283
759
|
this._controls.push(box);
|
|
284
760
|
// A control's value is the widget's, not the document's: putting it in
|
|
285
761
|
// the selection index would make Ctrl+A copy the contents of every text
|
|
286
762
|
// field, which no document viewer does.
|
|
287
763
|
}
|
|
764
|
+
/**
|
|
765
|
+
* An element's `::before` or `::after`, when a rule gives it content: a
|
|
766
|
+
* box of its own `display`, holding the text its `content` comes to, which
|
|
767
|
+
* goes through the same white-space processing as the document's text.
|
|
768
|
+
*/
|
|
769
|
+
_pseudo(el, which, elementStyle, into) {
|
|
770
|
+
const style = this._options.cascade.pseudoStyleFor(el, which, elementStyle);
|
|
771
|
+
if (!style || style.display === 'none')
|
|
772
|
+
return;
|
|
773
|
+
if (isRelative(style)) {
|
|
774
|
+
this._relative = true;
|
|
775
|
+
if (style.display === 'inline' && isOffset(style)) {
|
|
776
|
+
this._movedInline = true;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
// a translation is moved by the same pass
|
|
780
|
+
if (style.translate || style.transformTranslate)
|
|
781
|
+
this._relative = true;
|
|
782
|
+
if (style.backgroundClipText)
|
|
783
|
+
this._clipText = true;
|
|
784
|
+
if (style.verticalAlign !== 'baseline' && style.display === 'inline') {
|
|
785
|
+
this._movedInline = true;
|
|
786
|
+
}
|
|
787
|
+
if (isNegative(style))
|
|
788
|
+
this._negative = true;
|
|
789
|
+
// a column renders no content, and generated content is all it would
|
|
790
|
+
// hold; in a column group it is not a column either (CSS 2.1 17.2.1)
|
|
791
|
+
if (style.display === 'table-column' ||
|
|
792
|
+
style.display === 'table-column-group' ||
|
|
793
|
+
elementStyle.display === 'table-column-group') {
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
// a scope of its own, which what it counts is in where its element has
|
|
797
|
+
// the counter already (`CounterScopes`)
|
|
798
|
+
this._scopes.open();
|
|
799
|
+
this._counterChanges(style, null);
|
|
800
|
+
const box = new Box(boxKindFor(style.display), null, style);
|
|
801
|
+
box.pseudo = which;
|
|
802
|
+
into.append(box);
|
|
803
|
+
// a list item it generates has a marker as an element's has, of the
|
|
804
|
+
// `list-item` counter it has just counted (CSS 2.1 12.5): outside it,
|
|
805
|
+
// or at the start of its content
|
|
806
|
+
const written = style.display === 'list-item'
|
|
807
|
+
? markerFor(style, this._scopes.value('list-item'), this._options.cascade.counterStyles)
|
|
808
|
+
: null;
|
|
809
|
+
let marker = written?.text ?? '';
|
|
810
|
+
if (marker && style.listStylePosition !== 'inside') {
|
|
811
|
+
box.marker = { text: marker, layout: null, x: 0, y: 0, style: null };
|
|
812
|
+
if (written?.flush)
|
|
813
|
+
box.marker.flush = true;
|
|
814
|
+
marker = '';
|
|
815
|
+
}
|
|
816
|
+
if (style.position === 'absolute' || style.position === 'fixed')
|
|
817
|
+
box.outOfFlow = true;
|
|
818
|
+
else if (style.float !== 'none')
|
|
819
|
+
box.isFloat = true;
|
|
820
|
+
if (into.kind === 'inline' && (box.outOfFlow || box.isFloat))
|
|
821
|
+
this._nestedOutOfLine = true;
|
|
822
|
+
const pieces = this._generated(style.content, style, el);
|
|
823
|
+
if (marker)
|
|
824
|
+
pieces.unshift(written?.flush ? marker : `${marker} `);
|
|
825
|
+
this._scopes.close();
|
|
826
|
+
const flow = flowOf(style, box);
|
|
827
|
+
const around = this._ws;
|
|
828
|
+
const aroundWord = this._lettered;
|
|
829
|
+
if (flow === 'block')
|
|
830
|
+
this._endLine();
|
|
831
|
+
if (flow !== 'inline')
|
|
832
|
+
this._ws = 'start';
|
|
833
|
+
// the first letter can be generated, and is looked for here as in an
|
|
834
|
+
// element of the same display
|
|
835
|
+
const outerLetter = this._firstLetter;
|
|
836
|
+
const skipped = flow === 'out' || flow === 'atomic';
|
|
837
|
+
if (skipped)
|
|
838
|
+
this._firstLetter = null;
|
|
839
|
+
else if (flow === 'block' && outerLetter)
|
|
840
|
+
giveBack(outerLetter);
|
|
841
|
+
for (const piece of pieces) {
|
|
842
|
+
if (typeof piece === 'string')
|
|
843
|
+
this._textNode(piece, box, style, el);
|
|
844
|
+
else
|
|
845
|
+
this._contentImage(piece.url, box, style, el);
|
|
846
|
+
}
|
|
847
|
+
this._letterAfter(flow, skipped, outerLetter, null);
|
|
848
|
+
if (flow !== 'inline')
|
|
849
|
+
this._endLine();
|
|
850
|
+
this._ws = after(flow, this._ws, around);
|
|
851
|
+
this._lettered = letteredAfterFlow(flow, this._lettered, aroundWord);
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* An `inside` list marker: an inline box at the start of the item, the
|
|
855
|
+
* marker and a space, which takes its room on the first line as a
|
|
856
|
+
* `::marker` does (CSS Lists 3, 3.1), where a marker set beside the line
|
|
857
|
+
* was drawn over its first letters — every `list-style-position: inside`
|
|
858
|
+
* list, and every `<summary>`. It inherits the item's text style and has
|
|
859
|
+
* none of its box.
|
|
860
|
+
*/
|
|
861
|
+
_insideMarker(text, itemStyle, markerStyle, into, el) {
|
|
862
|
+
const style = inherit(markerStyle ?? itemStyle, this._options.cascade.initial);
|
|
863
|
+
style.display = 'inline';
|
|
864
|
+
// a marker's direction is its own, whatever the text after it, unless
|
|
865
|
+
// its rules say otherwise (the HTML style sheet's `::marker`)
|
|
866
|
+
style.unicodeBidi = markerStyle?.unicodeBidi ?? 'isolate';
|
|
867
|
+
const box = new Box('inline', null, style);
|
|
868
|
+
box.pseudo = 'before';
|
|
869
|
+
into.append(box);
|
|
870
|
+
this._textNode(text, box, style, el);
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* `counter-reset`, then `counter-increment`, then `counter-set`, as CSS
|
|
874
|
+
* Lists 3 orders them (4.2). A list item counts `list-item` besides,
|
|
875
|
+
* unless its `counter-increment` names it: one up, or one down in a list
|
|
876
|
+
* that counts down. `place` is where an element is, for a reversed
|
|
877
|
+
* counter to be counted from; a pseudo-element has none.
|
|
878
|
+
*/
|
|
879
|
+
_counterChanges(style, place) {
|
|
880
|
+
const resets = style.counterReset;
|
|
881
|
+
const increments = style.counterIncrement;
|
|
882
|
+
const sets = style.counterSet;
|
|
883
|
+
const listItem = style.display === 'list-item';
|
|
884
|
+
if (!resets && !increments && !sets && !listItem)
|
|
885
|
+
return;
|
|
886
|
+
if (resets) {
|
|
887
|
+
for (let i = 0; i < resets.length; i += 1) {
|
|
888
|
+
const change = resets[i];
|
|
889
|
+
// a name reset twice is reset by the last
|
|
890
|
+
if (resets.some((c, j) => j > i && c.name === change.name))
|
|
891
|
+
continue;
|
|
892
|
+
const value = change.counted
|
|
893
|
+
? this._countedStart(change.name, style, place)
|
|
894
|
+
: change.value;
|
|
895
|
+
this._scopes.reset(change.name, value, !!change.reversed);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
let counted = false;
|
|
899
|
+
for (const { name, value } of increments ?? []) {
|
|
900
|
+
this._scopes.increment(name, value);
|
|
901
|
+
if (name === 'list-item')
|
|
902
|
+
counted = true;
|
|
903
|
+
}
|
|
904
|
+
if (listItem && !counted) {
|
|
905
|
+
this._scopes.increment('list-item', this._scopes.reversed('list-item') ? -1 : 1);
|
|
906
|
+
}
|
|
907
|
+
for (const { name, value } of sets ?? [])
|
|
908
|
+
this._scopes.set(name, value);
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* Where a reversed counter written with no number starts (CSS Lists 3,
|
|
912
|
+
* 4.4.2): minus what the elements and pseudo-elements in its scope add to
|
|
913
|
+
* it, as far as the first that sets it, and what that sets it to, and as
|
|
914
|
+
* much again as the last of them that counted took away — 1 for a list
|
|
915
|
+
* item. `<ol reversed>`'s items count down to 1, and to an item's
|
|
916
|
+
* `value` before it. The scope is walked ahead of the builder, from the styles it will
|
|
917
|
+
* find shared when it gets there (`Cascade.sharedStyleFor`), and only for
|
|
918
|
+
* such a counter.
|
|
919
|
+
*/
|
|
920
|
+
_countedStart(name, style, place) {
|
|
921
|
+
const cascade = this._options.cascade;
|
|
922
|
+
/** What each element and pseudo-element in the scope does to the
|
|
923
|
+
* counter, in document order: what it adds, and what it sets it to. */
|
|
924
|
+
const steps = [];
|
|
925
|
+
const note = (st) => {
|
|
926
|
+
let by = 0;
|
|
927
|
+
let increments = false;
|
|
928
|
+
for (const change of st.counterIncrement ?? []) {
|
|
929
|
+
if (change.name !== name)
|
|
930
|
+
continue;
|
|
931
|
+
by += change.value;
|
|
932
|
+
increments = true;
|
|
933
|
+
}
|
|
934
|
+
if (!increments && name === 'list-item' && st.display === 'list-item') {
|
|
935
|
+
by = -1;
|
|
936
|
+
increments = true;
|
|
937
|
+
}
|
|
938
|
+
let set = null;
|
|
939
|
+
for (const change of st.counterSet ?? []) {
|
|
940
|
+
if (change.name === name)
|
|
941
|
+
set = change.value;
|
|
942
|
+
}
|
|
943
|
+
if (increments || set !== null)
|
|
944
|
+
steps.push({ by, set });
|
|
945
|
+
};
|
|
946
|
+
const resets = (st) => st.counterReset?.some((change) => change.name === name) ?? false;
|
|
947
|
+
const pseudo = (el, which, st) => {
|
|
948
|
+
const own = cascade.pseudoStyleFor(el, which, st);
|
|
949
|
+
if (own && own.display !== 'none' && !resets(own))
|
|
950
|
+
note(own);
|
|
951
|
+
};
|
|
952
|
+
/** The elements from `from` on of a parent, and what is in them: a
|
|
953
|
+
* reset of the counter in one is a counter of its own, and a later
|
|
954
|
+
* sibling's reset at the scope's own level ends it (`stopAtReset`). */
|
|
955
|
+
const walk = (children, from, parentStyle, parentKey, inFlex, stopAtReset) => {
|
|
956
|
+
for (let i = from; i < children.length; i += 1) {
|
|
957
|
+
const child = children[i];
|
|
958
|
+
if (!isElement(child))
|
|
959
|
+
continue;
|
|
960
|
+
const tag = tagOf(child);
|
|
961
|
+
if (NON_RENDERED.has(tag) || inImpliedHead(child, tag))
|
|
962
|
+
continue;
|
|
963
|
+
const shared = cascade.sharedStyleFor(child, parentStyle, parentKey, inFlex);
|
|
964
|
+
const st = shared.style;
|
|
965
|
+
if (st.display === 'none')
|
|
966
|
+
continue;
|
|
967
|
+
if (resets(st)) {
|
|
968
|
+
if (stopAtReset)
|
|
969
|
+
return;
|
|
970
|
+
continue;
|
|
971
|
+
}
|
|
972
|
+
note(st);
|
|
973
|
+
inside(child, st, shared.key);
|
|
974
|
+
}
|
|
975
|
+
};
|
|
976
|
+
const inside = (el, st, key) => {
|
|
977
|
+
pseudo(el, 'before', st);
|
|
978
|
+
walk(childrenOf(el), 0, st, key, st.display === 'flex' || st.display === 'inline-flex', false);
|
|
979
|
+
pseudo(el, 'after', st);
|
|
980
|
+
};
|
|
981
|
+
// the element that resets it counts in it too, after the reset
|
|
982
|
+
note(style);
|
|
983
|
+
if (place) {
|
|
984
|
+
const siblings = this._scopes.reachesSiblings(name);
|
|
985
|
+
inside(place.node, place.style, place.key);
|
|
986
|
+
const parent = place.parent;
|
|
987
|
+
const node = place.node;
|
|
988
|
+
if (siblings && parent && node.parent) {
|
|
989
|
+
const children = childrenOf(node.parent);
|
|
990
|
+
walk(children, children.indexOf(place.node) + 1, parent.style, parent.key, parent.inFlex, true);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
let num = 0;
|
|
994
|
+
let last = 0;
|
|
995
|
+
for (const { by, set } of steps) {
|
|
996
|
+
if (by !== 0)
|
|
997
|
+
last = -by;
|
|
998
|
+
if (set !== null) {
|
|
999
|
+
num += set;
|
|
1000
|
+
break;
|
|
1001
|
+
}
|
|
1002
|
+
num -= by;
|
|
1003
|
+
}
|
|
1004
|
+
return num + last;
|
|
1005
|
+
}
|
|
1006
|
+
/** A counter's value in a style the document can name, as `counter()`
|
|
1007
|
+
* writes it; `none` writes nothing. */
|
|
1008
|
+
_counterText(n, name, style) {
|
|
1009
|
+
if (name === 'none')
|
|
1010
|
+
return '';
|
|
1011
|
+
return this._options.cascade.counterStyles.text(n, name, style.direction === 'rtl');
|
|
1012
|
+
}
|
|
1013
|
+
/** What `content` comes to here, in document order: its text, broken
|
|
1014
|
+
* where it names an image. The quotes it opens and closes count for
|
|
1015
|
+
* everything after it. */
|
|
1016
|
+
_generated(items, style, el) {
|
|
1017
|
+
const pieces = [];
|
|
1018
|
+
let text = '';
|
|
1019
|
+
for (const item of items) {
|
|
1020
|
+
switch (item.kind) {
|
|
1021
|
+
case 'string':
|
|
1022
|
+
text += item.text;
|
|
1023
|
+
break;
|
|
1024
|
+
case 'url':
|
|
1025
|
+
if (text)
|
|
1026
|
+
pieces.push(text);
|
|
1027
|
+
text = '';
|
|
1028
|
+
pieces.push({ url: item.url });
|
|
1029
|
+
break;
|
|
1030
|
+
case 'attr':
|
|
1031
|
+
text += attr(el, item.name) ?? '';
|
|
1032
|
+
break;
|
|
1033
|
+
case 'counter':
|
|
1034
|
+
text += this._counterText(this._scopes.value(item.name), item.style, style);
|
|
1035
|
+
break;
|
|
1036
|
+
case 'counters':
|
|
1037
|
+
text += this._scopes
|
|
1038
|
+
.values(item.name)
|
|
1039
|
+
.map((v) => this._counterText(v, item.style, style))
|
|
1040
|
+
.join(item.separator);
|
|
1041
|
+
break;
|
|
1042
|
+
case 'open-quote':
|
|
1043
|
+
text += quoteAt(style.quotes, this._quoteDepth, 0);
|
|
1044
|
+
this._quoteDepth += 1;
|
|
1045
|
+
break;
|
|
1046
|
+
case 'close-quote':
|
|
1047
|
+
// a close with nothing open writes nothing and closes nothing
|
|
1048
|
+
if (this._quoteDepth > 0) {
|
|
1049
|
+
this._quoteDepth -= 1;
|
|
1050
|
+
text += quoteAt(style.quotes, this._quoteDepth, 1);
|
|
1051
|
+
}
|
|
1052
|
+
break;
|
|
1053
|
+
case 'no-open-quote':
|
|
1054
|
+
this._quoteDepth += 1;
|
|
1055
|
+
break;
|
|
1056
|
+
case 'no-close-quote':
|
|
1057
|
+
if (this._quoteDepth > 0)
|
|
1058
|
+
this._quoteDepth -= 1;
|
|
1059
|
+
break;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
if (text)
|
|
1063
|
+
pieces.push(text);
|
|
1064
|
+
return pieces;
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* An image generated content names: an inline replaced box in the
|
|
1068
|
+
* pseudo-element, of the style it inherits and no other (CSS 2.1 12.2),
|
|
1069
|
+
* the image's size once the host has it and none before. An image that
|
|
1070
|
+
* never arrives takes no room, as no image does.
|
|
1071
|
+
*/
|
|
1072
|
+
_contentImage(url, into, style, owner) {
|
|
1073
|
+
const box = new Box('replaced', null, {
|
|
1074
|
+
...inherit(style, this._options.cascade.initial),
|
|
1075
|
+
display: 'inline',
|
|
1076
|
+
});
|
|
1077
|
+
box.replaced = 'image';
|
|
1078
|
+
into.append(box);
|
|
1079
|
+
CONTENT_IMAGES.set(box, url);
|
|
1080
|
+
const size = this._options.urlSize?.(url) ?? null;
|
|
1081
|
+
this._contentImages.push({ url, element: owner, sized: !!size });
|
|
1082
|
+
if (size)
|
|
1083
|
+
setIntrinsics(box, size, this._options.scale ?? 1);
|
|
1084
|
+
else
|
|
1085
|
+
box.intrinsic = { width: 0, height: 0, missing: 0, ratio: 0 };
|
|
1086
|
+
this._ws = after('atomic', this._ws, this._ws);
|
|
1087
|
+
this._lettered = false;
|
|
1088
|
+
this._abandonLetter();
|
|
1089
|
+
}
|
|
288
1090
|
/** A text node, whitespace-processed per the inherited `white-space`. */
|
|
289
1091
|
_textNode(data, into, style, owner) {
|
|
1092
|
+
// text set at no size draws nothing and takes no room, so it needs no
|
|
1093
|
+
// box — and it is no part of the white space around it either
|
|
1094
|
+
if (!(style.fontSize > 0))
|
|
1095
|
+
return;
|
|
1096
|
+
// a word does not run on over the start of a line
|
|
1097
|
+
if (this._ws === 'start')
|
|
1098
|
+
this._lettered = false;
|
|
290
1099
|
const ws = style.whiteSpace;
|
|
291
1100
|
let text;
|
|
292
1101
|
if (ws === 'pre' || ws === 'pre-wrap') {
|
|
293
1102
|
text = data;
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
1103
|
+
if (!text)
|
|
1104
|
+
return;
|
|
1105
|
+
// preserved spaces do not collapse with the ones after them
|
|
1106
|
+
this._ws = text.endsWith('\n') ? 'start' : 'content';
|
|
297
1107
|
}
|
|
298
1108
|
else {
|
|
299
|
-
text =
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
1109
|
+
text =
|
|
1110
|
+
ws === 'pre-line'
|
|
1111
|
+
? data.replace(/[ \t]+/g, ' ').replace(/ *\n */g, '\n')
|
|
1112
|
+
: COLLAPSIBLE.test(data)
|
|
1113
|
+
? data.replace(/[\t\n\r\f ]+/g, ' ')
|
|
1114
|
+
: data;
|
|
1115
|
+
// A space at the start of a line goes, and so does one after another
|
|
1116
|
+
// space, across element boundaries (CSS 2.1 16.6.1): `<p>\n Hi` has
|
|
1117
|
+
// no space before the H, and `Hi <b> there</b>` has one between.
|
|
1118
|
+
if (text.charCodeAt(0) === 32 && this._ws !== 'content') {
|
|
1119
|
+
text = text.slice(1);
|
|
1120
|
+
}
|
|
1121
|
+
if (!text)
|
|
304
1122
|
return;
|
|
1123
|
+
const last = text.charCodeAt(text.length - 1);
|
|
1124
|
+
this._ws = last === 32 ? 'space' : last === 10 ? 'start' : 'content';
|
|
305
1125
|
}
|
|
306
|
-
|
|
1126
|
+
text = transformText(text, style.textTransform, this._lettered);
|
|
1127
|
+
this._lettered = letteredAfter(text, this._lettered);
|
|
1128
|
+
const search = this._firstLetter;
|
|
1129
|
+
const letter = search ? FIRST_LETTER.exec(text) : null;
|
|
1130
|
+
if (!search || !letter) {
|
|
1131
|
+
const punctuation = search ? PUNCTUATION_ONLY.exec(text) : null;
|
|
1132
|
+
if (!search || !punctuation) {
|
|
1133
|
+
this._textBox(text, into, style, owner);
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
// punctuation the letter comes after, in a text of its own —
|
|
1137
|
+
// `<q>`'s open quote — takes the letter's style, and gives it back
|
|
1138
|
+
// if no letter follows on the line
|
|
1139
|
+
const start = punctuation[1].length;
|
|
1140
|
+
if (start > 0)
|
|
1141
|
+
this._textBox(text.slice(0, start), into, style, owner);
|
|
1142
|
+
search.punctuation.push(this._letterBox(search, text.slice(start), into, style, owner));
|
|
307
1143
|
return;
|
|
308
|
-
|
|
1144
|
+
}
|
|
1145
|
+
// The first letter, with the punctuation around it, in a box of its
|
|
1146
|
+
// own inside the box it was found in, and so inheriting from that
|
|
1147
|
+
// (CSS 2.1 5.12.2): `<p><b>T</b>his` has a bold first letter.
|
|
1148
|
+
this._firstLetter = null;
|
|
1149
|
+
const start = letter[1].length;
|
|
1150
|
+
const end = letter[0].length;
|
|
1151
|
+
if (start > 0)
|
|
1152
|
+
this._textBox(text.slice(0, start), into, style, owner);
|
|
1153
|
+
this._letterBox(search, text.slice(start, end), into, style, owner);
|
|
1154
|
+
if (end < text.length) {
|
|
1155
|
+
this._textBox(text.slice(end), into, style, owner);
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
/** A box of the first letter's style around `text`, in `into`. */
|
|
1159
|
+
_letterBox(search, text, into, style, owner) {
|
|
1160
|
+
const cascade = this._options.cascade;
|
|
1161
|
+
const letterStyle = cascade.firstLetterStyle(search.rules, style);
|
|
1162
|
+
const box = new Box(boxKindFor(letterStyle.display), null, letterStyle);
|
|
1163
|
+
box.pseudo = 'first-letter';
|
|
1164
|
+
if (letterStyle.float !== 'none') {
|
|
1165
|
+
box.isFloat = true;
|
|
1166
|
+
if (into.kind === 'inline')
|
|
1167
|
+
this._nestedOutOfLine = true;
|
|
1168
|
+
}
|
|
1169
|
+
into.append(box);
|
|
1170
|
+
if (letterStyle.textTransform !== style.textTransform) {
|
|
1171
|
+
text = transformText(text, letterStyle.textTransform);
|
|
1172
|
+
}
|
|
1173
|
+
return { box, text: this._textBox(text, box, letterStyle, owner), style };
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Where the search for a first letter stands after a box. A box that was
|
|
1177
|
+
* no part of the line — a float, a positioned box — leaves it where it
|
|
1178
|
+
* was, and one that was something other than a letter on it ends it. A
|
|
1179
|
+
* block that looked for a letter of its own and found its first line
|
|
1180
|
+
* ended its parent's too, and one that found no line leaves the parent
|
|
1181
|
+
* looking. A block ends the line it is on.
|
|
1182
|
+
*/
|
|
1183
|
+
_letterAfter(flow, skipped, outer, own) {
|
|
1184
|
+
const lined = own !== null && this._firstLetter !== own;
|
|
1185
|
+
if (own && !lined)
|
|
1186
|
+
this._abandonLetter();
|
|
1187
|
+
if (skipped || own)
|
|
1188
|
+
this._firstLetter = skipped || !lined ? outer : null;
|
|
1189
|
+
if (lined && !skipped && outer)
|
|
1190
|
+
giveBack(outer);
|
|
1191
|
+
if (flow === 'atomic')
|
|
1192
|
+
this._abandonLetter();
|
|
1193
|
+
else if (flow === 'block' && this._firstLetter)
|
|
1194
|
+
giveBack(this._firstLetter);
|
|
1195
|
+
}
|
|
1196
|
+
/** The first line ended, or began with something other than a letter:
|
|
1197
|
+
* there is no first letter, and punctuation that took its style gives
|
|
1198
|
+
* it back. */
|
|
1199
|
+
_abandonLetter() {
|
|
1200
|
+
const search = this._firstLetter;
|
|
1201
|
+
this._firstLetter = null;
|
|
1202
|
+
if (search)
|
|
1203
|
+
giveBack(search);
|
|
1204
|
+
}
|
|
1205
|
+
_textBox(text, into, style, owner) {
|
|
309
1206
|
// The owning element rides on the text box, and from there onto the
|
|
310
1207
|
// `TextRun`: hit testing inside a paragraph has no rectangle to test —
|
|
311
1208
|
// an inline box is the runs on its lines — so the run is what has to
|
|
@@ -314,19 +1211,198 @@ class Builder {
|
|
|
314
1211
|
box.text = text;
|
|
315
1212
|
into.append(box);
|
|
316
1213
|
this._push(text, box);
|
|
1214
|
+
return box;
|
|
1215
|
+
}
|
|
1216
|
+
/**
|
|
1217
|
+
* A line ends here — a block's inline content is over, or a `<br>` or a
|
|
1218
|
+
* block interrupts it — and a collapsible space it ends on goes (CSS 2.1
|
|
1219
|
+
* 16.6.1). Content standing on a space means that space was the last
|
|
1220
|
+
* thing pushed, so taking it back is a character off the last chunk; a
|
|
1221
|
+
* text box it empties goes with it.
|
|
1222
|
+
*/
|
|
1223
|
+
_endLine() {
|
|
1224
|
+
if (this._ws !== 'space')
|
|
1225
|
+
return;
|
|
1226
|
+
this._ws = 'start';
|
|
1227
|
+
const box = this._textBoxes[this._textBoxes.length - 1];
|
|
1228
|
+
if (!box || box.textEnd !== this._length || !box.text.endsWith(' ')) {
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
box.text = box.text.slice(0, -1);
|
|
1232
|
+
box.textEnd -= 1;
|
|
1233
|
+
this._length -= 1;
|
|
1234
|
+
const last = this._chunks.length - 1;
|
|
1235
|
+
this._chunks[last] = this._chunks[last].slice(0, -1);
|
|
1236
|
+
if (!box.text) {
|
|
1237
|
+
this._textBoxes.pop();
|
|
1238
|
+
const siblings = box.parent?.children;
|
|
1239
|
+
const at = siblings ? siblings.lastIndexOf(box) : -1;
|
|
1240
|
+
if (at >= 0)
|
|
1241
|
+
siblings.splice(at, 1);
|
|
1242
|
+
}
|
|
317
1243
|
}
|
|
318
1244
|
/** Give a box its slice of the document text index. */
|
|
319
1245
|
_push(text, box) {
|
|
320
|
-
box.textStart = this.
|
|
321
|
-
this.
|
|
322
|
-
|
|
323
|
-
|
|
1246
|
+
box.textStart = this._length;
|
|
1247
|
+
this._chunks.push(text);
|
|
1248
|
+
this._length += text.length;
|
|
1249
|
+
box.textEnd = this._length;
|
|
1250
|
+
if (box.kind === 'text') {
|
|
324
1251
|
this._textBoxes.push(box);
|
|
1252
|
+
if (box.style !== this._lastTextStyle) {
|
|
1253
|
+
this._lastTextStyle = box.style;
|
|
1254
|
+
this._textStyles.add(box.style);
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
325
1257
|
}
|
|
326
1258
|
}
|
|
1259
|
+
/**
|
|
1260
|
+
* The CSS counters in scope as the builder walks the document (CSS Lists 3,
|
|
1261
|
+
* 4.5). Every element and pseudo-element opens a level, which closes after
|
|
1262
|
+
* what it holds. A `counter-reset` makes an instance that reaches the
|
|
1263
|
+
* element's descendants and — where its parent has no counter of the name
|
|
1264
|
+
* — its later siblings too, so that instance belongs to the level the
|
|
1265
|
+
* element is on, its parent's, and goes when that closes; a later sibling's
|
|
1266
|
+
* reset takes its place. Where its parent has one, the new instance nests
|
|
1267
|
+
* in it and reaches the element's descendants alone, on the element's own
|
|
1268
|
+
* level: a list in a list item, a counter reset in a `::before`. `counter()`
|
|
1269
|
+
* reads the innermost instance and `counters()` all of them, outermost
|
|
1270
|
+
* first. A counter used where none is in scope is reset to 0 there, as
|
|
1271
|
+
* though the element had asked.
|
|
1272
|
+
*/
|
|
1273
|
+
class CounterScopes {
|
|
1274
|
+
/** Per name, its instances, outermost first: the level each goes with,
|
|
1275
|
+
* whether it is the own one of the element that level is, and whether it
|
|
1276
|
+
* counts down. */
|
|
1277
|
+
_instances = new Map();
|
|
1278
|
+
/** The names each open level made an instance of, so closing it drops
|
|
1279
|
+
* exactly those: null for a level that made none, which is nearly every
|
|
1280
|
+
* one, so that opening an element allocates nothing. */
|
|
1281
|
+
_made = [null];
|
|
1282
|
+
/** The levels of the open elements with style containment, innermost
|
|
1283
|
+
* last: below one, a counter made further out is not counted on. */
|
|
1284
|
+
_contained = [];
|
|
1285
|
+
open() {
|
|
1286
|
+
this._made.push(null);
|
|
1287
|
+
}
|
|
1288
|
+
close() {
|
|
1289
|
+
const level = this._made.length - 1;
|
|
1290
|
+
const made = this._made.pop();
|
|
1291
|
+
if (made) {
|
|
1292
|
+
for (const name of made) {
|
|
1293
|
+
const stack = this._instances.get(name);
|
|
1294
|
+
if (stack && stack[stack.length - 1]?.level === level)
|
|
1295
|
+
stack.pop();
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
const contained = this._contained;
|
|
1299
|
+
if (contained[contained.length - 1] === level)
|
|
1300
|
+
contained.pop();
|
|
1301
|
+
}
|
|
1302
|
+
/** The element whose level is open has style containment. */
|
|
1303
|
+
contain() {
|
|
1304
|
+
this._contained.push(this._made.length - 1);
|
|
1305
|
+
}
|
|
1306
|
+
/** Whether the parent of the element whose level is open has a counter
|
|
1307
|
+
* of this name: its own, or one from further out — not one a sibling
|
|
1308
|
+
* before the element made. */
|
|
1309
|
+
_parentHas(name) {
|
|
1310
|
+
const stack = this._instances.get(name);
|
|
1311
|
+
if (!stack?.length)
|
|
1312
|
+
return false;
|
|
1313
|
+
const parent = this._made.length - 2;
|
|
1314
|
+
for (let i = stack.length - 1; i >= 0; i -= 1) {
|
|
1315
|
+
const instance = stack[i];
|
|
1316
|
+
if (instance.level < parent)
|
|
1317
|
+
return true;
|
|
1318
|
+
if (instance.level === parent && instance.own)
|
|
1319
|
+
return true;
|
|
1320
|
+
}
|
|
1321
|
+
return false;
|
|
1322
|
+
}
|
|
1323
|
+
/** Whether a reset of `name` here reaches the element's later siblings,
|
|
1324
|
+
* or its descendants alone. */
|
|
1325
|
+
reachesSiblings(name) {
|
|
1326
|
+
return this._made.length < 2 || !this._parentHas(name);
|
|
1327
|
+
}
|
|
1328
|
+
reset(name, value, reversed) {
|
|
1329
|
+
const own = !this.reachesSiblings(name);
|
|
1330
|
+
const level = own ? this._made.length - 1 : this._made.length - 2;
|
|
1331
|
+
let stack = this._instances.get(name);
|
|
1332
|
+
if (!stack) {
|
|
1333
|
+
stack = [];
|
|
1334
|
+
this._instances.set(name, stack);
|
|
1335
|
+
}
|
|
1336
|
+
const top = stack[stack.length - 1];
|
|
1337
|
+
if (top?.level === level && top.own === own) {
|
|
1338
|
+
top.value = value;
|
|
1339
|
+
top.reversed = reversed;
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
stack.push({ level, own, value, reversed });
|
|
1343
|
+
(this._made[level] ??= []).push(name);
|
|
1344
|
+
}
|
|
1345
|
+
/** The innermost instance, made here at 0 where there is none. */
|
|
1346
|
+
_innermost(name) {
|
|
1347
|
+
let stack = this._instances.get(name);
|
|
1348
|
+
if (!stack?.length) {
|
|
1349
|
+
this.reset(name, 0, false);
|
|
1350
|
+
stack = this._instances.get(name);
|
|
1351
|
+
}
|
|
1352
|
+
return stack[stack.length - 1];
|
|
1353
|
+
}
|
|
1354
|
+
increment(name, by) {
|
|
1355
|
+
const instance = this._counted(name);
|
|
1356
|
+
instance.value = clampCounter(instance.value + by);
|
|
1357
|
+
}
|
|
1358
|
+
set(name, value) {
|
|
1359
|
+
this._counted(name).value = value;
|
|
1360
|
+
}
|
|
1361
|
+
/** The instance an increment or a set counts on: the innermost, unless
|
|
1362
|
+
* it was made outside an element with style containment that this is
|
|
1363
|
+
* inside, where a new one is made instead, as though this reset it for
|
|
1364
|
+
* itself and its later siblings — which one outside the element's parent
|
|
1365
|
+
* then does not see (CSS Containment 2, 3.4). */
|
|
1366
|
+
_counted(name) {
|
|
1367
|
+
const instance = this._innermost(name);
|
|
1368
|
+
const bound = this._contained[this._contained.length - 1];
|
|
1369
|
+
const level = this._made.length - 1;
|
|
1370
|
+
if (bound === undefined || level <= bound || instance.level >= bound) {
|
|
1371
|
+
return instance;
|
|
1372
|
+
}
|
|
1373
|
+
const parent = level - 1;
|
|
1374
|
+
const fresh = { level: parent, own: false, value: 0, reversed: false };
|
|
1375
|
+
this._instances.get(name).push(fresh);
|
|
1376
|
+
(this._made[parent] ??= []).push(name);
|
|
1377
|
+
return fresh;
|
|
1378
|
+
}
|
|
1379
|
+
/** Whether the innermost instance counts down: a list item takes one
|
|
1380
|
+
* from it rather than adding one. */
|
|
1381
|
+
reversed(name) {
|
|
1382
|
+
return this._innermost(name).reversed;
|
|
1383
|
+
}
|
|
1384
|
+
value(name) {
|
|
1385
|
+
return this._innermost(name).value;
|
|
1386
|
+
}
|
|
1387
|
+
values(name) {
|
|
1388
|
+
this._innermost(name);
|
|
1389
|
+
return this._instances.get(name).map((instance) => instance.value);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
/** A counter is a 32-bit integer in a browser, and saturates there. */
|
|
1393
|
+
function clampCounter(value) {
|
|
1394
|
+
return Math.max(-2147483648, Math.min(2147483647, value));
|
|
1395
|
+
}
|
|
327
1396
|
/** Whether the parsed document has a `<body>`. htmlparser2 does not
|
|
328
1397
|
* synthesise one — it parses what it was given — so a fragment has none,
|
|
329
1398
|
* and the root box stands in for it. */
|
|
1399
|
+
function hasHtml(root) {
|
|
1400
|
+
for (const child of childrenOf(root)) {
|
|
1401
|
+
if (isElement(child) && tagOf(child) === 'html')
|
|
1402
|
+
return true;
|
|
1403
|
+
}
|
|
1404
|
+
return false;
|
|
1405
|
+
}
|
|
330
1406
|
function hasBody(root) {
|
|
331
1407
|
for (const child of childrenOf(root)) {
|
|
332
1408
|
if (!isElement(child))
|
|
@@ -339,6 +1415,13 @@ function hasBody(root) {
|
|
|
339
1415
|
}
|
|
340
1416
|
return false;
|
|
341
1417
|
}
|
|
1418
|
+
/** A canvas's bitmap dimension: a non-negative integer, as HTML parses
|
|
1419
|
+
* one, or its default where the attribute is missing or no number. */
|
|
1420
|
+
function canvasSize(el, name, fallback) {
|
|
1421
|
+
const raw = attr(el, name);
|
|
1422
|
+
const n = raw == null ? NaN : parseInt(raw.trim(), 10);
|
|
1423
|
+
return Number.isFinite(n) && n >= 0 ? n : fallback;
|
|
1424
|
+
}
|
|
342
1425
|
function numberAttr(el, name) {
|
|
343
1426
|
const raw = attr(el, name);
|
|
344
1427
|
if (!raw)
|
|
@@ -346,28 +1429,222 @@ function numberAttr(el, name) {
|
|
|
346
1429
|
const n = parseFloat(raw);
|
|
347
1430
|
return Number.isFinite(n) ? n : null;
|
|
348
1431
|
}
|
|
349
|
-
function
|
|
350
|
-
for (let i = box.children.length - 1; i >= 0; i -= 1) {
|
|
351
|
-
const kind = box.children[i].kind;
|
|
352
|
-
if (kind === 'text' || kind === 'inline' || kind === 'replaced')
|
|
353
|
-
return true;
|
|
354
|
-
if (kind === 'block' || kind === 'flex' || kind === 'table')
|
|
355
|
-
return false;
|
|
356
|
-
}
|
|
357
|
-
return false;
|
|
358
|
-
}
|
|
359
|
-
function transformText(text, transform) {
|
|
1432
|
+
function transformText(text, transform, lettered = false) {
|
|
360
1433
|
switch (transform) {
|
|
361
1434
|
case 'uppercase':
|
|
362
1435
|
return text.toUpperCase();
|
|
363
1436
|
case 'lowercase':
|
|
364
1437
|
return text.toLowerCase();
|
|
365
1438
|
case 'capitalize':
|
|
366
|
-
return text
|
|
1439
|
+
return capitalize(text, lettered);
|
|
367
1440
|
default:
|
|
368
1441
|
return text;
|
|
369
1442
|
}
|
|
370
1443
|
}
|
|
1444
|
+
/** A letter or a number: what a word's first typographic letter unit is
|
|
1445
|
+
* (CSS Text 3, 2.1). */
|
|
1446
|
+
const LETTER_UNIT = /[\p{L}\p{N}]/u;
|
|
1447
|
+
/**
|
|
1448
|
+
* What does not end a word (UAX #29): a combining mark, and the
|
|
1449
|
+
* punctuation that joins letters into one — `x.y`, `don't`, `a:b`. Any
|
|
1450
|
+
* other character that is not a letter or a number is between words: a
|
|
1451
|
+
* space, a no-break space, a hyphen, a bracket.
|
|
1452
|
+
*/
|
|
1453
|
+
const IN_WORD = /[\p{M}'.:\u00b7\u0387\u05f4\u2018\u2019\u2024\u2027\ufe13\ufe52\ufe55\uff07\uff0e\uff1a]/u;
|
|
1454
|
+
/**
|
|
1455
|
+
* `text-transform: capitalize` (CSS Text 3, 2.1): the first letter or
|
|
1456
|
+
* number of each word in upper case, and nothing else. Punctuation a word
|
|
1457
|
+
* starts with is not its first letter — `(p.p.)` is `(P.p.)` — and a word
|
|
1458
|
+
* the text continues, `lettered`, has had its own: `<b>fo</b>o` is `Foo`.
|
|
1459
|
+
*/
|
|
1460
|
+
function capitalize(text, lettered) {
|
|
1461
|
+
let out = '';
|
|
1462
|
+
let from = 0;
|
|
1463
|
+
let at = 0;
|
|
1464
|
+
for (const ch of text) {
|
|
1465
|
+
if (LETTER_UNIT.test(ch)) {
|
|
1466
|
+
if (!lettered) {
|
|
1467
|
+
const title = titleCase(ch);
|
|
1468
|
+
if (title !== ch) {
|
|
1469
|
+
out += text.slice(from, at) + title;
|
|
1470
|
+
from = at + ch.length;
|
|
1471
|
+
}
|
|
1472
|
+
lettered = true;
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
else if (!IN_WORD.test(ch)) {
|
|
1476
|
+
lettered = false;
|
|
1477
|
+
}
|
|
1478
|
+
at += ch.length;
|
|
1479
|
+
}
|
|
1480
|
+
return from ? out + text.slice(from) : text;
|
|
1481
|
+
}
|
|
1482
|
+
/**
|
|
1483
|
+
* A letter in title case (Unicode's SpecialCasing), which is its upper case
|
|
1484
|
+
* but for a letter that is two: a digraph is capitalized as its first
|
|
1485
|
+
* letter alone — `dž` as `Dž`, where upper case is `DŽ` — and a Greek vowel
|
|
1486
|
+
* with a subscript iota keeps the iota subscript, where upper case writes
|
|
1487
|
+
* it out. A letter whose upper case is two letters, as `ß` is `SS`, is the
|
|
1488
|
+
* first of them: `Ss`.
|
|
1489
|
+
*/
|
|
1490
|
+
function titleCase(ch) {
|
|
1491
|
+
const cp = ch.codePointAt(0);
|
|
1492
|
+
if (cp >= 0x1c4 && cp <= 0x1cc) {
|
|
1493
|
+
return String.fromCharCode(0x1c5 + 3 * Math.floor((cp - 0x1c4) / 3));
|
|
1494
|
+
}
|
|
1495
|
+
if (cp >= 0x1f1 && cp <= 0x1f3)
|
|
1496
|
+
return '\u01f2';
|
|
1497
|
+
if (cp >= 0x1f80 && cp <= 0x1faf)
|
|
1498
|
+
return String.fromCharCode(cp | 0x08);
|
|
1499
|
+
if (cp === 0x1fb3 || cp === 0x1fc3 || cp === 0x1ff3) {
|
|
1500
|
+
return String.fromCharCode(cp + 9);
|
|
1501
|
+
}
|
|
1502
|
+
if (cp === 0x1fbc || cp === 0x1fcc || cp === 0x1ffc)
|
|
1503
|
+
return ch;
|
|
1504
|
+
const upper = ch.toUpperCase();
|
|
1505
|
+
if (upper.length <= ch.length)
|
|
1506
|
+
return upper;
|
|
1507
|
+
const first = String.fromCodePoint(upper.codePointAt(0));
|
|
1508
|
+
return first + upper.slice(first.length).toLowerCase();
|
|
1509
|
+
}
|
|
1510
|
+
/**
|
|
1511
|
+
* Whether a word is under way after `text`, as `capitalize` reads words: its
|
|
1512
|
+
* last letter, number or character between words decides, and a text of
|
|
1513
|
+
* nothing but joining punctuation leaves it as it was. Asked of every text,
|
|
1514
|
+
* from the end, where it is almost always decided by the last character.
|
|
1515
|
+
*/
|
|
1516
|
+
function letteredAfter(text, lettered) {
|
|
1517
|
+
for (let i = text.length - 1; i >= 0; i -= 1) {
|
|
1518
|
+
const c = text.charCodeAt(i);
|
|
1519
|
+
if (c < 0x80) {
|
|
1520
|
+
if ((c >= 0x61 && c <= 0x7a) ||
|
|
1521
|
+
(c >= 0x41 && c <= 0x5a) ||
|
|
1522
|
+
(c >= 0x30 && c <= 0x39)) {
|
|
1523
|
+
return true;
|
|
1524
|
+
}
|
|
1525
|
+
if (c === 0x27 || c === 0x2e || c === 0x3a)
|
|
1526
|
+
continue;
|
|
1527
|
+
return false;
|
|
1528
|
+
}
|
|
1529
|
+
// a low surrogate is read with the high one before it
|
|
1530
|
+
const start = c >= 0xdc00 && c <= 0xdfff && i > 0 && isHighSurrogate(text, i - 1)
|
|
1531
|
+
? i - 1
|
|
1532
|
+
: i;
|
|
1533
|
+
const ch = text.slice(start, i + 1);
|
|
1534
|
+
if (LETTER_UNIT.test(ch))
|
|
1535
|
+
return true;
|
|
1536
|
+
if (!IN_WORD.test(ch))
|
|
1537
|
+
return false;
|
|
1538
|
+
i = start;
|
|
1539
|
+
}
|
|
1540
|
+
return lettered;
|
|
1541
|
+
}
|
|
1542
|
+
function isHighSurrogate(text, i) {
|
|
1543
|
+
const c = text.charCodeAt(i);
|
|
1544
|
+
return c >= 0xd800 && c <= 0xdbff;
|
|
1545
|
+
}
|
|
1546
|
+
/** Whether a word is under way after a box, as `after` says where white
|
|
1547
|
+
* space stands: an inline box leaves it as its content did, an atomic
|
|
1548
|
+
* inline and a block end it, and a float or a positioned box leaves the
|
|
1549
|
+
* word around it as it was. */
|
|
1550
|
+
function letteredAfterFlow(flow, inside, before) {
|
|
1551
|
+
if (flow === 'inline')
|
|
1552
|
+
return inside;
|
|
1553
|
+
if (flow === 'out')
|
|
1554
|
+
return before;
|
|
1555
|
+
return false;
|
|
1556
|
+
}
|
|
1557
|
+
/**
|
|
1558
|
+
* A text's first letter, with the punctuation before and after it that CSS
|
|
1559
|
+
* 2.1 5.12.2 counts in — the Ps, Pe, Pi, Pf and Po classes — and the white
|
|
1560
|
+
* space before it in the first group. No match when the text ends, or
|
|
1561
|
+
* reaches a space, before any letter: its first letter is in a later text,
|
|
1562
|
+
* as browsers read it.
|
|
1563
|
+
*/
|
|
1564
|
+
const FIRST_LETTER = /^([ \t\n\r\f\u00a0]*)(?:[\p{Ps}\p{Pe}\p{Pi}\p{Pf}\p{Po}]\p{M}*)*[^ \t\n\r\f\u00a0\p{Ps}\p{Pe}\p{Pi}\p{Pf}\p{Po}]\p{M}*(?:[\p{Ps}\p{Pe}\p{Pi}\p{Pf}\p{Po}]\p{M}*)*/u;
|
|
1565
|
+
/** A text that is punctuation and nothing else, but for the white space
|
|
1566
|
+
* before it in the first group. */
|
|
1567
|
+
const PUNCTUATION_ONLY = /^([ \t\n\r\f\u00a0]*)(?:[\p{Ps}\p{Pe}\p{Pi}\p{Pf}\p{Po}]\p{M}*)+$/u;
|
|
1568
|
+
/** Put punctuation a search styled back in its own box and style: the
|
|
1569
|
+
* letter it was waiting for is not on its line. */
|
|
1570
|
+
function giveBack(search) {
|
|
1571
|
+
for (const { box, text, style } of search.punctuation) {
|
|
1572
|
+
const parent = box.parent;
|
|
1573
|
+
if (!parent)
|
|
1574
|
+
continue;
|
|
1575
|
+
const at = parent.children.indexOf(box);
|
|
1576
|
+
if (at < 0)
|
|
1577
|
+
continue;
|
|
1578
|
+
parent.children[at] = text;
|
|
1579
|
+
text.parent = parent;
|
|
1580
|
+
text.style = style;
|
|
1581
|
+
}
|
|
1582
|
+
search.punctuation.length = 0;
|
|
1583
|
+
}
|
|
1584
|
+
/** Whether a box of this display is a block container, which is what can
|
|
1585
|
+
* have a first letter. */
|
|
1586
|
+
function hasFirstLetter(display) {
|
|
1587
|
+
switch (display) {
|
|
1588
|
+
case 'block':
|
|
1589
|
+
case 'inline-block':
|
|
1590
|
+
case 'list-item':
|
|
1591
|
+
case 'table-cell':
|
|
1592
|
+
case 'table-caption':
|
|
1593
|
+
return true;
|
|
1594
|
+
default:
|
|
1595
|
+
return false;
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
/**
|
|
1599
|
+
* How a box sits in its parent's inline content, for white space. An inline
|
|
1600
|
+
* box's content is its parent's own; an atomic inline — an inline-block, an
|
|
1601
|
+
* image — is one piece of it; a block ends the line it interrupts and starts
|
|
1602
|
+
* another; a float or a positioned box is no part of it at all.
|
|
1603
|
+
*/
|
|
1604
|
+
function flowOf(style, box) {
|
|
1605
|
+
if (box.outOfFlow || box.isFloat)
|
|
1606
|
+
return 'out';
|
|
1607
|
+
// an image told to be a table's part is an inline one (`isBlockLevel`)
|
|
1608
|
+
if (box.kind === 'replaced' && style.display.startsWith('table-')) {
|
|
1609
|
+
return 'atomic';
|
|
1610
|
+
}
|
|
1611
|
+
switch (style.display) {
|
|
1612
|
+
case 'inline':
|
|
1613
|
+
return box.kind === 'replaced' ? 'atomic' : 'inline';
|
|
1614
|
+
case 'inline-block':
|
|
1615
|
+
case 'inline-table':
|
|
1616
|
+
case 'inline-flex':
|
|
1617
|
+
return 'atomic';
|
|
1618
|
+
case 'table-row-group':
|
|
1619
|
+
case 'table-header-group':
|
|
1620
|
+
case 'table-footer-group':
|
|
1621
|
+
case 'table-row':
|
|
1622
|
+
case 'table-cell':
|
|
1623
|
+
case 'table-caption':
|
|
1624
|
+
case 'table-column':
|
|
1625
|
+
case 'table-column-group':
|
|
1626
|
+
// table parts in an inline box are given an inline table around them
|
|
1627
|
+
// (CSS 2.1 17.2.1), which sits in the line as an inline-block does:
|
|
1628
|
+
// the white space either side of it is the line's
|
|
1629
|
+
return box.parent?.kind === 'inline' ? 'atomic' : 'block';
|
|
1630
|
+
default:
|
|
1631
|
+
return 'block';
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
/** Where the inline content stands after a box, from where it stood inside
|
|
1635
|
+
* the box and before it. */
|
|
1636
|
+
function after(flow, inside, before) {
|
|
1637
|
+
switch (flow) {
|
|
1638
|
+
case 'inline':
|
|
1639
|
+
return inside;
|
|
1640
|
+
case 'atomic':
|
|
1641
|
+
return 'content';
|
|
1642
|
+
case 'out':
|
|
1643
|
+
return before;
|
|
1644
|
+
default:
|
|
1645
|
+
return 'start';
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
371
1648
|
function boxKindFor(display) {
|
|
372
1649
|
switch (display) {
|
|
373
1650
|
case 'inline':
|
|
@@ -400,18 +1677,37 @@ function boxKindFor(display) {
|
|
|
400
1677
|
return 'block';
|
|
401
1678
|
}
|
|
402
1679
|
}
|
|
1680
|
+
/** A replaced box's intrinsic size from what its content says, in CSS
|
|
1681
|
+
* pixels: an axis it does not give takes the default object size. */
|
|
1682
|
+
function setIntrinsics(box, size, scale) {
|
|
1683
|
+
box.intrinsic = {
|
|
1684
|
+
width: (size.width ?? 300) * scale,
|
|
1685
|
+
height: (size.height ?? 150) * scale,
|
|
1686
|
+
missing: (size.width === null ? 1 : 0) | (size.height === null ? 2 : 0),
|
|
1687
|
+
ratio: size.ratio,
|
|
1688
|
+
};
|
|
1689
|
+
}
|
|
403
1690
|
function replacedKind(el, tag) {
|
|
404
1691
|
switch (tag) {
|
|
405
1692
|
case 'img':
|
|
406
1693
|
return 'image';
|
|
1694
|
+
case 'svg':
|
|
1695
|
+
// XHTML's `<svg:svg>` too, which `tagOf` names `svg`
|
|
1696
|
+
return 'svg';
|
|
407
1697
|
case 'hr':
|
|
408
1698
|
return 'hr';
|
|
1699
|
+
case 'iframe':
|
|
1700
|
+
case 'video':
|
|
1701
|
+
case 'embed':
|
|
1702
|
+
case 'canvas':
|
|
1703
|
+
return 'frame';
|
|
409
1704
|
case 'textarea':
|
|
410
1705
|
return 'textarea';
|
|
411
1706
|
case 'select':
|
|
412
1707
|
return 'select';
|
|
413
|
-
|
|
414
|
-
|
|
1708
|
+
// A `<button>` is not one: its content is the document's, laid out and
|
|
1709
|
+
// drawn like any box's (the UA sheet's `button` rule), where an
|
|
1710
|
+
// `<input type=submit>` has only a value to show and is a widget.
|
|
415
1711
|
case 'input': {
|
|
416
1712
|
const type = (attr(el, 'type') ?? 'text').toLowerCase();
|
|
417
1713
|
if (type === 'checkbox')
|
|
@@ -428,82 +1724,28 @@ function replacedKind(el, tag) {
|
|
|
428
1724
|
return 'none';
|
|
429
1725
|
}
|
|
430
1726
|
}
|
|
431
|
-
const ROMAN = [
|
|
432
|
-
[1000, 'm'],
|
|
433
|
-
[900, 'cm'],
|
|
434
|
-
[500, 'd'],
|
|
435
|
-
[400, 'cd'],
|
|
436
|
-
[100, 'c'],
|
|
437
|
-
[90, 'xc'],
|
|
438
|
-
[50, 'l'],
|
|
439
|
-
[40, 'xl'],
|
|
440
|
-
[10, 'x'],
|
|
441
|
-
[9, 'ix'],
|
|
442
|
-
[5, 'v'],
|
|
443
|
-
[4, 'iv'],
|
|
444
|
-
[1, 'i'],
|
|
445
|
-
];
|
|
446
1727
|
/**
|
|
447
|
-
* The marker a `list-item` draws
|
|
448
|
-
*
|
|
449
|
-
*
|
|
450
|
-
*
|
|
1728
|
+
* The marker a `list-item` draws: its `list-item` counter written in its
|
|
1729
|
+
* `list-style-type`, between the style's prefix and suffix, or the string
|
|
1730
|
+
* the type is. The counter is the builder's (`CounterScopes`), which is
|
|
1731
|
+
* what lets `value` on an `<li>`, `start` and `reversed` on an `<ol>`, a
|
|
1732
|
+
* list in a list and an author's own `counter-reset` and `counter-set` all
|
|
1733
|
+
* count as a browser counts them. A suffix that ends in a space — `. `, a
|
|
1734
|
+
* bullet's — is written without it, and the layout sets the marker off by
|
|
1735
|
+
* its own gap; one that does not, `、`, sets the marker against the text.
|
|
451
1736
|
*/
|
|
452
|
-
function markerFor(
|
|
1737
|
+
function markerFor(style, n, styles) {
|
|
453
1738
|
const type = style.listStyleType;
|
|
454
1739
|
if (type === 'none')
|
|
455
|
-
return '';
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
case 'decimal':
|
|
465
|
-
return `${n}.`;
|
|
466
|
-
case 'decimal-leading-zero':
|
|
467
|
-
return `${n < 10 ? '0' : ''}${n}.`;
|
|
468
|
-
case 'lower-alpha':
|
|
469
|
-
case 'lower-latin':
|
|
470
|
-
return `${alpha(n).toLowerCase()}.`;
|
|
471
|
-
case 'upper-alpha':
|
|
472
|
-
case 'upper-latin':
|
|
473
|
-
return `${alpha(n)}.`;
|
|
474
|
-
case 'lower-roman':
|
|
475
|
-
return `${roman(n)}.`;
|
|
476
|
-
case 'upper-roman':
|
|
477
|
-
return `${roman(n).toUpperCase()}.`;
|
|
478
|
-
case 'circle':
|
|
479
|
-
return '◦';
|
|
480
|
-
case 'square':
|
|
481
|
-
return '▪';
|
|
482
|
-
case 'disc':
|
|
483
|
-
default:
|
|
484
|
-
return '•';
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
function alpha(n) {
|
|
488
|
-
let out = '';
|
|
489
|
-
let v = Math.max(1, n);
|
|
490
|
-
while (v > 0) {
|
|
491
|
-
const rem = (v - 1) % 26;
|
|
492
|
-
out = String.fromCharCode(65 + rem) + out;
|
|
493
|
-
v = Math.floor((v - 1) / 26);
|
|
494
|
-
}
|
|
495
|
-
return out;
|
|
496
|
-
}
|
|
497
|
-
function roman(n) {
|
|
498
|
-
let v = Math.max(1, Math.min(3999, n));
|
|
499
|
-
let out = '';
|
|
500
|
-
for (const [value, sym] of ROMAN) {
|
|
501
|
-
while (v >= value) {
|
|
502
|
-
out += sym;
|
|
503
|
-
v -= value;
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
return out;
|
|
1740
|
+
return { text: '', flush: false };
|
|
1741
|
+
if (type.startsWith('"'))
|
|
1742
|
+
return { text: type.slice(1), flush: true };
|
|
1743
|
+
const { prefix, text, suffix } = styles.marker(n, type, style.direction === 'rtl');
|
|
1744
|
+
const spaced = /\s$/.test(suffix);
|
|
1745
|
+
return {
|
|
1746
|
+
text: prefix + text + (spaced ? suffix.trimEnd() : suffix),
|
|
1747
|
+
flush: !spaced,
|
|
1748
|
+
};
|
|
507
1749
|
}
|
|
508
1750
|
/**
|
|
509
1751
|
* Give every box the document range its subtree covers. Runs after `fixUp`,
|
|
@@ -546,23 +1788,38 @@ function assignSubtreeRanges(box) {
|
|
|
546
1788
|
* straight to `<tr>`, and CSS says the missing row group is generated
|
|
547
1789
|
* rather than the rows being dropped.
|
|
548
1790
|
*/
|
|
549
|
-
function fixUp(box) {
|
|
1791
|
+
function fixUp(box, anonymous) {
|
|
550
1792
|
for (const child of box.children)
|
|
551
|
-
fixUp(child);
|
|
1793
|
+
fixUp(child, anonymous);
|
|
1794
|
+
fixUpOwn(box, anonymous);
|
|
1795
|
+
}
|
|
1796
|
+
/** `fixUp` of a box whose children have had theirs. */
|
|
1797
|
+
function fixUpOwn(box, anonymous) {
|
|
552
1798
|
if (box.kind === 'table') {
|
|
553
|
-
fixUpTable(box);
|
|
1799
|
+
fixUpTable(box, anonymous);
|
|
554
1800
|
return;
|
|
555
1801
|
}
|
|
556
1802
|
if (box.kind === 'table-row-group') {
|
|
557
|
-
wrapOrphans(box, 'table-row', (k) => k === 'table-row');
|
|
1803
|
+
wrapOrphans(box, 'table-row', (k) => k === 'table-row', anonymous);
|
|
558
1804
|
return;
|
|
559
1805
|
}
|
|
560
1806
|
if (box.kind === 'table-row') {
|
|
561
|
-
wrapOrphans(box, 'table-cell', (k) => k === 'table-cell');
|
|
1807
|
+
wrapOrphans(box, 'table-cell', (k) => k === 'table-cell', anonymous);
|
|
562
1808
|
return;
|
|
563
1809
|
}
|
|
1810
|
+
// a column group holds its columns, which are where they belong, and the
|
|
1811
|
+
// builder kept nothing else in it
|
|
1812
|
+
if (box.style.display === 'table-column-group')
|
|
1813
|
+
return;
|
|
564
1814
|
if (!box.children.length)
|
|
565
1815
|
return;
|
|
1816
|
+
wrapTableParts(box, anonymous);
|
|
1817
|
+
// An inline box holds no block: a block in one is its block container's,
|
|
1818
|
+
// which breaks the inline box around it
|
|
1819
|
+
if (box.kind === 'inline')
|
|
1820
|
+
return;
|
|
1821
|
+
if (box.kind !== 'flex')
|
|
1822
|
+
breakInlines(box);
|
|
566
1823
|
let hasBlockLevel = false;
|
|
567
1824
|
let hasInlineLevel = false;
|
|
568
1825
|
for (const child of box.children) {
|
|
@@ -591,7 +1848,7 @@ function fixUp(box) {
|
|
|
591
1848
|
// finds itself; it does not force an anonymous block on its own.
|
|
592
1849
|
if (isBlockLevel(child) && !child.outOfFlow && !child.isFloat) {
|
|
593
1850
|
if (run) {
|
|
594
|
-
next.push(
|
|
1851
|
+
next.push(anonymousOf(box, 'block', run, anonymous));
|
|
595
1852
|
run = null;
|
|
596
1853
|
}
|
|
597
1854
|
next.push(child);
|
|
@@ -599,28 +1856,140 @@ function fixUp(box) {
|
|
|
599
1856
|
}
|
|
600
1857
|
// Whitespace between two blocks is not content and must not generate a
|
|
601
1858
|
// line box — `<div><p>a</p> <p>b</p></div>` has no blank line in it.
|
|
602
|
-
if (!run && child.kind === 'text' &&
|
|
1859
|
+
if (!run && child.kind === 'text' && isBlank(child.text))
|
|
603
1860
|
continue;
|
|
604
1861
|
(run ??= []).push(child);
|
|
605
1862
|
}
|
|
606
1863
|
if (run) {
|
|
607
|
-
if (run.every((c) => c.kind === 'text' &&
|
|
1864
|
+
if (run.every((c) => c.kind === 'text' && isBlank(c.text))) {
|
|
608
1865
|
// trailing whitespace after the last block: same rule
|
|
609
1866
|
}
|
|
610
1867
|
else {
|
|
611
|
-
next.push(
|
|
1868
|
+
next.push(anonymousOf(box, 'block', run, anonymous));
|
|
612
1869
|
}
|
|
613
1870
|
}
|
|
614
1871
|
box.children = next;
|
|
615
1872
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
1873
|
+
/**
|
|
1874
|
+
* Break a block container's inline children around the blocks in them
|
|
1875
|
+
* (CSS 2.1 9.2.1.1): an inline box with an in-flow block in it — at any
|
|
1876
|
+
* depth of inline box — becomes the pieces of it before, between and after
|
|
1877
|
+
* its blocks, and the blocks stand beside them as the container's own, for
|
|
1878
|
+
* the mixed-content rule to wrap the pieces in anonymous blocks.
|
|
1879
|
+
* `<font><p>One</p><p>Two</p></font>` laid its paragraphs side by side as
|
|
1880
|
+
* inline-blocks, and a link around a card's blocks set them in a line.
|
|
1881
|
+
*/
|
|
1882
|
+
function breakInlines(box) {
|
|
1883
|
+
let next = null;
|
|
1884
|
+
for (let i = 0; i < box.children.length; i += 1) {
|
|
1885
|
+
const child = box.children[i];
|
|
1886
|
+
const pieces = child.kind === 'inline' && holdsBlock(child) ? breakAround(child) : null;
|
|
1887
|
+
if (!pieces) {
|
|
1888
|
+
next?.push(child);
|
|
1889
|
+
continue;
|
|
1890
|
+
}
|
|
1891
|
+
next ??= box.children.slice(0, i);
|
|
1892
|
+
for (const piece of pieces) {
|
|
1893
|
+
piece.parent = box;
|
|
1894
|
+
next.push(piece);
|
|
1895
|
+
}
|
|
622
1896
|
}
|
|
623
|
-
|
|
1897
|
+
if (next)
|
|
1898
|
+
box.children = next;
|
|
1899
|
+
}
|
|
1900
|
+
/** Whether an inline box has an in-flow block in it, at any depth of
|
|
1901
|
+
* inline box: asked first, and without making anything, since nearly
|
|
1902
|
+
* every inline box has none. */
|
|
1903
|
+
function holdsBlock(inline) {
|
|
1904
|
+
for (const child of inline.children) {
|
|
1905
|
+
if (child.kind === 'inline') {
|
|
1906
|
+
if (holdsBlock(child))
|
|
1907
|
+
return true;
|
|
1908
|
+
}
|
|
1909
|
+
else if (isBlockLevel(child) && !child.outOfFlow && !child.isFloat) {
|
|
1910
|
+
return true;
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
return false;
|
|
1914
|
+
}
|
|
1915
|
+
/**
|
|
1916
|
+
* An inline box broken around the in-flow blocks in it: its pieces, each a
|
|
1917
|
+
* box of its element and style holding the content between two blocks,
|
|
1918
|
+
* with the blocks between them; null where it has none, which is nearly
|
|
1919
|
+
* every inline box. A piece has no edge on a side a block cut: the start of
|
|
1920
|
+
* the first and the end of the last are the box's own.
|
|
1921
|
+
*/
|
|
1922
|
+
function breakAround(inline) {
|
|
1923
|
+
let out = null;
|
|
1924
|
+
const pieces = [];
|
|
1925
|
+
let run = [];
|
|
1926
|
+
const close = () => {
|
|
1927
|
+
const piece = new Box('inline', inline.el, inline.style);
|
|
1928
|
+
piece.pseudo = inline.pseudo;
|
|
1929
|
+
for (const child of run)
|
|
1930
|
+
piece.append(child);
|
|
1931
|
+
pieces.push(piece);
|
|
1932
|
+
(out ??= []).push(piece);
|
|
1933
|
+
run = [];
|
|
1934
|
+
};
|
|
1935
|
+
const blocks = [];
|
|
1936
|
+
for (const child of inline.children) {
|
|
1937
|
+
if (isBlockLevel(child) && !child.outOfFlow && !child.isFloat) {
|
|
1938
|
+
close();
|
|
1939
|
+
out.push(child);
|
|
1940
|
+
blocks.push(child);
|
|
1941
|
+
continue;
|
|
1942
|
+
}
|
|
1943
|
+
const inner = child.kind === 'inline' && holdsBlock(child) ? breakAround(child) : null;
|
|
1944
|
+
if (!inner) {
|
|
1945
|
+
run.push(child);
|
|
1946
|
+
continue;
|
|
1947
|
+
}
|
|
1948
|
+
for (const piece of inner) {
|
|
1949
|
+
if (piece.kind === 'inline') {
|
|
1950
|
+
run.push(piece);
|
|
1951
|
+
}
|
|
1952
|
+
else {
|
|
1953
|
+
close();
|
|
1954
|
+
out.push(piece);
|
|
1955
|
+
blocks.push(piece);
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
if (!out)
|
|
1960
|
+
return null;
|
|
1961
|
+
close();
|
|
1962
|
+
for (let i = 0; i < pieces.length; i += 1) {
|
|
1963
|
+
pieces[i].cut = (i > 0 ? 1 : 0) | (i < pieces.length - 1 ? 2 : 0);
|
|
1964
|
+
}
|
|
1965
|
+
if (isRelative(inline.style))
|
|
1966
|
+
CUT_BLOCKS.set(pieces[0], blocks);
|
|
1967
|
+
const fade = inline.style.opacity;
|
|
1968
|
+
if (fade < 1) {
|
|
1969
|
+
for (const block of blocks) {
|
|
1970
|
+
FADED_BLOCKS.set(block, (FADED_BLOCKS.get(block) ?? 1) * fade);
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
return out;
|
|
1974
|
+
}
|
|
1975
|
+
function anonymousStyles(initial) {
|
|
1976
|
+
const made = new WeakMap();
|
|
1977
|
+
return (parent, display) => {
|
|
1978
|
+
let byDisplay = made.get(parent.style);
|
|
1979
|
+
if (!byDisplay) {
|
|
1980
|
+
byDisplay = new Map();
|
|
1981
|
+
made.set(parent.style, byDisplay);
|
|
1982
|
+
}
|
|
1983
|
+
let style = byDisplay.get(display);
|
|
1984
|
+
if (!style) {
|
|
1985
|
+
// `display` is the one property it does not start from: it is the
|
|
1986
|
+
// box the fix-up made, and layout asks the style what a box is
|
|
1987
|
+
style = inherit(parent.style, initial);
|
|
1988
|
+
style.display = display;
|
|
1989
|
+
byDisplay.set(display, style);
|
|
1990
|
+
}
|
|
1991
|
+
return style;
|
|
1992
|
+
};
|
|
624
1993
|
}
|
|
625
1994
|
function isBlockLevel(box) {
|
|
626
1995
|
switch (box.kind) {
|
|
@@ -634,6 +2003,15 @@ function isBlockLevel(box) {
|
|
|
634
2003
|
// An `inline-block` or `inline-flex` is a block *container* with an
|
|
635
2004
|
// inline-level outer role, so it belongs to the inline run around it.
|
|
636
2005
|
return !isInlineLevelDisplay(box.style.display);
|
|
2006
|
+
case 'replaced':
|
|
2007
|
+
// an image is inline unless it is told otherwise, and then it is a
|
|
2008
|
+
// block: `img { display: block }`, which mail writes to lose the gap
|
|
2009
|
+
// under its images, stacks them. A table's part is no display it
|
|
2010
|
+
// can take, and one told to be a cell is inline (CSS Display 3,
|
|
2011
|
+
// 2.4), which is what a table wraps a cell around.
|
|
2012
|
+
return (box.style.display !== 'inline' &&
|
|
2013
|
+
!isInlineLevelDisplay(box.style.display) &&
|
|
2014
|
+
!box.style.display.startsWith('table-'));
|
|
637
2015
|
default:
|
|
638
2016
|
return false;
|
|
639
2017
|
}
|
|
@@ -644,7 +2022,7 @@ function isInlineLevelDisplay(display) {
|
|
|
644
2022
|
display === 'inline-table');
|
|
645
2023
|
}
|
|
646
2024
|
/** Wrap children that are not of `expect` in an anonymous box that is. */
|
|
647
|
-
function wrapOrphans(box, kind, accept) {
|
|
2025
|
+
function wrapOrphans(box, kind, accept, anonymous) {
|
|
648
2026
|
let needed = false;
|
|
649
2027
|
for (const child of box.children) {
|
|
650
2028
|
if (!accept(child.kind) && !isDroppableWhitespace(child)) {
|
|
@@ -654,27 +2032,50 @@ function wrapOrphans(box, kind, accept) {
|
|
|
654
2032
|
}
|
|
655
2033
|
if (!needed)
|
|
656
2034
|
return;
|
|
2035
|
+
// White space goes only between two of the parts, or beside one at an
|
|
2036
|
+
// end (CSS 2.1 17.2.1, rule 1): beside a child the fix-up wraps, it is
|
|
2037
|
+
// that child's run's, so `<span>a</span> <span>b</span>` in a row is one
|
|
2038
|
+
// anonymous cell of `a b`, where it was `ab`
|
|
657
2039
|
const next = [];
|
|
658
2040
|
let run = null;
|
|
2041
|
+
let space = [];
|
|
659
2042
|
for (const child of box.children) {
|
|
660
2043
|
if (accept(child.kind)) {
|
|
661
2044
|
if (run) {
|
|
662
|
-
|
|
2045
|
+
run.push(...space);
|
|
2046
|
+
next.push(anonymousOf(box, kind, run, anonymous));
|
|
663
2047
|
run = null;
|
|
664
2048
|
}
|
|
2049
|
+
space = [];
|
|
665
2050
|
next.push(child);
|
|
666
2051
|
continue;
|
|
667
2052
|
}
|
|
668
|
-
if (isDroppableWhitespace(child))
|
|
2053
|
+
if (isDroppableWhitespace(child)) {
|
|
2054
|
+
space.push(child);
|
|
669
2055
|
continue;
|
|
670
|
-
|
|
2056
|
+
}
|
|
2057
|
+
(run ??= []).push(...space, child);
|
|
2058
|
+
space = [];
|
|
2059
|
+
}
|
|
2060
|
+
if (run) {
|
|
2061
|
+
run.push(...space);
|
|
2062
|
+
next.push(anonymousOf(box, kind, run, anonymous));
|
|
671
2063
|
}
|
|
672
|
-
if (run)
|
|
673
|
-
next.push(anonymousOf(box, kind, run));
|
|
674
2064
|
box.children = next;
|
|
2065
|
+
// A cell made here is a block container the fix-up has not been to: its
|
|
2066
|
+
// children had theirs, as the table's, before it held them. Text beside a
|
|
2067
|
+
// block in it goes in an anonymous block as in any other (CSS 2.1
|
|
2068
|
+
// 9.2.1.1) — `<span style="display: inline-table">bcd<div>x</div>` laid
|
|
2069
|
+
// `bcd` out as nothing, and drew the table as wide as the `x`.
|
|
2070
|
+
if (kind === 'table-cell') {
|
|
2071
|
+
for (const child of next) {
|
|
2072
|
+
if (child.kind === 'table-cell' && !child.el)
|
|
2073
|
+
fixUpOwn(child, anonymous);
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
675
2076
|
}
|
|
676
|
-
function anonymousOf(parent, kind, run) {
|
|
677
|
-
const box = new Box(kind, null, parent
|
|
2077
|
+
function anonymousOf(parent, kind, run, anonymous, display = kind) {
|
|
2078
|
+
const box = new Box(kind, null, anonymous(parent, display));
|
|
678
2079
|
box.parent = parent;
|
|
679
2080
|
for (const child of run) {
|
|
680
2081
|
child.parent = box;
|
|
@@ -682,17 +2083,140 @@ function anonymousOf(parent, kind, run) {
|
|
|
682
2083
|
}
|
|
683
2084
|
return box;
|
|
684
2085
|
}
|
|
2086
|
+
/** White space alone, which the table fix-up drops between the parts of a
|
|
2087
|
+
* table whether it is kept or not (CSS 2.1 17.2.1, rule 1). Kept, under
|
|
2088
|
+
* `white-space: pre`, it made a cell of the line break before every row. */
|
|
685
2089
|
function isDroppableWhitespace(box) {
|
|
686
|
-
return box.kind === 'text' &&
|
|
2090
|
+
return box.kind === 'text' && isBlank(box.text);
|
|
2091
|
+
}
|
|
2092
|
+
/**
|
|
2093
|
+
* Whether a text is white space alone: CSS's white space, which is the
|
|
2094
|
+
* space, the tab and the three line breaks and nothing else (CSS Text 3,
|
|
2095
|
+
* 4.1). JavaScript's `\s` and `trim` take in the no-break space and the
|
|
2096
|
+
* other Unicode spaces too, so a `<p> </p>` spacer or a
|
|
2097
|
+
* `<td> </td>` beside a block was dropped as if it held nothing. Tested
|
|
2098
|
+
* rather than trimmed, which copies a paragraph's text to find out.
|
|
2099
|
+
*/
|
|
2100
|
+
export function isBlank(text) {
|
|
2101
|
+
return BLANK.test(text);
|
|
2102
|
+
}
|
|
2103
|
+
const BLANK = /^[ \t\n\r\f]*$/;
|
|
2104
|
+
/** White space that collapsing would change: anything but a lone space.
|
|
2105
|
+
* Most of a document's text has none, and is its own collapsed form. */
|
|
2106
|
+
const COLLAPSIBLE = /[\t\n\r\f]| {2}/;
|
|
2107
|
+
function isRelative(style) {
|
|
2108
|
+
return style.position === 'relative' || style.position === 'sticky';
|
|
2109
|
+
}
|
|
2110
|
+
/** Whether `position: relative` moves a box: an inset is set. An inline box
|
|
2111
|
+
* that is moved has its text laid out apart (`collect`), so one whose
|
|
2112
|
+
* insets resolve to nothing still costs its paragraph the layout of a line
|
|
2113
|
+
* at a time, which a box with every inset `auto` does not. */
|
|
2114
|
+
export function isOffset(style) {
|
|
2115
|
+
if (!isRelative(style))
|
|
2116
|
+
return false;
|
|
2117
|
+
return (style.top !== AUTO ||
|
|
2118
|
+
style.right !== AUTO ||
|
|
2119
|
+
style.bottom !== AUTO ||
|
|
2120
|
+
style.left !== AUTO);
|
|
2121
|
+
}
|
|
2122
|
+
/** Whether a style may put its box under its stacking context's flow: a
|
|
2123
|
+
* negative `z-index` on a positioned box, or on a flex item, which that
|
|
2124
|
+
* makes a stacking context as it is (`stacksLayers`). */
|
|
2125
|
+
function isNegative(style) {
|
|
2126
|
+
return typeof style.zIndex === 'number' && style.zIndex < 0;
|
|
2127
|
+
}
|
|
2128
|
+
/** A box that belongs inside a table: a row group, a row, a cell, a
|
|
2129
|
+
* caption or a column. Asked of every child of every box in a build, so
|
|
2130
|
+
* a text or an inline box is answered from its kind, without its style. */
|
|
2131
|
+
function isTablePart(box) {
|
|
2132
|
+
switch (box.kind) {
|
|
2133
|
+
case 'table-row-group':
|
|
2134
|
+
case 'table-row':
|
|
2135
|
+
case 'table-cell':
|
|
2136
|
+
case 'table-caption':
|
|
2137
|
+
return !box.outOfFlow && !box.isFloat;
|
|
2138
|
+
case 'block': {
|
|
2139
|
+
if (box.outOfFlow || box.isFloat)
|
|
2140
|
+
return false;
|
|
2141
|
+
const display = box.style.display;
|
|
2142
|
+
return display === 'table-column' || display === 'table-column-group';
|
|
2143
|
+
}
|
|
2144
|
+
default:
|
|
2145
|
+
return false;
|
|
2146
|
+
}
|
|
687
2147
|
}
|
|
688
|
-
|
|
2148
|
+
/**
|
|
2149
|
+
* Table parts outside a table get one around them (CSS 2.1 17.2.1, rule 3):
|
|
2150
|
+
* each run of them — the white space between them no part of it — is
|
|
2151
|
+
* wrapped in an anonymous table, an inline one where the parent is inline,
|
|
2152
|
+
* and the table is then completed as any other. Laid out on their own, two
|
|
2153
|
+
* cells side by side in a line were two inline-blocks with a space between
|
|
2154
|
+
* them, and in a block two blocks, one above the other.
|
|
2155
|
+
*/
|
|
2156
|
+
function wrapTableParts(box, anonymous) {
|
|
2157
|
+
if (!box.children.some(isTablePart))
|
|
2158
|
+
return;
|
|
2159
|
+
const display = box.kind === 'inline' ? 'inline-table' : 'table';
|
|
2160
|
+
const next = [];
|
|
2161
|
+
let run = null;
|
|
2162
|
+
let space = [];
|
|
2163
|
+
const flush = () => {
|
|
2164
|
+
if (!run)
|
|
2165
|
+
return;
|
|
2166
|
+
const table = anonymousOf(box, 'table', run, anonymous, display);
|
|
2167
|
+
fixUpTable(table, anonymous);
|
|
2168
|
+
next.push(table);
|
|
2169
|
+
run = null;
|
|
2170
|
+
};
|
|
2171
|
+
for (const child of box.children) {
|
|
2172
|
+
if (isTablePart(child)) {
|
|
2173
|
+
run ??= [];
|
|
2174
|
+
run.push(child);
|
|
2175
|
+
space = [];
|
|
2176
|
+
continue;
|
|
2177
|
+
}
|
|
2178
|
+
if (run && isDroppableWhitespace(child)) {
|
|
2179
|
+
// held back: between two parts it goes, after the last it stays
|
|
2180
|
+
space.push(child);
|
|
2181
|
+
continue;
|
|
2182
|
+
}
|
|
2183
|
+
flush();
|
|
2184
|
+
next.push(...space, child);
|
|
2185
|
+
space = [];
|
|
2186
|
+
}
|
|
2187
|
+
flush();
|
|
2188
|
+
next.push(...space);
|
|
2189
|
+
box.children = next;
|
|
2190
|
+
for (const child of next)
|
|
2191
|
+
child.parent = box;
|
|
2192
|
+
}
|
|
2193
|
+
function fixUpTable(table, anonymous) {
|
|
689
2194
|
const groups = [];
|
|
690
2195
|
const captions = [];
|
|
2196
|
+
const columns = [];
|
|
691
2197
|
let looseRows = null;
|
|
2198
|
+
let looseCells = null;
|
|
2199
|
+
const flushCells = () => {
|
|
2200
|
+
if (!looseCells)
|
|
2201
|
+
return;
|
|
2202
|
+
(looseRows ??= []).push(anonymousOf(table, 'table-row', looseCells, anonymous));
|
|
2203
|
+
looseCells = null;
|
|
2204
|
+
};
|
|
692
2205
|
for (const child of table.children) {
|
|
693
|
-
|
|
2206
|
+
const display = child.style.display;
|
|
2207
|
+
// an image told to be a column is an inline image, as one told to be a
|
|
2208
|
+
// cell is (`flowOf`): taken for a column, it was never drawn
|
|
2209
|
+
if ((display === 'table-column' || display === 'table-column-group') &&
|
|
2210
|
+
child.kind !== 'replaced') {
|
|
2211
|
+
// A column is the table's, beside its rows: it lays out nothing and
|
|
2212
|
+
// paints nothing (17.2.1). Taken for a stray child, it was wrapped in
|
|
2213
|
+
// a row of its own and drawn as a cell.
|
|
2214
|
+
columns.push(child);
|
|
2215
|
+
}
|
|
2216
|
+
else if (child.kind === 'table-row-group') {
|
|
2217
|
+
flushCells();
|
|
694
2218
|
if (looseRows) {
|
|
695
|
-
groups.push(anonymousOf(table, 'table-row-group', looseRows));
|
|
2219
|
+
groups.push(anonymousOf(table, 'table-row-group', looseRows, anonymous));
|
|
696
2220
|
looseRows = null;
|
|
697
2221
|
}
|
|
698
2222
|
groups.push(child);
|
|
@@ -701,27 +2225,35 @@ function fixUpTable(table) {
|
|
|
701
2225
|
captions.push(child);
|
|
702
2226
|
}
|
|
703
2227
|
else if (isDroppableWhitespace(child)) {
|
|
2228
|
+
// after a loose child it goes with the loose children, and the cells
|
|
2229
|
+
// they are wrapped in say whether it stays (`wrapOrphans`)
|
|
2230
|
+
if (looseCells)
|
|
2231
|
+
looseCells.push(child);
|
|
704
2232
|
continue;
|
|
705
2233
|
}
|
|
2234
|
+
else if (child.kind === 'table-row') {
|
|
2235
|
+
flushCells();
|
|
2236
|
+
(looseRows ??= []).push(child);
|
|
2237
|
+
}
|
|
706
2238
|
else {
|
|
707
|
-
//
|
|
708
|
-
//
|
|
709
|
-
//
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
: anonymousOf(table, 'table-row', [child]);
|
|
713
|
-
(looseRows ??= []).push(row);
|
|
2239
|
+
// Anything else that ended up here is a cell, or goes in one, and a
|
|
2240
|
+
// run of them shares an anonymous row — which is how a browser
|
|
2241
|
+
// rescues `<table>text</table>`, and how three cells in a table
|
|
2242
|
+
// with no row are one row of three rather than three rows of one.
|
|
2243
|
+
(looseCells ??= []).push(child);
|
|
714
2244
|
}
|
|
715
2245
|
}
|
|
716
|
-
|
|
717
|
-
|
|
2246
|
+
flushCells();
|
|
2247
|
+
if (looseRows) {
|
|
2248
|
+
groups.push(anonymousOf(table, 'table-row-group', looseRows, anonymous));
|
|
2249
|
+
}
|
|
718
2250
|
for (const group of groups) {
|
|
719
|
-
wrapOrphans(group, 'table-row', (k) => k === 'table-row');
|
|
2251
|
+
wrapOrphans(group, 'table-row', (k) => k === 'table-row', anonymous);
|
|
720
2252
|
for (const row of group.children) {
|
|
721
|
-
wrapOrphans(row, 'table-cell', (k) => k === 'table-cell');
|
|
2253
|
+
wrapOrphans(row, 'table-cell', (k) => k === 'table-cell', anonymous);
|
|
722
2254
|
}
|
|
723
2255
|
}
|
|
724
|
-
table.children = [...captions, ...groups];
|
|
2256
|
+
table.children = [...captions, ...columns, ...groups];
|
|
725
2257
|
for (const child of table.children)
|
|
726
2258
|
child.parent = table;
|
|
727
2259
|
}
|