@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
package/src/html/css/cascade.ts
CHANGED
|
@@ -21,12 +21,36 @@ import { Element as DomElement, isTag } from 'domhandler';
|
|
|
21
21
|
import type { Element } from 'domhandler';
|
|
22
22
|
|
|
23
23
|
import { attr, tagOf } from '../dom.js';
|
|
24
|
-
import {
|
|
24
|
+
import { svgSizeHint } from '../svg.js';
|
|
25
|
+
import {
|
|
26
|
+
asciiLower,
|
|
27
|
+
escapeEnd,
|
|
28
|
+
mediaMatches,
|
|
29
|
+
readIdent,
|
|
30
|
+
startsIdent,
|
|
31
|
+
} from './parse.js';
|
|
25
32
|
import type { Declaration, StyleRule, Stylesheet } from './parse.js';
|
|
26
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
applyDeclaration,
|
|
35
|
+
blockify,
|
|
36
|
+
settleContentVisibility,
|
|
37
|
+
settleOverflow,
|
|
38
|
+
settleClamp,
|
|
39
|
+
copyStyle,
|
|
40
|
+
decorate,
|
|
41
|
+
inherit,
|
|
42
|
+
initialOne,
|
|
43
|
+
initialStyle,
|
|
44
|
+
isInherited,
|
|
45
|
+
} from './style.js';
|
|
27
46
|
import type { ComputedStyle, RootLook } from './style.js';
|
|
47
|
+
import type { FontFamilies } from '../fonts.js';
|
|
28
48
|
import { parseDeclarations } from './parse.js';
|
|
49
|
+
import { CounterStyles, counterStyleRule } from './counter-styles.js';
|
|
50
|
+
import type { CounterStyleRule } from './counter-styles.js';
|
|
29
51
|
import type { UnitContext } from './values.js';
|
|
52
|
+
import { customProperties, substituteIn } from './vars.js';
|
|
53
|
+
import type { CustomProps } from './vars.js';
|
|
30
54
|
|
|
31
55
|
/** Where a declaration came from. Higher wins before specificity is asked. */
|
|
32
56
|
const enum Origin {
|
|
@@ -40,6 +64,8 @@ const enum Origin {
|
|
|
40
64
|
|
|
41
65
|
interface Candidate {
|
|
42
66
|
origin: Origin;
|
|
67
|
+
/** The rule's cascade layer, null for none (`StyleRule.layer`). */
|
|
68
|
+
layer: readonly number[] | null;
|
|
43
69
|
specificity: number;
|
|
44
70
|
order: number;
|
|
45
71
|
declarations: Declaration[];
|
|
@@ -59,6 +85,13 @@ type CssSelectAdapter = typeof DomUtils & {
|
|
|
59
85
|
isVisited(el: Element): boolean;
|
|
60
86
|
};
|
|
61
87
|
|
|
88
|
+
/** A style handed to every element with the same sharing key, and the key
|
|
89
|
+
* that element's children share under (`Cascade.sharedStyleFor`). */
|
|
90
|
+
export interface SharedStyle {
|
|
91
|
+
style: ComputedStyle;
|
|
92
|
+
key: number;
|
|
93
|
+
}
|
|
94
|
+
|
|
62
95
|
/** A compiled matcher, kept beside the rule it came from. */
|
|
63
96
|
interface IndexedRule {
|
|
64
97
|
rule: StyleRule;
|
|
@@ -66,8 +99,26 @@ interface IndexedRule {
|
|
|
66
99
|
/** Set once compilation has been attempted, so a selector `css-select`
|
|
67
100
|
* refuses is not recompiled once per element for the rest of the pass. */
|
|
68
101
|
compiled: boolean;
|
|
102
|
+
/** Which rule this is, in a sharing key (`Cascade.sharedStyleFor`). */
|
|
103
|
+
id: number;
|
|
69
104
|
}
|
|
70
105
|
|
|
106
|
+
/**
|
|
107
|
+
* What makes a selector's match depend on more than the element's own tag and
|
|
108
|
+
* attributes and its ancestors' — its siblings, where it sits among them,
|
|
109
|
+
* what it contains, or where the document is scrolled to. Two elements that
|
|
110
|
+
* look alike from the root down can still differ under a rule with one of
|
|
111
|
+
* these, so an element such a rule could reach does not share its style
|
|
112
|
+
* (`Cascade.sharedStyleFor`). css-select spells some of them under other
|
|
113
|
+
* names, and those count too: `:checked` and `:selected` read which option
|
|
114
|
+
* comes first, `:disabled` and `:enabled` which legend does, and `:parent`
|
|
115
|
+
* and `:contains()` the element's contents. Over-broad on purpose: a `+` or
|
|
116
|
+
* `~` inside an attribute value, or the `~=` operator, costs sharing and
|
|
117
|
+
* nothing else.
|
|
118
|
+
*/
|
|
119
|
+
const UNSHAREABLE =
|
|
120
|
+
/[+~]|:(?:first|last|only)-(?:child|of-type)|:nth-|:empty|:blank|:has\(|:focus-within|:target|:scope|:(?:checked|selected|disabled|enabled|parent)\b|:i?contains\(/;
|
|
121
|
+
|
|
71
122
|
/**
|
|
72
123
|
* Rules bucketed by the key of their rightmost compound selector. An element
|
|
73
124
|
* only ever tries `id`, its classes, its tag and the universal bucket.
|
|
@@ -80,13 +131,59 @@ class RuleIndex {
|
|
|
80
131
|
/** Whether any rule in here is pointer-sensitive, so the renderer knows
|
|
81
132
|
* whether a pointer move can change the cascade at all. */
|
|
82
133
|
hoverSensitive = false;
|
|
134
|
+
/** The buckets holding a rule an element cannot share its style under
|
|
135
|
+
* (`UNSHAREABLE`): the ids, classes and tags whose elements compute
|
|
136
|
+
* their own, and whether the universal bucket makes every element do so. */
|
|
137
|
+
readonly ownStyleIds = new Set<string>();
|
|
138
|
+
readonly ownStyleClasses = new Set<string>();
|
|
139
|
+
readonly ownStyleTags = new Set<string>();
|
|
140
|
+
ownStyleEverywhere = false;
|
|
141
|
+
/** How many rules are in here, so an empty index costs one comparison. */
|
|
142
|
+
size = 0;
|
|
143
|
+
private _nextId = 0;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Whether a bucket here could hold a rule for `el`, by its tag, id and
|
|
147
|
+
* classes and without matching anything: an index of a rule or two is
|
|
148
|
+
* asked of every element — the user-agent sheet's `q::before` and
|
|
149
|
+
* `q::after` made every element of a document split its `class` twice —
|
|
150
|
+
* and almost none of them has a rule in it.
|
|
151
|
+
*/
|
|
152
|
+
reaches(el: Element): boolean {
|
|
153
|
+
if (this.universal.length || this.byTag.has(tagOf(el))) return true;
|
|
154
|
+
if (this.byId.size) {
|
|
155
|
+
const id = attr(el, 'id');
|
|
156
|
+
if (id && this.byId.has(id)) return true;
|
|
157
|
+
}
|
|
158
|
+
if (this.byClass.size) {
|
|
159
|
+
const className = attr(el, 'class');
|
|
160
|
+
if (className) {
|
|
161
|
+
for (const name of className.split(/\s+/)) {
|
|
162
|
+
if (name && this.byClass.has(name)) return true;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
83
168
|
|
|
84
169
|
add(rule: StyleRule): void {
|
|
85
|
-
|
|
170
|
+
this.size += 1;
|
|
171
|
+
const indexed: IndexedRule = {
|
|
172
|
+
rule,
|
|
173
|
+
match: null,
|
|
174
|
+
compiled: false,
|
|
175
|
+
id: this._nextId++,
|
|
176
|
+
};
|
|
86
177
|
if (rule.selector.includes(':hover') || rule.selector.includes(':active')) {
|
|
87
178
|
this.hoverSensitive = true;
|
|
88
179
|
}
|
|
89
180
|
const key = rightmostKey(rule.selector);
|
|
181
|
+
if (UNSHAREABLE.test(rule.selector)) {
|
|
182
|
+
if (key.kind === 'id') this.ownStyleIds.add(key.name);
|
|
183
|
+
else if (key.kind === 'class') this.ownStyleClasses.add(key.name);
|
|
184
|
+
else if (key.kind === 'tag') this.ownStyleTags.add(key.name);
|
|
185
|
+
else this.ownStyleEverywhere = true;
|
|
186
|
+
}
|
|
90
187
|
const bucket =
|
|
91
188
|
key.kind === 'id'
|
|
92
189
|
? mapBucket(this.byId, key.name)
|
|
@@ -99,6 +196,204 @@ class RuleIndex {
|
|
|
99
196
|
}
|
|
100
197
|
}
|
|
101
198
|
|
|
199
|
+
/** The pseudo-elements a rule can style here. */
|
|
200
|
+
type PseudoElement =
|
|
201
|
+
'before' | 'after' | 'first-letter' | 'first-line' | 'marker';
|
|
202
|
+
|
|
203
|
+
/** A selector's trailing `::before`, `::after`, `::first-letter`,
|
|
204
|
+
* `::first-line` or `::marker`, or CSS 2's single-colon spelling of any of
|
|
205
|
+
* its four. */
|
|
206
|
+
const PSEUDO_ELEMENT = /::?(before|after|first-letter|first-line)$|::marker$/i;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* A rule for a pseudo-element, as the pseudo-element it styles and a rule
|
|
210
|
+
* for the element it hangs off, which is what gets matched. The specificity
|
|
211
|
+
* is the whole selector's, the pseudo-element counted in. `p::before`
|
|
212
|
+
* matches `p`; `p ::before` and `p > ::before` have nothing left of their
|
|
213
|
+
* last compound and match any child, `p *` and `p > *`.
|
|
214
|
+
*/
|
|
215
|
+
function splitPseudoElement(
|
|
216
|
+
rule: StyleRule,
|
|
217
|
+
): { which: PseudoElement; rule: StyleRule } | null {
|
|
218
|
+
const m = PSEUDO_ELEMENT.exec(rule.selector);
|
|
219
|
+
if (!m) return null;
|
|
220
|
+
const head = rule.selector.slice(0, m.index);
|
|
221
|
+
const trimmed = head.trimEnd();
|
|
222
|
+
const selector = !trimmed
|
|
223
|
+
? '*'
|
|
224
|
+
: head !== trimmed || /[>+~]$/.test(trimmed)
|
|
225
|
+
? `${trimmed} *`
|
|
226
|
+
: trimmed;
|
|
227
|
+
return {
|
|
228
|
+
which: (m[1] ?? 'marker').toLowerCase() as PseudoElement,
|
|
229
|
+
rule: { ...rule, selector },
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* The compounds of a selector that test `:hover`, each without it (and
|
|
235
|
+
* without an `:active` beside it), and whether one is followed by a sibling
|
|
236
|
+
* combinator, which reaches the element's later siblings. `nested` where a
|
|
237
|
+
* `:hover` sits inside a functional pseudo-class — `:not(:hover)`,
|
|
238
|
+
* `:is(:hover)` — and reaches elements no compound names.
|
|
239
|
+
*
|
|
240
|
+
* A `:hover` in a compound's `:has()` is the one exception, since what it
|
|
241
|
+
* reaches is known: the compound, without its `:has()`, is an ancestor of
|
|
242
|
+
* the element the pointer is over — or an earlier sibling of it or of an
|
|
243
|
+
* ancestor, where the argument starts at a sibling (Selectors 4, 4.5). So
|
|
244
|
+
* it is given as an anchor (`has`) rather than making the rule `nested`.
|
|
245
|
+
*
|
|
246
|
+
* `:active` is not the pointer's here: nothing sets it (`setPointer` is
|
|
247
|
+
* handed none), so a selector that tests only it never changes as the
|
|
248
|
+
* pointer moves — Wikipedia's buttons' `:focus:not(:active)` among them.
|
|
249
|
+
* A press that sets it would have to be counted here too.
|
|
250
|
+
*/
|
|
251
|
+
export function pointerCompounds(selector: string): {
|
|
252
|
+
compounds: string[];
|
|
253
|
+
siblings: boolean;
|
|
254
|
+
nested: boolean;
|
|
255
|
+
has: { anchor: string; siblings: boolean }[];
|
|
256
|
+
} {
|
|
257
|
+
const out = {
|
|
258
|
+
compounds: [] as string[],
|
|
259
|
+
siblings: false,
|
|
260
|
+
nested: false,
|
|
261
|
+
has: [] as { anchor: string; siblings: boolean }[],
|
|
262
|
+
};
|
|
263
|
+
// the compounds at the top level, each with the combinator after it
|
|
264
|
+
const parts: { text: string; next: string }[] = [];
|
|
265
|
+
let start = 0;
|
|
266
|
+
let depth = 0;
|
|
267
|
+
let quote = '';
|
|
268
|
+
const split = (end: number, next: string): void => {
|
|
269
|
+
const text = selector.slice(start, end).trim();
|
|
270
|
+
if (text) parts.push({ text, next });
|
|
271
|
+
else if (parts.length && next !== ' ') parts[parts.length - 1].next = next;
|
|
272
|
+
};
|
|
273
|
+
for (let i = 0; i < selector.length; i += 1) {
|
|
274
|
+
const c = selector[i];
|
|
275
|
+
if (quote) {
|
|
276
|
+
if (c === quote && selector[i - 1] !== '\\') quote = '';
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
if (c === '\\') i = escapeEnd(selector, i) - 1;
|
|
280
|
+
else if (c === '"' || c === "'") quote = c;
|
|
281
|
+
else if (c === '(' || c === '[') depth += 1;
|
|
282
|
+
else if (c === ')' || c === ']') depth = Math.max(0, depth - 1);
|
|
283
|
+
else if (depth === 0 && /[\s>+~]/.test(c)) {
|
|
284
|
+
split(i, c === '+' || c === '~' || c === '>' ? c : ' ');
|
|
285
|
+
start = i + 1;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
split(selector.length, '');
|
|
289
|
+
for (const { text, next } of parts) {
|
|
290
|
+
let bare = '';
|
|
291
|
+
let pointer = false;
|
|
292
|
+
depth = 0;
|
|
293
|
+
quote = '';
|
|
294
|
+
/** Where a top-level `:has(` starts in `bare`, and its argument in
|
|
295
|
+
* `text`, while one is open. */
|
|
296
|
+
let hasAt = -1;
|
|
297
|
+
let argAt = -1;
|
|
298
|
+
let hasPointer = false;
|
|
299
|
+
let anchored = false;
|
|
300
|
+
const args: string[] = [];
|
|
301
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
302
|
+
const c = text[i];
|
|
303
|
+
if (quote) {
|
|
304
|
+
if (c === quote && text[i - 1] !== '\\') quote = '';
|
|
305
|
+
bare += c;
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
if (c === '\\') {
|
|
309
|
+
const end = escapeEnd(text, i);
|
|
310
|
+
bare += text.slice(i, end);
|
|
311
|
+
i = end - 1;
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (c === '"' || c === "'") quote = c;
|
|
315
|
+
else if (c === '(' || c === '[') {
|
|
316
|
+
if (depth === 0 && c === '(' && /:has$/i.test(bare)) {
|
|
317
|
+
hasAt = bare.length - 4;
|
|
318
|
+
argAt = i + 1;
|
|
319
|
+
hasPointer = false;
|
|
320
|
+
}
|
|
321
|
+
depth += 1;
|
|
322
|
+
} else if (c === ')' || c === ']') {
|
|
323
|
+
depth = Math.max(0, depth - 1);
|
|
324
|
+
if (depth === 0 && hasAt >= 0 && c === ')') {
|
|
325
|
+
// the compound is kept without it: its argument is what the
|
|
326
|
+
// pointer changes
|
|
327
|
+
if (hasPointer) {
|
|
328
|
+
anchored = true;
|
|
329
|
+
args.push(text.slice(argAt, i));
|
|
330
|
+
bare = bare.slice(0, hasAt);
|
|
331
|
+
} else bare += c;
|
|
332
|
+
hasAt = -1;
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
} else if (c === ':' && text[i + 1] !== ':') {
|
|
336
|
+
const m = POINTER_PSEUDO_AT.exec(text.slice(i));
|
|
337
|
+
if (m) {
|
|
338
|
+
const hover = m[0].length === 6;
|
|
339
|
+
if (depth > 0) {
|
|
340
|
+
// whatever holds it in the argument, the element it tests is
|
|
341
|
+
// in the anchor's subtree or a later sibling's
|
|
342
|
+
if (hover && hasAt >= 0) hasPointer = true;
|
|
343
|
+
else if (hover) out.nested = true;
|
|
344
|
+
} else {
|
|
345
|
+
if (hover) pointer = true;
|
|
346
|
+
i += m[0].length - 1;
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
bare += c;
|
|
352
|
+
}
|
|
353
|
+
if (anchored) {
|
|
354
|
+
out.has.push({
|
|
355
|
+
anchor: bare.trim() || '*',
|
|
356
|
+
siblings: args.some((arg) =>
|
|
357
|
+
arg.split(',').some((one) => /^\s*[+~]/.test(one)),
|
|
358
|
+
),
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
if (!pointer) continue;
|
|
362
|
+
out.compounds.push(bare.trim() || '*');
|
|
363
|
+
if (next === '+' || next === '~') out.siblings = true;
|
|
364
|
+
}
|
|
365
|
+
return out;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/** `:hover` or `:active` at the start of a string, and nothing longer. */
|
|
369
|
+
const POINTER_PSEUDO_AT = /^:(?:hover|active)(?![\w-])/i;
|
|
370
|
+
|
|
371
|
+
/** `:hover` or `:active` anywhere in a selector. */
|
|
372
|
+
const POINTER_PSEUDO = /:(?:hover|active)(?![\w-])/i;
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* A selector compiled to a matcher over this adapter. css-select keeps the
|
|
376
|
+
* answers of a `:has()` above the subject, or of the ancestors a
|
|
377
|
+
* descendant combinator tried under one, for as long as the matcher lives
|
|
378
|
+
* (`cacheResults`) — which is the cascade's life, and a pointer move is no
|
|
379
|
+
* new cascade. So a selector that tests the pointer keeps none: with them,
|
|
380
|
+
* `#box:has(a:hover) .m` answered what it did before the first move, for
|
|
381
|
+
* good.
|
|
382
|
+
*/
|
|
383
|
+
function compileSelector(
|
|
384
|
+
selector: string,
|
|
385
|
+
adapter: CssSelectAdapter,
|
|
386
|
+
): (el: Element) => boolean {
|
|
387
|
+
return compile(noEmptyWords(selector), {
|
|
388
|
+
adapter,
|
|
389
|
+
xmlMode: false,
|
|
390
|
+
pseudos: PSEUDOS,
|
|
391
|
+
cacheResults: !POINTER_PSEUDO.test(selector),
|
|
392
|
+
} as unknown as Parameters<typeof compile>[1]) as unknown as (
|
|
393
|
+
node: Element,
|
|
394
|
+
) => boolean;
|
|
395
|
+
}
|
|
396
|
+
|
|
102
397
|
function mapBucket(
|
|
103
398
|
map: Map<string, IndexedRule[]>,
|
|
104
399
|
key: string,
|
|
@@ -132,12 +427,23 @@ function rightmostKey(selector: string): {
|
|
|
132
427
|
if (c === quote && selector[i - 1] !== '\\') quote = '';
|
|
133
428
|
continue;
|
|
134
429
|
}
|
|
135
|
-
|
|
430
|
+
// an escaped character is part of a name, whatever it is — and a hex
|
|
431
|
+
// escape takes the space after it: `.c\6c ass` is the class `class`
|
|
432
|
+
if (c === '\\') i = escapeEnd(selector, i) - 1;
|
|
433
|
+
else if (c === '"' || c === "'") quote = c;
|
|
136
434
|
else if (c === '(' || c === '[') depth += 1;
|
|
137
435
|
else if (c === ')' || c === ']') depth = Math.max(0, depth - 1);
|
|
138
436
|
else if (
|
|
139
437
|
depth === 0 &&
|
|
140
|
-
(c === ' ' ||
|
|
438
|
+
(c === ' ' ||
|
|
439
|
+
c === '>' ||
|
|
440
|
+
c === '+' ||
|
|
441
|
+
c === '~' ||
|
|
442
|
+
// CSS's other white space: `div\fp` is a descendant too
|
|
443
|
+
c === '\t' ||
|
|
444
|
+
c === '\n' ||
|
|
445
|
+
c === '\r' ||
|
|
446
|
+
c === '\f')
|
|
141
447
|
) {
|
|
142
448
|
start = i + 1;
|
|
143
449
|
}
|
|
@@ -149,24 +455,23 @@ function rightmostKey(selector: string): {
|
|
|
149
455
|
let i = 0;
|
|
150
456
|
while (i < compound.length) {
|
|
151
457
|
const c = compound[i];
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
i = end;
|
|
458
|
+
// Names as the matcher reads them, escapes resolved: `.md\:flex` is
|
|
459
|
+
// the class `md:flex`, and filed under its first half it never matched.
|
|
460
|
+
if (c === '#' || c === '.') {
|
|
461
|
+
const name = readIdent(compound, i + 1);
|
|
462
|
+
if (c === '#' && !id) id = name.value;
|
|
463
|
+
if (c === '.' && !cls) cls = name.value;
|
|
464
|
+
i = Math.max(name.end, i + 1);
|
|
160
465
|
} else if (c === '[') {
|
|
161
466
|
i = balancedEnd(compound, i, '[', ']');
|
|
162
467
|
} else if (c === ':') {
|
|
163
468
|
const skip = compound[i + 1] === ':' ? 2 : 1;
|
|
164
|
-
const end =
|
|
469
|
+
const end = Math.max(readIdent(compound, i + skip).end, i + skip);
|
|
165
470
|
i = compound[end] === '(' ? balancedEnd(compound, end, '(', ')') : end;
|
|
166
|
-
} else if (
|
|
167
|
-
const
|
|
168
|
-
if (!tag) tag =
|
|
169
|
-
i = end;
|
|
471
|
+
} else if (startsIdent(compound, i)) {
|
|
472
|
+
const name = readIdent(compound, i);
|
|
473
|
+
if (!tag) tag = name.value.toLowerCase();
|
|
474
|
+
i = name.end;
|
|
170
475
|
} else {
|
|
171
476
|
i += 1;
|
|
172
477
|
}
|
|
@@ -177,12 +482,6 @@ function rightmostKey(selector: string): {
|
|
|
177
482
|
return { kind: 'any', name: '' };
|
|
178
483
|
}
|
|
179
484
|
|
|
180
|
-
function identEnd(text: string, from: number): number {
|
|
181
|
-
let i = from;
|
|
182
|
-
while (i < text.length && /[a-zA-Z0-9_\-\\]/.test(text[i])) i += 1;
|
|
183
|
-
return i;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
485
|
function balancedEnd(
|
|
187
486
|
text: string,
|
|
188
487
|
from: number,
|
|
@@ -200,6 +499,12 @@ function balancedEnd(
|
|
|
200
499
|
return text.length;
|
|
201
500
|
}
|
|
202
501
|
|
|
502
|
+
/** What `Cascade.firstLetterRules` found for an element. */
|
|
503
|
+
export interface FirstLetterRules {
|
|
504
|
+
readonly el: Element;
|
|
505
|
+
readonly candidates: readonly unknown[];
|
|
506
|
+
}
|
|
507
|
+
|
|
203
508
|
/** What the pointer is over, for `:hover`. A chain rather than one element,
|
|
204
509
|
* because `li:hover a` has to light up while the pointer is on the `li`. */
|
|
205
510
|
export interface PointerState {
|
|
@@ -215,7 +520,34 @@ const NO_POINTER: PointerState = { hovered: new Set(), active: new Set() };
|
|
|
215
520
|
*/
|
|
216
521
|
export class Cascade {
|
|
217
522
|
private _index = new RuleIndex();
|
|
523
|
+
/** The rules for pseudo-elements, kept apart: they never style the
|
|
524
|
+
* element itself, and a document with none of them asks nothing. */
|
|
525
|
+
private _pseudo: Record<PseudoElement, RuleIndex> = {
|
|
526
|
+
before: new RuleIndex(),
|
|
527
|
+
after: new RuleIndex(),
|
|
528
|
+
'first-letter': new RuleIndex(),
|
|
529
|
+
'first-line': new RuleIndex(),
|
|
530
|
+
marker: new RuleIndex(),
|
|
531
|
+
};
|
|
218
532
|
private _adapter: CssSelectAdapter;
|
|
533
|
+
/** The compounds of the selectors that test the pointer, each without
|
|
534
|
+
* its `:hover` or `:active` (`pointerCompounds`), and their matchers,
|
|
535
|
+
* compiled when a pointer move first asks (`hoverTouches`). */
|
|
536
|
+
private _hoverCompounds = new Set<string>();
|
|
537
|
+
private _hoverMatchers: ((el: Element) => boolean)[] | null = null;
|
|
538
|
+
/** Whether a pointer move can be restyled where it happened
|
|
539
|
+
* (`HtmlViewNode._hoverInPlace`): false where a selector tests the
|
|
540
|
+
* pointer inside a functional pseudo-class, which reaches elements no
|
|
541
|
+
* compound names. */
|
|
542
|
+
hoverLocal = true;
|
|
543
|
+
/** Whether a compound that tests the pointer is followed by a sibling
|
|
544
|
+
* combinator, so an element's later siblings restyle with it. */
|
|
545
|
+
hoverSiblings = false;
|
|
546
|
+
/** The compounds whose `:has()` tests the pointer, each without it, and
|
|
547
|
+
* whether its argument starts at a sibling (`hoverAnchors`). */
|
|
548
|
+
private _hoverAnchors = new Map<string, boolean>();
|
|
549
|
+
private _anchorMatchers:
|
|
550
|
+
{ match: (el: Element) => boolean; siblings: boolean }[] | null = null;
|
|
219
551
|
private _pointer: PointerState = NO_POINTER;
|
|
220
552
|
readonly initial: ComputedStyle;
|
|
221
553
|
readonly look: RootLook;
|
|
@@ -229,6 +561,37 @@ export class Cascade {
|
|
|
229
561
|
/** Every width at which some `@media` rule changes its mind, in CSS
|
|
230
562
|
* pixels — the unit the author wrote them in. */
|
|
231
563
|
readonly breakpoints: number[];
|
|
564
|
+
/** The counter styles the sheets define, over the predefined ones. */
|
|
565
|
+
readonly counterStyles: CounterStyles;
|
|
566
|
+
|
|
567
|
+
/** A font's x-height at a size, for `ex`, where the fonts can say. */
|
|
568
|
+
private _xHeightOf: ((family: string, size: number) => number | null) | null;
|
|
569
|
+
private _xHeights = new Map<string, number>();
|
|
570
|
+
/** The advance of a font's "0" at a size, for `ch`, likewise. */
|
|
571
|
+
private _zeroWidthOf:
|
|
572
|
+
((family: string, size: number) => number | null) | null;
|
|
573
|
+
private _zeroWidths = new Map<string, number>();
|
|
574
|
+
/** A font's own line height at a size, for `lh` where `line-height` is
|
|
575
|
+
* `normal`, likewise. */
|
|
576
|
+
private _normalLineOf:
|
|
577
|
+
((family: string, size: number) => number | null) | null;
|
|
578
|
+
private _normalLines = new Map<string, number>();
|
|
579
|
+
/** Whether any declaration sets a custom property or reads one: without
|
|
580
|
+
* one, no element's style asks about them. */
|
|
581
|
+
private _vars = false;
|
|
582
|
+
/** Whether any declaration is in a unit of the viewport's width — `vw`,
|
|
583
|
+
* `vmin`, `vmax` — or of its height, `vh` and the same two: a style
|
|
584
|
+
* holds such a length as a number, so it is computed again when that
|
|
585
|
+
* side of the viewport moves (`HtmlViewNode._update`). */
|
|
586
|
+
readsViewportWidth = false;
|
|
587
|
+
readsViewportHeight = false;
|
|
588
|
+
/** The families the document loads itself (`fonts.ts`), or null for a
|
|
589
|
+
* document with no `@font-face`. */
|
|
590
|
+
private _families: FontFamilies | null;
|
|
591
|
+
private _mapFamilies: ((list: string) => string) | undefined;
|
|
592
|
+
/** Whether any declaration has a length in `lh` or `rlh`: only then is
|
|
593
|
+
* the line height settled ahead of the declarations that read it. */
|
|
594
|
+
private _lh = false;
|
|
232
595
|
|
|
233
596
|
constructor(
|
|
234
597
|
sheets: Stylesheet[],
|
|
@@ -236,18 +599,44 @@ export class Cascade {
|
|
|
236
599
|
viewportWidth: number,
|
|
237
600
|
viewportHeight: number,
|
|
238
601
|
scale = 1,
|
|
602
|
+
xHeight: ((family: string, size: number) => number | null) | null = null,
|
|
603
|
+
zeroWidth: ((family: string, size: number) => number | null) | null = null,
|
|
604
|
+
normalLine: ((family: string, size: number) => number | null) | null = null,
|
|
605
|
+
families: FontFamilies | null = null,
|
|
239
606
|
) {
|
|
607
|
+
this._xHeightOf = xHeight;
|
|
608
|
+
this._zeroWidthOf = zeroWidth;
|
|
609
|
+
this._normalLineOf = normalLine;
|
|
610
|
+
this._families = families;
|
|
611
|
+
this._mapFamilies = families
|
|
612
|
+
? (list: string) => families.map(list)
|
|
613
|
+
: undefined;
|
|
240
614
|
this.look = look;
|
|
241
|
-
this.initial = initialStyle(look);
|
|
615
|
+
this.initial = initialStyle(look, scale);
|
|
242
616
|
this.viewportWidth = viewportWidth;
|
|
243
617
|
this.viewportHeight = viewportHeight;
|
|
244
618
|
this.scale = scale;
|
|
245
619
|
const breakpoints = new Set<number>();
|
|
620
|
+
const counterStyles = new Map<string, CounterStyleRule>();
|
|
246
621
|
for (const sheet of sheets) {
|
|
247
|
-
|
|
622
|
+
// a counter style's rule whole over any before it of its name
|
|
623
|
+
for (const { prelude, declarations } of sheet.counterStyles ?? []) {
|
|
624
|
+
const style = counterStyleRule(prelude, declarations);
|
|
625
|
+
if (style) counterStyles.set(style.name, style.rule);
|
|
626
|
+
}
|
|
627
|
+
for (const rule of sheet.rules) {
|
|
628
|
+
if (!this._vars && usesVars(rule.declarations)) this._vars = true;
|
|
629
|
+
this._noteViewportUnits(rule.declarations);
|
|
630
|
+
if (!this._lh && usesLh(rule.declarations)) this._lh = true;
|
|
631
|
+
const pseudo = splitPseudoElement(rule);
|
|
632
|
+
if (pseudo) this._pseudo[pseudo.which].add(pseudo.rule);
|
|
633
|
+
else this._index.add(rule);
|
|
634
|
+
this._noteHover((pseudo?.rule ?? rule).selector);
|
|
635
|
+
}
|
|
248
636
|
for (const bp of sheet.breakpoints) breakpoints.add(bp);
|
|
249
637
|
}
|
|
250
638
|
this.breakpoints = [...breakpoints].sort((a, b) => a - b);
|
|
639
|
+
this.counterStyles = new CounterStyles(counterStyles);
|
|
251
640
|
// css-select's default adapter is domutils; `isHovered` and `isActive`
|
|
252
641
|
// are its documented hooks for exactly this, so `:hover` costs an
|
|
253
642
|
// adapter field rather than a fork of the matcher.
|
|
@@ -258,21 +647,127 @@ export class Cascade {
|
|
|
258
647
|
this._adapter = {
|
|
259
648
|
...DomUtils,
|
|
260
649
|
isTag,
|
|
650
|
+
// a type selector sees `tagOf`'s name — XHTML's `<svg:svg>` is an
|
|
651
|
+
// `svg` — asked only of a name that could be one: this runs for every
|
|
652
|
+
// type selector tried, and the parser has lowercased the rest
|
|
653
|
+
getName: (el: Element) =>
|
|
654
|
+
el.name.endsWith(':svg') ? tagOf(el) : el.name,
|
|
261
655
|
isHovered: (el: Element) => this._pointer.hovered.has(el),
|
|
262
656
|
isActive: (el: Element) => this._pointer.active.has(el),
|
|
263
657
|
isVisited: () => false,
|
|
264
658
|
};
|
|
265
659
|
}
|
|
266
660
|
|
|
661
|
+
/** Whether declarations read the viewport's width or height (`vw`,
|
|
662
|
+
* `vh`, `vmin`, `vmax`), noted on the cascade. */
|
|
663
|
+
private _noteViewportUnits(declarations: readonly Declaration[]): void {
|
|
664
|
+
if (this.readsViewportWidth && this.readsViewportHeight) return;
|
|
665
|
+
for (const d of declarations) {
|
|
666
|
+
if (!VIEWPORT_UNIT.test(d.value)) continue;
|
|
667
|
+
for (const m of d.value.matchAll(VIEWPORT_UNITS)) {
|
|
668
|
+
const unit = m[1].toLowerCase();
|
|
669
|
+
if (unit !== 'vh') this.readsViewportWidth = true;
|
|
670
|
+
if (unit !== 'vw') this.readsViewportHeight = true;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
267
675
|
/** Whether a pointer move can change what this cascade produces. */
|
|
268
676
|
get hoverSensitive(): boolean {
|
|
269
|
-
return
|
|
677
|
+
return (
|
|
678
|
+
this._index.hoverSensitive ||
|
|
679
|
+
this._pseudo.before.hoverSensitive ||
|
|
680
|
+
this._pseudo.after.hoverSensitive ||
|
|
681
|
+
this._pseudo['first-letter'].hoverSensitive ||
|
|
682
|
+
this._pseudo['first-line'].hoverSensitive ||
|
|
683
|
+
this._pseudo.marker.hoverSensitive
|
|
684
|
+
);
|
|
270
685
|
}
|
|
271
686
|
|
|
272
687
|
setPointer(pointer: PointerState): void {
|
|
273
688
|
this._pointer = pointer;
|
|
274
689
|
}
|
|
275
690
|
|
|
691
|
+
/**
|
|
692
|
+
* Whether the pointer entering or leaving `el` can change a style: one of
|
|
693
|
+
* the compounds a selector tests the pointer in matches it, the pointer
|
|
694
|
+
* aside. A move between two paragraphs of a page whose only such rule is
|
|
695
|
+
* `a:hover` touches nothing, and restyles nothing.
|
|
696
|
+
*/
|
|
697
|
+
hoverTouches(el: Element): boolean {
|
|
698
|
+
this._hoverMatchers ??= [...this._hoverCompounds].flatMap((c) => {
|
|
699
|
+
const match = this._compile(c);
|
|
700
|
+
return match ? [match] : [];
|
|
701
|
+
});
|
|
702
|
+
for (const match of this._hoverMatchers) if (match(el)) return true;
|
|
703
|
+
return false;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* The elements whose `:has()` may have changed its answer as `el` was
|
|
708
|
+
* hovered or left: the ancestors of it a compound whose `:has()` tests
|
|
709
|
+
* the pointer names, and their earlier siblings and its own where the
|
|
710
|
+
* argument starts at a sibling — a superset, which is restyled and found
|
|
711
|
+
* the same where it did not. Into `into`.
|
|
712
|
+
*/
|
|
713
|
+
hoverAnchors(el: Element, into: Element[]): void {
|
|
714
|
+
if (!this._hoverAnchors.size) return;
|
|
715
|
+
this._anchorMatchers ??= [...this._hoverAnchors].flatMap(
|
|
716
|
+
([anchor, siblings]) => {
|
|
717
|
+
const match = this._compile(anchor);
|
|
718
|
+
return match ? [{ match, siblings }] : [];
|
|
719
|
+
},
|
|
720
|
+
);
|
|
721
|
+
const siblings = this._anchorMatchers.some((m) => m.siblings);
|
|
722
|
+
for (let at: Element | null = el; at;) {
|
|
723
|
+
if (at !== el) {
|
|
724
|
+
for (const { match } of this._anchorMatchers) {
|
|
725
|
+
if (match(at)) {
|
|
726
|
+
into.push(at);
|
|
727
|
+
break;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
if (siblings) {
|
|
732
|
+
for (let s = at.prev; s; s = s.prev) {
|
|
733
|
+
if (!isTag(s as Element)) continue;
|
|
734
|
+
for (const { match, siblings: apart } of this._anchorMatchers) {
|
|
735
|
+
if (apart && match(s as Element)) {
|
|
736
|
+
into.push(s as Element);
|
|
737
|
+
break;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
const parent: Element['parent'] = at.parent;
|
|
743
|
+
at = parent && isTag(parent as Element) ? (parent as Element) : null;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
private _noteHover(selector: string): void {
|
|
748
|
+
if (!/:(?:hover|active)/i.test(selector)) return;
|
|
749
|
+
const found = pointerCompounds(selector);
|
|
750
|
+
if (found.nested) this.hoverLocal = false;
|
|
751
|
+
if (found.siblings) this.hoverSiblings = true;
|
|
752
|
+
for (const c of found.compounds) this._hoverCompounds.add(c);
|
|
753
|
+
for (const { anchor, siblings } of found.has) {
|
|
754
|
+
this._hoverAnchors.set(
|
|
755
|
+
anchor,
|
|
756
|
+
siblings || (this._hoverAnchors.get(anchor) ?? false),
|
|
757
|
+
);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/** A selector compiled as a rule's is, or null where css-select refuses
|
|
762
|
+
* it, as a rule it refuses drops out of the cascade. */
|
|
763
|
+
private _compile(selector: string): ((el: Element) => boolean) | null {
|
|
764
|
+
try {
|
|
765
|
+
return compileSelector(selector, this._adapter);
|
|
766
|
+
} catch {
|
|
767
|
+
return null;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
|
|
276
771
|
/** Which media band a device-pixel width falls in. Two widths in the same
|
|
277
772
|
* band produce identical styles, which is what lets a resize skip
|
|
278
773
|
* restyling. */
|
|
@@ -286,18 +781,361 @@ export class Cascade {
|
|
|
286
781
|
return band;
|
|
287
782
|
}
|
|
288
783
|
|
|
784
|
+
/** Computed styles by sharing key, for one build (`sharedStyleFor`): one
|
|
785
|
+
* map for the elements shared without matching, one for the elements
|
|
786
|
+
* shared by what they matched. */
|
|
787
|
+
private _shared = new Map<string, SharedStyle>();
|
|
788
|
+
private _sharedByMatch = new Map<string, SharedStyle>();
|
|
789
|
+
private _nextShareKey = 1;
|
|
790
|
+
/** Custom property sets by what made them, for one build (`_customFor`). */
|
|
791
|
+
private _customs = new Map<string, CustomProps>();
|
|
792
|
+
private _ids = new WeakMap<object, number>();
|
|
793
|
+
private _nextId = 1;
|
|
794
|
+
|
|
795
|
+
/** A box tree is about to be built: the styles shared in the last build
|
|
796
|
+
* were computed against a pointer and a viewport that may have moved. */
|
|
797
|
+
beginSharing(): void {
|
|
798
|
+
this._shared.clear();
|
|
799
|
+
this._sharedByMatch.clear();
|
|
800
|
+
this._customs.clear();
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
* An element's custom properties: its parent's, or its own over them —
|
|
805
|
+
* shared by every element whose parent has the same ones and whose own
|
|
806
|
+
* come from the same declarations. Tailwind 4 sets thirty-five of them on
|
|
807
|
+
* every element (`*, ::before, ::after { --tw-shadow: 0 0 #0000; … }`),
|
|
808
|
+
* and a set per element was a fifth of building the boxes; it also kept
|
|
809
|
+
* each element's `var()`s from being replaced once for its siblings,
|
|
810
|
+
* which `substituteIn` remembers by the set they were read against.
|
|
811
|
+
*/
|
|
812
|
+
private _customFor(
|
|
813
|
+
parentStyle: ComputedStyle,
|
|
814
|
+
candidates: Candidate[],
|
|
815
|
+
): CustomProps | null {
|
|
816
|
+
let key = '';
|
|
817
|
+
for (const c of candidates) {
|
|
818
|
+
if (!this._hasCustom(c)) continue;
|
|
819
|
+
key += `${this._idOf(c.declarations)}:${c.only},`;
|
|
820
|
+
}
|
|
821
|
+
const parent = parentStyle.custom;
|
|
822
|
+
if (!key) return parent;
|
|
823
|
+
key = `${parent ? this._idOf(parent) : 0}|${key}`;
|
|
824
|
+
const hit = this._customs.get(key);
|
|
825
|
+
if (hit) return hit;
|
|
826
|
+
const own = new Map<string, string>();
|
|
827
|
+
for (const c of candidates) {
|
|
828
|
+
for (const d of pick(c)) if (d.custom) own.set(d.prop, d.value);
|
|
829
|
+
}
|
|
830
|
+
const made = customProperties(own, parent);
|
|
831
|
+
this._customs.set(key, made);
|
|
832
|
+
return made;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/** Whether a candidate sets a custom property. */
|
|
836
|
+
private _hasCustom(c: Candidate): boolean {
|
|
837
|
+
if (c.only >= 0) return !!c.declarations[c.only].custom;
|
|
838
|
+
let has = CUSTOM_IN.get(c.declarations);
|
|
839
|
+
if (has === undefined) {
|
|
840
|
+
has = c.declarations.some((d) => !!d.custom);
|
|
841
|
+
CUSTOM_IN.set(c.declarations, has);
|
|
842
|
+
}
|
|
843
|
+
return has;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
/** A number for an object, to key a string by. */
|
|
847
|
+
private _idOf(of: object): number {
|
|
848
|
+
let id = this._ids.get(of);
|
|
849
|
+
if (id === undefined) {
|
|
850
|
+
id = this._nextId++;
|
|
851
|
+
this._ids.set(of, id);
|
|
852
|
+
}
|
|
853
|
+
return id;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
/**
|
|
857
|
+
* `styleFor`, shared between the elements that must compute the same
|
|
858
|
+
* style, with the key this element's children share under.
|
|
859
|
+
*
|
|
860
|
+
* A style is a function of the parent's style, the rules that match, the
|
|
861
|
+
* presentational attributes, the inline style and whether the parent is a
|
|
862
|
+
* flex container. Outside the rules `UNSHAREABLE` names, what matches
|
|
863
|
+
* depends only on the element's tag and attributes and its ancestors', and
|
|
864
|
+
* the hints read nothing else either. So the key is the parent's key, the
|
|
865
|
+
* flex flag, the tag and every attribute — plus the pointer state where a
|
|
866
|
+
* rule asks for it — and an element whose key has been computed in this
|
|
867
|
+
* build takes that style object, matching nothing. A long document is a
|
|
868
|
+
* few kinds of element many times over: the benchmark's 600 KB report is
|
|
869
|
+
* 9,039 elements and 110 keys, and computing every style again was a third
|
|
870
|
+
* of an edit.
|
|
871
|
+
*
|
|
872
|
+
* An element a rule `UNSHAREABLE` could reach is matched, since where it
|
|
873
|
+
* sits decides what matches — and then shared by what matched: the same
|
|
874
|
+
* parent, attributes and rules make the same style. That is what keeps a
|
|
875
|
+
* striped table from being a style per row, and it keeps the key its
|
|
876
|
+
* children share under, which an element-per-key would have taken from the
|
|
877
|
+
* whole subtree.
|
|
878
|
+
*
|
|
879
|
+
* The style is handed out shared, so nothing may write to it — nothing
|
|
880
|
+
* does after the cascade, and `rootStyle` does not go through here.
|
|
881
|
+
*/
|
|
882
|
+
sharedStyleFor(
|
|
883
|
+
el: Element,
|
|
884
|
+
parentStyle: ComputedStyle,
|
|
885
|
+
parentKey: number,
|
|
886
|
+
inFlexContainer: boolean,
|
|
887
|
+
): SharedStyle {
|
|
888
|
+
let key = `${parentKey}\u0001${inFlexContainer ? 1 : 0}`;
|
|
889
|
+
const tag = tagOf(el);
|
|
890
|
+
key += `\u0001${tag.length}:${tag}`;
|
|
891
|
+
if (this._index.hoverSensitive) {
|
|
892
|
+
if (this._pointer.hovered.has(el)) key += '\u0001:hover';
|
|
893
|
+
if (this._pointer.active.has(el)) key += '\u0001:active';
|
|
894
|
+
}
|
|
895
|
+
const attribs = el.attribs;
|
|
896
|
+
for (const name in attribs) {
|
|
897
|
+
const value = attribs[name];
|
|
898
|
+
key += `\u0001${name.length}:${name}=${value.length}:${value}`;
|
|
899
|
+
}
|
|
900
|
+
if (!this._shareable(el)) {
|
|
901
|
+
const matched: number[] = [];
|
|
902
|
+
const candidates = this._candidates(el, matched);
|
|
903
|
+
key += `\u0001${matched.join(',')}`;
|
|
904
|
+
let shared = this._sharedByMatch.get(key);
|
|
905
|
+
if (shared === undefined) {
|
|
906
|
+
shared = {
|
|
907
|
+
style: this._computeStyle(
|
|
908
|
+
el,
|
|
909
|
+
parentStyle,
|
|
910
|
+
inFlexContainer,
|
|
911
|
+
candidates,
|
|
912
|
+
),
|
|
913
|
+
key: this._nextShareKey++,
|
|
914
|
+
};
|
|
915
|
+
this._sharedByMatch.set(key, shared);
|
|
916
|
+
}
|
|
917
|
+
return shared;
|
|
918
|
+
}
|
|
919
|
+
let shared = this._shared.get(key);
|
|
920
|
+
if (shared === undefined) {
|
|
921
|
+
shared = {
|
|
922
|
+
style: this.styleFor(el, parentStyle, inFlexContainer),
|
|
923
|
+
key: this._nextShareKey++,
|
|
924
|
+
};
|
|
925
|
+
this._shared.set(key, shared);
|
|
926
|
+
}
|
|
927
|
+
return shared;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
/** Whether no rule `UNSHAREABLE` names could reach this element: the same
|
|
931
|
+
* buckets `_candidates` looks in. */
|
|
932
|
+
private _shareable(el: Element): boolean {
|
|
933
|
+
const index = this._index;
|
|
934
|
+
if (index.ownStyleEverywhere) return false;
|
|
935
|
+
if (index.ownStyleTags.size && index.ownStyleTags.has(tagOf(el))) {
|
|
936
|
+
return false;
|
|
937
|
+
}
|
|
938
|
+
if (index.ownStyleIds.size) {
|
|
939
|
+
const id = attr(el, 'id');
|
|
940
|
+
if (id && index.ownStyleIds.has(id)) return false;
|
|
941
|
+
}
|
|
942
|
+
if (index.ownStyleClasses.size) {
|
|
943
|
+
const className = attr(el, 'class');
|
|
944
|
+
if (className) {
|
|
945
|
+
for (const name of className.split(/\s+/)) {
|
|
946
|
+
if (name && index.ownStyleClasses.has(name)) return false;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
return true;
|
|
951
|
+
}
|
|
952
|
+
|
|
289
953
|
/**
|
|
290
|
-
* The computed style for one element, given its parent's.
|
|
291
|
-
*
|
|
292
|
-
*
|
|
954
|
+
* The computed style for one element, given its parent's. The box builder
|
|
955
|
+
* asks for every element in document order, through `sharedStyleFor`, so
|
|
956
|
+
* a style is computed only for the elements with none to share.
|
|
293
957
|
*/
|
|
294
958
|
styleFor(
|
|
295
959
|
el: Element,
|
|
296
960
|
parentStyle: ComputedStyle,
|
|
297
961
|
inFlexContainer: boolean,
|
|
962
|
+
): ComputedStyle {
|
|
963
|
+
return this._computeStyle(
|
|
964
|
+
el,
|
|
965
|
+
parentStyle,
|
|
966
|
+
inFlexContainer,
|
|
967
|
+
this._candidates(el),
|
|
968
|
+
);
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
/**
|
|
972
|
+
* The style of an element's `::before` or `::after`, or null when it has
|
|
973
|
+
* none: no rule reaches it, or the rules that do leave `content` at
|
|
974
|
+
* `normal` or `none`, which make no box (CSS 2.1 12.2). It inherits from
|
|
975
|
+
* the element's own style, as a pseudo-element does, and a flex
|
|
976
|
+
* container's are flex items. Not shared: few elements have one, and where
|
|
977
|
+
* a rule gives every element one — a clearfix — the style is the cost of
|
|
978
|
+
* the box it makes.
|
|
979
|
+
*/
|
|
980
|
+
pseudoStyleFor(
|
|
981
|
+
el: Element,
|
|
982
|
+
which: 'before' | 'after',
|
|
983
|
+
elementStyle: ComputedStyle,
|
|
984
|
+
): ComputedStyle | null {
|
|
985
|
+
const index = this._pseudo[which];
|
|
986
|
+
if (!index.size || !index.reaches(el)) return null;
|
|
987
|
+
const candidates: Candidate[] = [];
|
|
988
|
+
this._matchInto(index, el, candidates);
|
|
989
|
+
if (!candidates.length) return null;
|
|
990
|
+
// One that no rule gives a `content` is none, whatever else reaches it —
|
|
991
|
+
// `content` is not inherited, and its initial `normal` is nothing here.
|
|
992
|
+
// Tailwind's `*, ::before, ::after` reaches both of every element's.
|
|
993
|
+
if (!candidates.some(setsContent)) return null;
|
|
994
|
+
candidates.sort(byCascade);
|
|
995
|
+
const style = this._computeStyle(
|
|
996
|
+
el,
|
|
997
|
+
elementStyle,
|
|
998
|
+
elementStyle.display === 'flex' || elementStyle.display === 'inline-flex',
|
|
999
|
+
candidates,
|
|
1000
|
+
);
|
|
1001
|
+
return style.content === 'normal' || style.content === 'none'
|
|
1002
|
+
? null
|
|
1003
|
+
: style;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
/**
|
|
1007
|
+
* The rules for an element's `::first-letter`, or null when none reaches
|
|
1008
|
+
* it. They are matched against the element here, and its style computed
|
|
1009
|
+
* later by `firstLetterStyle`, because the pseudo-element inherits from
|
|
1010
|
+
* the box its letter turns out to be in — a `<span>` the block opens with,
|
|
1011
|
+
* a `::before` — which the builder has not reached yet.
|
|
1012
|
+
*/
|
|
1013
|
+
firstLetterRules(el: Element): FirstLetterRules | null {
|
|
1014
|
+
const index = this._pseudo['first-letter'];
|
|
1015
|
+
if (!index.size || !index.reaches(el)) return null;
|
|
1016
|
+
const candidates: Candidate[] = [];
|
|
1017
|
+
this._matchInto(index, el, candidates);
|
|
1018
|
+
if (!candidates.length) return null;
|
|
1019
|
+
candidates.sort(byCascade);
|
|
1020
|
+
return { el, candidates };
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
/** Whether any rule styles a `::first-line`: a document with none asks
|
|
1024
|
+
* nothing more of its blocks. */
|
|
1025
|
+
get hasFirstLine(): boolean {
|
|
1026
|
+
return this._pseudo['first-line'].size > 0;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/**
|
|
1030
|
+
* A list item's `::marker` style, inheriting from its own, or null when no
|
|
1031
|
+
* rule reaches it (CSS Lists 3, 3.1): the colour and the font a bullet or
|
|
1032
|
+
* a number is set in, and a `content` it is set as instead.
|
|
1033
|
+
*/
|
|
1034
|
+
markerStyle(el: Element, style: ComputedStyle): ComputedStyle | null {
|
|
1035
|
+
const index = this._pseudo.marker;
|
|
1036
|
+
if (!index.size || !index.reaches(el)) return null;
|
|
1037
|
+
const candidates: Candidate[] = [];
|
|
1038
|
+
this._matchInto(index, el, candidates);
|
|
1039
|
+
if (!candidates.length) return null;
|
|
1040
|
+
candidates.sort(byCascade);
|
|
1041
|
+
const out = this._computeStyle(el, style, false, candidates);
|
|
1042
|
+
// a marker's direction is its own unless a rule says otherwise (the
|
|
1043
|
+
// HTML style sheet's `::marker { unicode-bidi: isolate }`)
|
|
1044
|
+
const set = candidates.some((c) =>
|
|
1045
|
+
c.declarations.some((d) => d.prop === 'unicode-bidi'),
|
|
1046
|
+
);
|
|
1047
|
+
if (!set) out.unicodeBidi = 'isolate';
|
|
1048
|
+
return out;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
/**
|
|
1052
|
+
* An element's `::first-line` style, inheriting from its own, or null
|
|
1053
|
+
* when no rule reaches it (CSS 2.1 5.12.1).
|
|
1054
|
+
*/
|
|
1055
|
+
firstLineStyle(el: Element, style: ComputedStyle): ComputedStyle | null {
|
|
1056
|
+
const index = this._pseudo['first-line'];
|
|
1057
|
+
if (!index.size) return null;
|
|
1058
|
+
const candidates: Candidate[] = [];
|
|
1059
|
+
this._matchInto(index, el, candidates);
|
|
1060
|
+
if (!candidates.length) return null;
|
|
1061
|
+
candidates.sort(byCascade);
|
|
1062
|
+
return this._computeStyle(el, style, false, candidates);
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
/**
|
|
1066
|
+
* The style of a `::first-letter` inside a box of `parentStyle`. It is an
|
|
1067
|
+
* inline box, or a float where it floats, whatever the rules say of its
|
|
1068
|
+
* `display` or its `position` (CSS 2.1 5.12.2).
|
|
1069
|
+
*/
|
|
1070
|
+
firstLetterStyle(
|
|
1071
|
+
rules: FirstLetterRules,
|
|
1072
|
+
parentStyle: ComputedStyle,
|
|
1073
|
+
): ComputedStyle {
|
|
1074
|
+
const style = this._computeStyle(
|
|
1075
|
+
rules.el,
|
|
1076
|
+
parentStyle,
|
|
1077
|
+
false,
|
|
1078
|
+
rules.candidates as Candidate[],
|
|
1079
|
+
);
|
|
1080
|
+
style.display = style.float === 'none' ? 'inline' : 'block';
|
|
1081
|
+
style.position = 'static';
|
|
1082
|
+
return style;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
/** The x-height of a style's font: the font's own, asked once per face
|
|
1086
|
+
* and size, or half an em. */
|
|
1087
|
+
private _exOf(style: ComputedStyle): number {
|
|
1088
|
+
const key = `${style.fontFamily}\u0001${style.fontSize}`;
|
|
1089
|
+
let ex = this._xHeights.get(key);
|
|
1090
|
+
if (ex === undefined) {
|
|
1091
|
+
ex = this._xHeightOf?.(style.fontFamily, style.fontSize) ?? NaN;
|
|
1092
|
+
if (!(ex > 0)) ex = style.fontSize * 0.5;
|
|
1093
|
+
this._xHeights.set(key, ex);
|
|
1094
|
+
}
|
|
1095
|
+
return ex;
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
/** The advance of a style's font's "0": the font's own, asked once per
|
|
1099
|
+
* face and size, or half an em. */
|
|
1100
|
+
private _chOf(style: ComputedStyle): number {
|
|
1101
|
+
const key = `${style.fontFamily}\u0001${style.fontSize}`;
|
|
1102
|
+
let ch = this._zeroWidths.get(key);
|
|
1103
|
+
if (ch === undefined) {
|
|
1104
|
+
ch = this._zeroWidthOf?.(style.fontFamily, style.fontSize) ?? NaN;
|
|
1105
|
+
if (!(ch > 0)) ch = style.fontSize * 0.5;
|
|
1106
|
+
this._zeroWidths.set(key, ch);
|
|
1107
|
+
}
|
|
1108
|
+
return ch;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
/** A style's computed line height as a length, for `lh`: `normal` as
|
|
1112
|
+
* its font's own, asked once per face and size, or 1.2em. */
|
|
1113
|
+
private _lineHeightOf(style: ComputedStyle): number {
|
|
1114
|
+
const set = style.lineHeight;
|
|
1115
|
+
if (set !== 'normal') {
|
|
1116
|
+
return style.lineHeightIsLength ? set : set * style.fontSize;
|
|
1117
|
+
}
|
|
1118
|
+
const key = `${style.fontFamily}\u0001${style.fontSize}`;
|
|
1119
|
+
let line = this._normalLines.get(key);
|
|
1120
|
+
if (line === undefined) {
|
|
1121
|
+
line = this._normalLineOf?.(style.fontFamily, style.fontSize) ?? NaN;
|
|
1122
|
+
if (!(line > 0)) line = style.fontSize * 1.2;
|
|
1123
|
+
this._normalLines.set(key, line);
|
|
1124
|
+
}
|
|
1125
|
+
return line;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
/** `styleFor`, from the rules and hints already gathered for `el`. */
|
|
1129
|
+
private _computeStyle(
|
|
1130
|
+
el: Element,
|
|
1131
|
+
parentStyle: ComputedStyle,
|
|
1132
|
+
inFlexContainer: boolean,
|
|
1133
|
+
candidates: Candidate[],
|
|
298
1134
|
): ComputedStyle {
|
|
299
1135
|
const style = inherit(parentStyle, this.initial);
|
|
300
|
-
|
|
1136
|
+
// custom properties first, in cascade order, so every `var()` in the
|
|
1137
|
+
// declarations below finds the one that wins
|
|
1138
|
+
if (this._vars) style.custom = this._customFor(parentStyle, candidates);
|
|
301
1139
|
|
|
302
1140
|
// The unit context has to be built twice: once with the parent's font
|
|
303
1141
|
// size, so a `font-size: 1.2em` in the cascade resolves against the
|
|
@@ -306,29 +1144,128 @@ export class Cascade {
|
|
|
306
1144
|
const ctxParent: UnitContext = {
|
|
307
1145
|
em: parentStyle.fontSize,
|
|
308
1146
|
rem: this.initial.fontSize,
|
|
1147
|
+
initial: this.initial,
|
|
309
1148
|
vw: this.viewportWidth,
|
|
310
1149
|
vh: this.viewportHeight,
|
|
311
1150
|
scale: this.scale,
|
|
1151
|
+
ex: () => this._exOf(parentStyle),
|
|
1152
|
+
ch: () => this._chOf(parentStyle),
|
|
1153
|
+
families: this._mapFamilies,
|
|
1154
|
+
lh: () => this._lineHeightOf(parentStyle),
|
|
1155
|
+
rlh: () => this._lineHeightOf(this.initial),
|
|
312
1156
|
};
|
|
1157
|
+
// the family goes with the size, so an `ex` after it is its font's
|
|
1158
|
+
let sized = false;
|
|
1159
|
+
let keyword = false;
|
|
313
1160
|
for (const c of candidates) {
|
|
314
1161
|
for (const d of pick(c)) {
|
|
315
|
-
if (d.prop === 'font-
|
|
316
|
-
|
|
1162
|
+
if (d.prop === 'font-family') {
|
|
1163
|
+
this._apply(style, parentStyle, d, ctxParent);
|
|
1164
|
+
} else if (d.prop === 'font-size' || d.prop === 'font') {
|
|
1165
|
+
// NaN until a size is set, so one the declaration did not take
|
|
1166
|
+
// leaves the size where it was, and says nothing of it
|
|
1167
|
+
const was = style.fontSize;
|
|
1168
|
+
style.fontSize = NaN;
|
|
1169
|
+
this._apply(style, parentStyle, d, ctxParent);
|
|
1170
|
+
if (Number.isNaN(style.fontSize)) {
|
|
1171
|
+
style.fontSize = was;
|
|
1172
|
+
} else {
|
|
1173
|
+
sized = true;
|
|
1174
|
+
keyword = !d.vars && sizeKeyword(d.prop, d.value);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
// The generic `monospace` on its own has a smaller size than the rest,
|
|
1180
|
+
// 13 pixels to their 16 (the "fixed" font size every browser keeps):
|
|
1181
|
+
// an element whose family becomes it, or stops being it, scales the
|
|
1182
|
+
// size it inherits by that, and a keyword size is read from the
|
|
1183
|
+
// smaller scale — the size it sets itself stays its own. Blink's
|
|
1184
|
+
// CheckForGenericFamilyChange, and why a `<pre>` in a browser is 13px.
|
|
1185
|
+
const mono = monospaceOnly(style.fontFamily);
|
|
1186
|
+
if (
|
|
1187
|
+
sized ? keyword && mono : mono !== monospaceOnly(parentStyle.fontFamily)
|
|
1188
|
+
) {
|
|
1189
|
+
style.fontSize *= mono ? FIXED_SIZE : 1 / FIXED_SIZE;
|
|
1190
|
+
}
|
|
1191
|
+
const ctx: UnitContext = {
|
|
1192
|
+
...ctxParent,
|
|
1193
|
+
em: style.fontSize,
|
|
1194
|
+
ex: () => this._exOf(style),
|
|
1195
|
+
ch: () => this._chOf(style),
|
|
1196
|
+
lh: () => this._lineHeightOf(style),
|
|
1197
|
+
};
|
|
1198
|
+
// the line height, which an `lh` in any other declaration reads, ahead
|
|
1199
|
+
// of them: set again with the rest, in its place among them
|
|
1200
|
+
if (this._lh) {
|
|
1201
|
+
for (const c of candidates) {
|
|
1202
|
+
for (const d of pick(c)) {
|
|
1203
|
+
if (d.prop === 'line-height') this._apply(style, parentStyle, d, ctx);
|
|
317
1204
|
}
|
|
318
1205
|
}
|
|
319
1206
|
}
|
|
320
|
-
const
|
|
1207
|
+
const settled = style.fontSize;
|
|
321
1208
|
for (const c of candidates) {
|
|
322
1209
|
for (const d of pick(c)) {
|
|
323
|
-
if (d.prop === 'font-size') continue;
|
|
324
|
-
|
|
1210
|
+
if (d.prop === 'font-size' || d.custom) continue;
|
|
1211
|
+
this._apply(style, parentStyle, d, ctx);
|
|
325
1212
|
}
|
|
326
1213
|
}
|
|
1214
|
+
// A `font` is applied again, to keep its other longhands in cascade
|
|
1215
|
+
// order, and it sets the size it names as well: the size is the first
|
|
1216
|
+
// pass's, which the declarations that outrank the `font` had their say
|
|
1217
|
+
// in, and the scale after them. `span { font: 15px/1 Ahem }` under
|
|
1218
|
+
// `.b > span { font-size: 3.75em }` was 15px, its `em`s 3.75 of its
|
|
1219
|
+
// parent's.
|
|
1220
|
+
style.fontSize = settled;
|
|
327
1221
|
|
|
1222
|
+
// A table never keeps HTML's alignment, `-webkit-center` and its kin:
|
|
1223
|
+
// the `<td align="center">` every mail centres its body table in
|
|
1224
|
+
// centres the table, and the text in its cells stays at their start —
|
|
1225
|
+
// as Blink resets it ("tables never support the -webkit-* values for
|
|
1226
|
+
// text-align"). An author's own `text-align: center` is not HTML's, and
|
|
1227
|
+
// is inherited into the table as it always was.
|
|
1228
|
+
if (style.alignBlocks !== null && tagOf(el) === 'table') {
|
|
1229
|
+
style.textAlign = 'start';
|
|
1230
|
+
style.alignBlocks = null;
|
|
1231
|
+
}
|
|
1232
|
+
settleClamp(style, parentStyle);
|
|
1233
|
+
settleOverflow(style);
|
|
1234
|
+
settleContentVisibility(style);
|
|
328
1235
|
blockify(style, inFlexContainer);
|
|
1236
|
+
decorate(style);
|
|
1237
|
+
// which faces of the document's own families this family, weight and
|
|
1238
|
+
// slant ask for — known only now, with all three computed
|
|
1239
|
+
this._families?.note(style);
|
|
329
1240
|
return style;
|
|
330
1241
|
}
|
|
331
1242
|
|
|
1243
|
+
/** One declaration, its `var()`s replaced first. One that names a
|
|
1244
|
+
* custom property with no value, and has no fallback, is invalid at
|
|
1245
|
+
* computed-value time, and the property is as though `unset`. */
|
|
1246
|
+
private _apply(
|
|
1247
|
+
style: ComputedStyle,
|
|
1248
|
+
parentStyle: ComputedStyle,
|
|
1249
|
+
d: Declaration,
|
|
1250
|
+
ctx: UnitContext,
|
|
1251
|
+
): void {
|
|
1252
|
+
if (!d.vars) {
|
|
1253
|
+
applyDeclaration(style, parentStyle, d.prop, d.value, ctx);
|
|
1254
|
+
return;
|
|
1255
|
+
}
|
|
1256
|
+
// unset first: a value that does not parse once it is substituted is
|
|
1257
|
+
// invalid at computed-value time, and does not leave the property what
|
|
1258
|
+
// an earlier declaration gave it
|
|
1259
|
+
if (isInherited(d.prop)) {
|
|
1260
|
+
applyDeclaration(style, parentStyle, d.prop, 'inherit', ctx);
|
|
1261
|
+
} else {
|
|
1262
|
+
initialOne(style, this.initial, d.prop);
|
|
1263
|
+
}
|
|
1264
|
+
const value = substituteIn(d.value, style.custom);
|
|
1265
|
+
if (value !== null)
|
|
1266
|
+
applyDeclaration(style, parentStyle, d.prop, value, ctx);
|
|
1267
|
+
}
|
|
1268
|
+
|
|
332
1269
|
/**
|
|
333
1270
|
* The root's style — nothing above it to inherit from.
|
|
334
1271
|
*
|
|
@@ -337,24 +1274,41 @@ export class Cascade {
|
|
|
337
1274
|
* a `<body>` would have had: the UA sheet's margin and font, and any
|
|
338
1275
|
* author `body { … }` rule. Without it a fragment renders hard against the
|
|
339
1276
|
* left edge while the same markup inside `<html><body>` does not, which
|
|
340
|
-
* reads as a bug in the renderer rather than as a missing element.
|
|
1277
|
+
* reads as a bug in the renderer rather than as a missing element. With
|
|
1278
|
+
* no `<html>` either, the body inherits from an `<html>` that author
|
|
1279
|
+
* `html { … }` rules have styled, as the one a browser implies would be.
|
|
1280
|
+
*
|
|
1281
|
+
* A document with a `<body>` and no `<html>` — the usual shape of a page
|
|
1282
|
+
* that starts `<!DOCTYPE html><title>` — has the root box standing in for
|
|
1283
|
+
* that implied `<html>` instead, so an `html { font-size }` still reaches
|
|
1284
|
+
* the body. Taken as the initial style, the rule matched nothing and the
|
|
1285
|
+
* whole page came out at the theme's size.
|
|
341
1286
|
*/
|
|
342
|
-
rootStyle(hasBody: boolean): ComputedStyle {
|
|
343
|
-
const style =
|
|
1287
|
+
rootStyle(hasBody: boolean, hasHtml = true): ComputedStyle {
|
|
1288
|
+
const style = copyStyle(this.initial);
|
|
344
1289
|
style.display = 'block';
|
|
345
|
-
if (hasBody) return style;
|
|
1290
|
+
if (hasBody && hasHtml) return style;
|
|
346
1291
|
const synthetic = new DomElement('body', {}, []);
|
|
347
|
-
|
|
1292
|
+
let parent = style;
|
|
1293
|
+
if (!hasHtml) {
|
|
1294
|
+
const html = new DomElement('html', {}, hasBody ? [] : [synthetic]);
|
|
1295
|
+
synthetic.parent = html;
|
|
1296
|
+
parent = this.styleFor(html, style, false);
|
|
1297
|
+
if (hasBody) return asRoot(parent);
|
|
1298
|
+
}
|
|
348
1299
|
// Only the box the body would have drawn is taken, not its layout role:
|
|
349
1300
|
// the root is still the initial containing block.
|
|
350
|
-
|
|
351
|
-
bodyStyle.position = 'static';
|
|
352
|
-
bodyStyle.float = 'none';
|
|
353
|
-
return bodyStyle;
|
|
1301
|
+
return asRoot(this.styleFor(synthetic, parent, false));
|
|
354
1302
|
}
|
|
355
1303
|
|
|
356
|
-
|
|
357
|
-
|
|
1304
|
+
/** The rules of `index` that match `el`, pushed onto `out` as candidates,
|
|
1305
|
+
* and their ids onto `matched` in the order they were tried. */
|
|
1306
|
+
private _matchInto(
|
|
1307
|
+
index: RuleIndex,
|
|
1308
|
+
el: Element,
|
|
1309
|
+
out: Candidate[],
|
|
1310
|
+
matched?: number[],
|
|
1311
|
+
): void {
|
|
358
1312
|
// A media query's width is CSS pixels; the viewport is kept in device.
|
|
359
1313
|
const width = this.viewportWidth / this.scale;
|
|
360
1314
|
|
|
@@ -366,12 +1320,7 @@ export class Cascade {
|
|
|
366
1320
|
if (!indexed.compiled) {
|
|
367
1321
|
indexed.compiled = true;
|
|
368
1322
|
try {
|
|
369
|
-
indexed.match =
|
|
370
|
-
adapter: this._adapter,
|
|
371
|
-
xmlMode: false,
|
|
372
|
-
} as unknown as Parameters<typeof compile>[1]) as unknown as (
|
|
373
|
-
node: Element,
|
|
374
|
-
) => boolean;
|
|
1323
|
+
indexed.match = compileSelector(rule.selector, this._adapter);
|
|
375
1324
|
} catch {
|
|
376
1325
|
// A selector this matcher does not know (`::-moz-…`, a CSS4 form
|
|
377
1326
|
// it has not learnt) drops out of the cascade rather than out of
|
|
@@ -380,26 +1329,36 @@ export class Cascade {
|
|
|
380
1329
|
}
|
|
381
1330
|
}
|
|
382
1331
|
if (!indexed.match || !indexed.match(el)) continue;
|
|
1332
|
+
matched?.push(indexed.id);
|
|
383
1333
|
const origin = rule.order < 0 ? Origin.UserAgent : Origin.Author;
|
|
384
1334
|
pushRule(out, rule, origin);
|
|
385
1335
|
}
|
|
386
1336
|
};
|
|
387
1337
|
|
|
388
1338
|
const id = attr(el, 'id');
|
|
389
|
-
if (id) consider(
|
|
1339
|
+
if (id) consider(index.byId.get(id));
|
|
390
1340
|
const className = attr(el, 'class');
|
|
391
1341
|
if (className) {
|
|
392
1342
|
for (const name of className.split(/\s+/)) {
|
|
393
|
-
if (name) consider(
|
|
1343
|
+
if (name) consider(index.byClass.get(name));
|
|
394
1344
|
}
|
|
395
1345
|
}
|
|
396
|
-
consider(
|
|
397
|
-
consider(
|
|
1346
|
+
consider(index.byTag.get(tagOf(el)));
|
|
1347
|
+
consider(index.universal);
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
/** Every rule and hint that applies to `el`, in cascade order — and, into
|
|
1351
|
+
* `matched` when it is passed, the ids of the rules, in the order they
|
|
1352
|
+
* were tried. */
|
|
1353
|
+
private _candidates(el: Element, matched?: number[]): Candidate[] {
|
|
1354
|
+
const out: Candidate[] = [];
|
|
1355
|
+
this._matchInto(this._index, el, out, matched);
|
|
398
1356
|
|
|
399
1357
|
const hints = presentationHints(el);
|
|
400
1358
|
if (hints.length) {
|
|
401
1359
|
out.push({
|
|
402
1360
|
origin: Origin.Presentation,
|
|
1361
|
+
layer: null,
|
|
403
1362
|
specificity: 0,
|
|
404
1363
|
order: 0,
|
|
405
1364
|
declarations: hints,
|
|
@@ -410,11 +1369,15 @@ export class Cascade {
|
|
|
410
1369
|
const inline = attr(el, 'style');
|
|
411
1370
|
if (inline) {
|
|
412
1371
|
const declarations = parseDeclarations(inline);
|
|
1372
|
+
if (!this._vars && usesVars(declarations)) this._vars = true;
|
|
1373
|
+
this._noteViewportUnits(declarations);
|
|
1374
|
+
if (!this._lh && usesLh(declarations)) this._lh = true;
|
|
413
1375
|
const normal = declarations.filter((d) => !d.important);
|
|
414
1376
|
const important = declarations.filter((d) => d.important);
|
|
415
1377
|
if (normal.length) {
|
|
416
1378
|
out.push({
|
|
417
1379
|
origin: Origin.Inline,
|
|
1380
|
+
layer: null,
|
|
418
1381
|
specificity: 0,
|
|
419
1382
|
order: 0,
|
|
420
1383
|
declarations: normal,
|
|
@@ -424,6 +1387,7 @@ export class Cascade {
|
|
|
424
1387
|
if (important.length) {
|
|
425
1388
|
out.push({
|
|
426
1389
|
origin: Origin.InlineImportant,
|
|
1390
|
+
layer: null,
|
|
427
1391
|
specificity: 0,
|
|
428
1392
|
order: 0,
|
|
429
1393
|
declarations: important,
|
|
@@ -437,6 +1401,145 @@ export class Cascade {
|
|
|
437
1401
|
}
|
|
438
1402
|
}
|
|
439
1403
|
|
|
1404
|
+
/** An implied element's style as the root box's: its look, not its role —
|
|
1405
|
+
* the root is still a block, in flow, and the initial containing block. */
|
|
1406
|
+
function asRoot(style: ComputedStyle): ComputedStyle {
|
|
1407
|
+
style.display = 'block';
|
|
1408
|
+
style.position = 'static';
|
|
1409
|
+
style.float = 'none';
|
|
1410
|
+
return style;
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
/** `:root` is the `<html>` element: the one a browser implies around a
|
|
1414
|
+
* fragment, whose style the root box takes (`rootStyle`), and never a
|
|
1415
|
+
* fragment's top-level elements, which css-select would take for it. */
|
|
1416
|
+
const PSEUDOS = {
|
|
1417
|
+
root: (el: Element) =>
|
|
1418
|
+
el.name === 'html' && !(el.parent && isTag(el.parent as Element)),
|
|
1419
|
+
// css-select's, but its ranges and tags lower-cased as ASCII has it, and
|
|
1420
|
+
// no other script (CSS 2.1 4.1.3): Unicode's took `:lang(\u212Al)`, a
|
|
1421
|
+
// Kelvin sign for the K, for `:lang(kl)`
|
|
1422
|
+
lang: (el: Element, code: string | null): boolean => {
|
|
1423
|
+
const ranges = (code ?? '')
|
|
1424
|
+
.split(',')
|
|
1425
|
+
.map((range) => range.trim().replace(/^['"]|['"]$/g, ''))
|
|
1426
|
+
.filter((range) => range.length > 0)
|
|
1427
|
+
.map((range) => asciiLower(range).split('-'));
|
|
1428
|
+
let root = el;
|
|
1429
|
+
for (let node: Element | null = el; node;) {
|
|
1430
|
+
const value = node.attribs['xml:lang'] ?? node.attribs.lang;
|
|
1431
|
+
if (value != null) {
|
|
1432
|
+
if (!value) return ranges.some((range) => range[0] === '');
|
|
1433
|
+
const tag = asciiLower(value).split('-');
|
|
1434
|
+
return ranges.some((range) => langRangeMatches(tag, range));
|
|
1435
|
+
}
|
|
1436
|
+
root = node;
|
|
1437
|
+
const parent: Element['parent'] = node.parent;
|
|
1438
|
+
node = parent && isTag(parent as Element) ? (parent as Element) : null;
|
|
1439
|
+
}
|
|
1440
|
+
// with no element saying, the document's, as its `<meta>` sets it —
|
|
1441
|
+
// searched from the document, where a fragment's `<meta>` is a
|
|
1442
|
+
// sibling of what it covers
|
|
1443
|
+
const pragma = pragmaLanguage(root.parent ?? root);
|
|
1444
|
+
if (!pragma) return ranges.some((range) => range[0] === '');
|
|
1445
|
+
const tag = asciiLower(pragma).split('-');
|
|
1446
|
+
return ranges.some((range) => langRangeMatches(tag, range));
|
|
1447
|
+
},
|
|
1448
|
+
};
|
|
1449
|
+
|
|
1450
|
+
/** A length in a unit of the viewport: a number, then the unit, and no
|
|
1451
|
+
* more of a name after it. */
|
|
1452
|
+
const VIEWPORT_UNIT = /\d(?:vw|vh|vmin|vmax)(?![\w-])/i;
|
|
1453
|
+
const VIEWPORT_UNITS = /\d(vw|vh|vmin|vmax)(?![\w-])/gi;
|
|
1454
|
+
|
|
1455
|
+
/**
|
|
1456
|
+
* A selector with its `[attr~=""]` made one that matches nothing: an empty
|
|
1457
|
+
* string is no word of a list, and such a selector represents nothing
|
|
1458
|
+
* (Selectors 3, 6.3.1) — which the matcher's `~=` is not told, taking the
|
|
1459
|
+
* empty word for one between two spaces, or at either end of none.
|
|
1460
|
+
*/
|
|
1461
|
+
function noEmptyWords(selector: string): string {
|
|
1462
|
+
if (!selector.includes('~=')) return selector;
|
|
1463
|
+
return selector.replace(
|
|
1464
|
+
/\[[^\]"']*~=\s*(?:""|'')\s*(?:[is]\s*)?\]/gi,
|
|
1465
|
+
':not(*)',
|
|
1466
|
+
);
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
/**
|
|
1470
|
+
* A document's language as a `<meta http-equiv="content-language">` sets
|
|
1471
|
+
* it — HTML's pragma-set default language, the last such `<meta>`'s
|
|
1472
|
+
* `content` up to its first white space, and none where it lists more
|
|
1473
|
+
* than one — by the document's root element, found once.
|
|
1474
|
+
*/
|
|
1475
|
+
function pragmaLanguage(root: { children: unknown[] }): string {
|
|
1476
|
+
let lang = PRAGMA_LANGUAGE.get(root);
|
|
1477
|
+
if (lang !== undefined) return lang;
|
|
1478
|
+
lang = '';
|
|
1479
|
+
const stack: { children: unknown[] }[] = [root];
|
|
1480
|
+
while (stack.length) {
|
|
1481
|
+
const node = stack.pop()! as Element;
|
|
1482
|
+
if (
|
|
1483
|
+
isTag(node) &&
|
|
1484
|
+
node.name === 'meta' &&
|
|
1485
|
+
node.attribs['http-equiv']?.trim().toLowerCase() === 'content-language'
|
|
1486
|
+
) {
|
|
1487
|
+
const content = node.attribs.content;
|
|
1488
|
+
if (content !== undefined && !content.includes(',')) {
|
|
1489
|
+
const candidate = content.trim().split(/[\t\n\f\r ]/)[0];
|
|
1490
|
+
if (candidate) lang = candidate;
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
// in document order: the last one pushed is taken first
|
|
1494
|
+
for (let i = node.children.length - 1; i >= 0; i -= 1) {
|
|
1495
|
+
const child = node.children[i];
|
|
1496
|
+
if (isTag(child as Element)) stack.push(child as Element);
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
PRAGMA_LANGUAGE.set(root, lang);
|
|
1500
|
+
return lang;
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
const PRAGMA_LANGUAGE = new WeakMap<object, string>();
|
|
1504
|
+
|
|
1505
|
+
/** RFC 4647's extended filtering, of a tag's subtags by a range's, as
|
|
1506
|
+
* css-select does it. */
|
|
1507
|
+
function langRangeMatches(tag: string[], range: string[]): boolean {
|
|
1508
|
+
if (range[0] !== '*' && range[0] !== tag[0]) return false;
|
|
1509
|
+
let at = 1;
|
|
1510
|
+
for (let r = 1; r < range.length; r += 1) {
|
|
1511
|
+
if (range[r] === '*') continue;
|
|
1512
|
+
while (at < tag.length && tag[at] !== range[r]) {
|
|
1513
|
+
if (tag[at++].length <= 1) return false;
|
|
1514
|
+
}
|
|
1515
|
+
if (at >= tag.length) return false;
|
|
1516
|
+
at += 1;
|
|
1517
|
+
}
|
|
1518
|
+
return true;
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
/** Whether any of these declarations sets a custom property or reads one. */
|
|
1522
|
+
function usesVars(declarations: readonly Declaration[]): boolean {
|
|
1523
|
+
for (const d of declarations) if (d.custom || d.vars) return true;
|
|
1524
|
+
return false;
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
/** Whether any of the declarations has a length in `lh` or `rlh`. */
|
|
1528
|
+
function usesLh(declarations: readonly Declaration[]): boolean {
|
|
1529
|
+
for (const d of declarations) if (LH.test(d.value)) return true;
|
|
1530
|
+
return false;
|
|
1531
|
+
}
|
|
1532
|
+
const LH = /\d\.?r?lh\b/i;
|
|
1533
|
+
|
|
1534
|
+
/** Whether a candidate declares `content`. */
|
|
1535
|
+
function setsContent(c: Candidate): boolean {
|
|
1536
|
+
for (const d of pick(c)) if (d.prop === 'content') return true;
|
|
1537
|
+
return false;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
/** Whether a rule's declarations set a custom property, by the array. */
|
|
1541
|
+
const CUSTOM_IN = new WeakMap<Declaration[], boolean>();
|
|
1542
|
+
|
|
440
1543
|
function pick(c: Candidate): Declaration[] {
|
|
441
1544
|
return c.only < 0 ? c.declarations : [c.declarations[c.only]];
|
|
442
1545
|
}
|
|
@@ -452,6 +1555,7 @@ function pushRule(out: Candidate[], rule: StyleRule, origin: Origin): void {
|
|
|
452
1555
|
if (!hasImportant) {
|
|
453
1556
|
out.push({
|
|
454
1557
|
origin,
|
|
1558
|
+
layer: rule.layer,
|
|
455
1559
|
specificity: rule.specificity,
|
|
456
1560
|
order: rule.order,
|
|
457
1561
|
declarations: rule.declarations,
|
|
@@ -469,6 +1573,7 @@ function pushRule(out: Candidate[], rule: StyleRule, origin: Origin): void {
|
|
|
469
1573
|
? Origin.UserAgent
|
|
470
1574
|
: Origin.AuthorImportant
|
|
471
1575
|
: origin,
|
|
1576
|
+
layer: rule.layer,
|
|
472
1577
|
specificity: rule.specificity,
|
|
473
1578
|
order: rule.order,
|
|
474
1579
|
declarations: rule.declarations,
|
|
@@ -479,10 +1584,34 @@ function pushRule(out: Candidate[], rule: StyleRule, origin: Origin): void {
|
|
|
479
1584
|
|
|
480
1585
|
function byCascade(a: Candidate, b: Candidate): number {
|
|
481
1586
|
if (a.origin !== b.origin) return a.origin - b.origin;
|
|
1587
|
+
if (a.layer !== b.layer) {
|
|
1588
|
+
// a later layer wins over an earlier one whatever the specificity, and
|
|
1589
|
+
// for `!important` an earlier one does (CSS Cascade 5, 6.4)
|
|
1590
|
+
const by = compareLayers(a.layer, b.layer);
|
|
1591
|
+
if (by !== 0) return a.origin >= Origin.AuthorImportant ? -by : by;
|
|
1592
|
+
}
|
|
482
1593
|
if (a.specificity !== b.specificity) return a.specificity - b.specificity;
|
|
483
1594
|
return a.order - b.order;
|
|
484
1595
|
}
|
|
485
1596
|
|
|
1597
|
+
/**
|
|
1598
|
+
* Which of two layers wins for a normal declaration: a later one over an
|
|
1599
|
+
* earlier one, and a rule in no layer over any, as a rule directly in a
|
|
1600
|
+
* layer is over the layers inside it.
|
|
1601
|
+
*/
|
|
1602
|
+
function compareLayers(
|
|
1603
|
+
a: readonly number[] | null,
|
|
1604
|
+
b: readonly number[] | null,
|
|
1605
|
+
): number {
|
|
1606
|
+
if (a === null) return b === null ? 0 : 1;
|
|
1607
|
+
if (b === null) return -1;
|
|
1608
|
+
const n = Math.min(a.length, b.length);
|
|
1609
|
+
for (let i = 0; i < n; i += 1) {
|
|
1610
|
+
if (a[i] !== b[i]) return a[i] - b[i];
|
|
1611
|
+
}
|
|
1612
|
+
return b.length - a.length;
|
|
1613
|
+
}
|
|
1614
|
+
|
|
486
1615
|
/**
|
|
487
1616
|
* The presentational attributes, as declarations. They sit above the UA
|
|
488
1617
|
* sheet and below every author rule, which is where HTML says they go — and
|
|
@@ -500,16 +1629,79 @@ function presentationHints(el: Element): Declaration[] {
|
|
|
500
1629
|
const align = attr(el, 'align');
|
|
501
1630
|
if (align) {
|
|
502
1631
|
const v = align.toLowerCase();
|
|
503
|
-
|
|
504
|
-
|
|
1632
|
+
const side = v === 'middle' ? 'center' : v;
|
|
1633
|
+
if ((tag === 'img' || tag === 'table') && (v === 'left' || v === 'right')) {
|
|
1634
|
+
push('float', v);
|
|
1635
|
+
} else if (tag === 'img') {
|
|
1636
|
+
// an image's other alignments are its line's
|
|
1637
|
+
const va = IMAGE_ALIGN[v];
|
|
1638
|
+
if (va) push('vertical-align', va);
|
|
1639
|
+
} else if (tag === 'hr') {
|
|
1640
|
+
// a rule narrower than its line stands where it is aligned
|
|
1641
|
+
if (v === 'left') {
|
|
1642
|
+
push('margin-left', '0');
|
|
1643
|
+
push('margin-right', 'auto');
|
|
1644
|
+
} else if (v === 'right') {
|
|
1645
|
+
push('margin-left', 'auto');
|
|
1646
|
+
push('margin-right', '0');
|
|
1647
|
+
}
|
|
1648
|
+
} else if (tag === 'table') {
|
|
1649
|
+
// a table's `align` places the table and leaves its text alone: set
|
|
1650
|
+
// as its text's, `<table align="center">` — the frame of nearly
|
|
1651
|
+
// every mail — stood at the left with its cells' text centred
|
|
1652
|
+
if (side === 'center') {
|
|
1653
|
+
push('margin-left', 'auto');
|
|
1654
|
+
push('margin-right', 'auto');
|
|
1655
|
+
}
|
|
1656
|
+
} else if (
|
|
1657
|
+
ALIGNS_BLOCKS.has(tag) &&
|
|
1658
|
+
(side === 'center' || side === 'left' || side === 'right')
|
|
1659
|
+
) {
|
|
1660
|
+
// these align the blocks in them as well as their text (HTML's
|
|
1661
|
+
// rendering, "align descendants"): the table a `<td align="center">`
|
|
1662
|
+
// or a `<div align="center">` holds is centred in it
|
|
1663
|
+
push('text-align', `-webkit-${side}`);
|
|
1664
|
+
} else if (side === 'center') push('text-align', 'center');
|
|
505
1665
|
else if (v === 'left' || v === 'right' || v === 'justify')
|
|
506
1666
|
push('text-align', v);
|
|
507
1667
|
}
|
|
508
1668
|
const valign = attr(el, 'valign');
|
|
509
1669
|
if (valign) push('vertical-align', valign.toLowerCase());
|
|
1670
|
+
// HTML's `dir` is its element's `direction`: `<table dir="rtl">` starts
|
|
1671
|
+
// at the right, and a Hebrew or an Arabic message is written this way
|
|
1672
|
+
const dir = attr(el, 'dir')?.toLowerCase();
|
|
1673
|
+
if (dir === 'rtl' || dir === 'ltr') push('direction', dir);
|
|
1674
|
+
// and it isolates the element from the text around it, or with `auto`
|
|
1675
|
+
// takes its first strong letter's direction (HTML's UA sheet: `[dir]`,
|
|
1676
|
+
// `[dir=auto i]`, `bdo[dir]`)
|
|
1677
|
+
if (dir !== undefined) {
|
|
1678
|
+
push(
|
|
1679
|
+
'unicode-bidi',
|
|
1680
|
+
tag === 'bdo'
|
|
1681
|
+
? 'isolate-override'
|
|
1682
|
+
: dir === 'auto'
|
|
1683
|
+
? 'plaintext'
|
|
1684
|
+
: 'isolate',
|
|
1685
|
+
);
|
|
1686
|
+
}
|
|
510
1687
|
|
|
511
1688
|
const bgcolor = attr(el, 'bgcolor');
|
|
512
1689
|
if (bgcolor) push('background-color', bgcolor);
|
|
1690
|
+
if (BACKGROUNDS.has(tag)) {
|
|
1691
|
+
const background = attr(el, 'background')?.trim();
|
|
1692
|
+
if (background) {
|
|
1693
|
+
push('background-image', `url("${background.replace(/["\\\n]/g, '')}")`);
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
if (tag === 'body') {
|
|
1697
|
+
// the text's colour, and the links'
|
|
1698
|
+
const text = attr(el, 'text');
|
|
1699
|
+
if (text) push('color', text);
|
|
1700
|
+
} else if (tag === 'a' && attr(el, 'href') !== undefined) {
|
|
1701
|
+
const body = closestBody(el);
|
|
1702
|
+
const link = body ? attr(body, 'link') : undefined;
|
|
1703
|
+
if (link) push('color', link);
|
|
1704
|
+
}
|
|
513
1705
|
const color = attr(el, 'color');
|
|
514
1706
|
if (color && (tag === 'font' || tag === 'basefont')) push('color', color);
|
|
515
1707
|
const face = attr(el, 'face');
|
|
@@ -529,6 +1721,24 @@ function presentationHints(el: Element): Declaration[] {
|
|
|
529
1721
|
if (width) push('width', lengthAttr(width));
|
|
530
1722
|
const height = attr(el, 'height');
|
|
531
1723
|
if (height) push('height', lengthAttr(height));
|
|
1724
|
+
// and, both numbers, an image's the ratio it has before it loads, or
|
|
1725
|
+
// where it has none of its own (HTML 15.4.3, "map to the aspect-ratio
|
|
1726
|
+
// property")
|
|
1727
|
+
if (width && height && RATIO_SIZED.has(tag)) {
|
|
1728
|
+
const w = parseFloat(width);
|
|
1729
|
+
const h = parseFloat(height);
|
|
1730
|
+
if (w > 0 && h > 0 && !/%/.test(width + height)) {
|
|
1731
|
+
push('aspect-ratio', `auto ${w} / ${h}`);
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
} else if (tag === 'svg') {
|
|
1735
|
+
// an SVG's are CSS lengths of their own, units and all (SVG 2, 5.1.1)
|
|
1736
|
+
const width = attr(el, 'width');
|
|
1737
|
+
const w = width ? svgSizeHint(width) : null;
|
|
1738
|
+
if (w) push('width', w);
|
|
1739
|
+
const height = attr(el, 'height');
|
|
1740
|
+
const h = height ? svgSizeHint(height) : null;
|
|
1741
|
+
if (h) push('height', h);
|
|
532
1742
|
}
|
|
533
1743
|
|
|
534
1744
|
if (tag === 'table') {
|
|
@@ -550,13 +1760,50 @@ function presentationHints(el: Element): Declaration[] {
|
|
|
550
1760
|
if (tag === 'hr') {
|
|
551
1761
|
const noshade = attr(el, 'noshade');
|
|
552
1762
|
if (noshade !== undefined) push('border-top-width', '2px');
|
|
1763
|
+
// the rule's colour and its thickness
|
|
1764
|
+
const color = attr(el, 'color');
|
|
1765
|
+
if (color) push('border-top-color', color);
|
|
1766
|
+
const size = attr(el, 'size');
|
|
1767
|
+
if (size && Number(size) > 0) push('border-top-width', `${Number(size)}px`);
|
|
1768
|
+
}
|
|
1769
|
+
if ((tag === 'td' || tag === 'th') && attr(el, 'nowrap') !== undefined) {
|
|
1770
|
+
push('white-space', 'nowrap');
|
|
1771
|
+
}
|
|
1772
|
+
if (tag === 'br') {
|
|
1773
|
+
// below the floats: `<br clear="all">` after a floated image
|
|
1774
|
+
const clear = attr(el, 'clear')?.toLowerCase();
|
|
1775
|
+
if (clear === 'all' || clear === 'both') push('clear', 'both');
|
|
1776
|
+
else if (clear === 'left' || clear === 'right') push('clear', clear);
|
|
553
1777
|
}
|
|
554
1778
|
if (tag === 'ol') {
|
|
555
1779
|
const type = attr(el, 'type');
|
|
556
1780
|
const mapped = OL_TYPES[type ?? ''];
|
|
557
1781
|
if (mapped) push('list-style-type', mapped);
|
|
558
|
-
|
|
559
|
-
|
|
1782
|
+
// the number a list starts at is its `list-item` counter's, one before
|
|
1783
|
+
// it, or counting down one after it (HTML's rendering, "Lists")
|
|
1784
|
+
const start = htmlInteger(attr(el, 'start'));
|
|
1785
|
+
if (start !== null) {
|
|
1786
|
+
push(
|
|
1787
|
+
'counter-reset',
|
|
1788
|
+
attr(el, 'reversed') !== undefined
|
|
1789
|
+
? `reversed(list-item) ${start + 1}`
|
|
1790
|
+
: `list-item ${start - 1}`,
|
|
1791
|
+
);
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
if (tag === 'ul') {
|
|
1795
|
+
const type = attr(el, 'type')?.toLowerCase();
|
|
1796
|
+
if (type && BULLETS.has(type)) push('list-style-type', type);
|
|
1797
|
+
}
|
|
1798
|
+
if (tag === 'li') {
|
|
1799
|
+
// an item's `value` is the number it goes on from
|
|
1800
|
+
const value = htmlInteger(attr(el, 'value'));
|
|
1801
|
+
if (value !== null) push('counter-set', `list-item ${value}`);
|
|
1802
|
+
const type = attr(el, 'type');
|
|
1803
|
+
const mapped =
|
|
1804
|
+
OL_TYPES[type ?? ''] ??
|
|
1805
|
+
(type && BULLETS.has(type.toLowerCase()) ? type.toLowerCase() : null);
|
|
1806
|
+
if (mapped) push('list-style-type', mapped);
|
|
560
1807
|
}
|
|
561
1808
|
if (tag === 'img' || tag === 'object') {
|
|
562
1809
|
const hspace = attr(el, 'hspace');
|
|
@@ -573,6 +1820,56 @@ function presentationHints(el: Element): Declaration[] {
|
|
|
573
1820
|
return out;
|
|
574
1821
|
}
|
|
575
1822
|
|
|
1823
|
+
/** An image's `align` other than a side, as its `vertical-align`. */
|
|
1824
|
+
const IMAGE_ALIGN: Record<string, string> = {
|
|
1825
|
+
top: 'top',
|
|
1826
|
+
texttop: 'text-top',
|
|
1827
|
+
middle: 'middle',
|
|
1828
|
+
absmiddle: 'middle',
|
|
1829
|
+
center: 'middle',
|
|
1830
|
+
bottom: 'baseline',
|
|
1831
|
+
baseline: 'baseline',
|
|
1832
|
+
absbottom: 'bottom',
|
|
1833
|
+
};
|
|
1834
|
+
|
|
1835
|
+
/** The elements whose `align` aligns the blocks in them too. */
|
|
1836
|
+
const ALIGNS_BLOCKS = new Set([
|
|
1837
|
+
'div',
|
|
1838
|
+
'caption',
|
|
1839
|
+
'thead',
|
|
1840
|
+
'tbody',
|
|
1841
|
+
'tfoot',
|
|
1842
|
+
'tr',
|
|
1843
|
+
'td',
|
|
1844
|
+
'th',
|
|
1845
|
+
]);
|
|
1846
|
+
|
|
1847
|
+
/** The elements a `background` attribute gives a background image. */
|
|
1848
|
+
const BACKGROUNDS = new Set([
|
|
1849
|
+
'body',
|
|
1850
|
+
'table',
|
|
1851
|
+
'thead',
|
|
1852
|
+
'tbody',
|
|
1853
|
+
'tfoot',
|
|
1854
|
+
'tr',
|
|
1855
|
+
'td',
|
|
1856
|
+
'th',
|
|
1857
|
+
]);
|
|
1858
|
+
|
|
1859
|
+
function closestBody(el: Element): Element | null {
|
|
1860
|
+
let node = el.parent;
|
|
1861
|
+
while (node) {
|
|
1862
|
+
if (node.type === 'tag' && (node as Element).name === 'body') {
|
|
1863
|
+
return node as Element;
|
|
1864
|
+
}
|
|
1865
|
+
node = node.parent;
|
|
1866
|
+
}
|
|
1867
|
+
return null;
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
/** What the `width` and `height` attributes give a ratio as well. */
|
|
1871
|
+
const RATIO_SIZED = new Set(['img', 'video']);
|
|
1872
|
+
|
|
576
1873
|
const SIZED = new Set([
|
|
577
1874
|
'img',
|
|
578
1875
|
'table',
|
|
@@ -582,7 +1879,6 @@ const SIZED = new Set([
|
|
|
582
1879
|
'colgroup',
|
|
583
1880
|
'iframe',
|
|
584
1881
|
'video',
|
|
585
|
-
'canvas',
|
|
586
1882
|
'object',
|
|
587
1883
|
'embed',
|
|
588
1884
|
'hr',
|
|
@@ -596,6 +1892,18 @@ const OL_TYPES: Record<string, string> = {
|
|
|
596
1892
|
I: 'upper-roman',
|
|
597
1893
|
};
|
|
598
1894
|
|
|
1895
|
+
/** What a `<ul type>` or an `<li type>` names that is not a number. */
|
|
1896
|
+
const BULLETS = new Set(['disc', 'circle', 'square', 'none']);
|
|
1897
|
+
|
|
1898
|
+
/** An attribute read as HTML reads an integer: its leading digits, signed,
|
|
1899
|
+
* whatever follows them; null where it has none. A counter is a 32-bit
|
|
1900
|
+
* integer in a browser, and one past that is kept to it. */
|
|
1901
|
+
function htmlInteger(value: string | undefined): number | null {
|
|
1902
|
+
const match = value === undefined ? null : /^\s*([+-]?\d+)/.exec(value);
|
|
1903
|
+
if (!match) return null;
|
|
1904
|
+
return Math.max(-2147483648, Math.min(2147483647, Number(match[1])));
|
|
1905
|
+
}
|
|
1906
|
+
|
|
599
1907
|
const FONT_SIZE_STEPS = [1, 0.63, 0.82, 1, 1.13, 1.5, 2, 3];
|
|
600
1908
|
|
|
601
1909
|
/** A presentational length: bare numbers are pixels, `50%` stays a percent. */
|
|
@@ -615,3 +1923,36 @@ function closestTable(el: Element): Element | null {
|
|
|
615
1923
|
}
|
|
616
1924
|
return null;
|
|
617
1925
|
}
|
|
1926
|
+
|
|
1927
|
+
/** The generic fixed-width family's size to everyone else's: 13 to 16. */
|
|
1928
|
+
const FIXED_SIZE = 13 / 16;
|
|
1929
|
+
|
|
1930
|
+
/** Whether a family list is the generic `monospace` and nothing else,
|
|
1931
|
+
* which has the smaller size; a list with a fallback after it does not,
|
|
1932
|
+
* so `monospace, monospace` keeps the size it had. */
|
|
1933
|
+
function monospaceOnly(family: string): boolean {
|
|
1934
|
+
return family.length === 9 && family.toLowerCase() === 'monospace';
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
/** Whether a `font-size`, or the size in a `font`, is an absolute-size
|
|
1938
|
+
* keyword, or `initial`, which is `medium`. */
|
|
1939
|
+
function sizeKeyword(prop: string, value: string): boolean {
|
|
1940
|
+
const words = value.trim().toLowerCase();
|
|
1941
|
+
if (prop === 'font-size') return ABSOLUTE_SIZES.has(words);
|
|
1942
|
+
for (const word of words.split(/[\s/]+/)) {
|
|
1943
|
+
if (ABSOLUTE_SIZES.has(word)) return true;
|
|
1944
|
+
}
|
|
1945
|
+
return false;
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
const ABSOLUTE_SIZES = new Set([
|
|
1949
|
+
'xx-small',
|
|
1950
|
+
'x-small',
|
|
1951
|
+
'small',
|
|
1952
|
+
'medium',
|
|
1953
|
+
'large',
|
|
1954
|
+
'x-large',
|
|
1955
|
+
'xx-large',
|
|
1956
|
+
'xxx-large',
|
|
1957
|
+
'initial',
|
|
1958
|
+
]);
|