@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/dist/html/css/style.js
CHANGED
|
@@ -12,11 +12,59 @@
|
|
|
12
12
|
// - **Nothing here depends on the containing block.** Percentages and `auto`
|
|
13
13
|
// survive as `Len`s (see values.ts). A style computed at one width is
|
|
14
14
|
// correct at every width, which is the whole of the resize story.
|
|
15
|
-
import { AUTO, fourSides, keywordFontSize, parseAlpha, parseColor, parseLength, parseNumber,
|
|
15
|
+
import { AUTO, alphaOf, fourSides, inkColor, isTransparent, keywordFontSize, parseAlpha, parseColor, isPct, parseLength, parseNumber, parseWeight, resolve, splitCommas, splitValue, } from './values.js';
|
|
16
|
+
import { parseUrl, readIdent, startsIdent } from './parse.js';
|
|
17
|
+
import { DEFAULT_QUOTES, parseContent, parseCounterList, parseListStyleType, parseQuotes, } from './content.js';
|
|
18
|
+
/** Whether a length is one `padding` takes: not `auto`, not negative. A
|
|
19
|
+
* `calc()` with a percentage in it has no sign until layout, which clamps
|
|
20
|
+
* it at zero (CSS Values 4 10.12). */
|
|
21
|
+
function notNegative(len) {
|
|
22
|
+
if (len === AUTO)
|
|
23
|
+
return true;
|
|
24
|
+
if (typeof len === 'number')
|
|
25
|
+
return len >= 0;
|
|
26
|
+
return len.px !== undefined || len.pct >= 0;
|
|
27
|
+
}
|
|
28
|
+
function validPadding(len) {
|
|
29
|
+
if (len === null || len === AUTO)
|
|
30
|
+
return false;
|
|
31
|
+
return notNegative(len);
|
|
32
|
+
}
|
|
33
|
+
/** `auto`, or `rect()` of four lengths or `auto`s, commas between them or
|
|
34
|
+
* not; undefined for anything else, which leaves the value as it was. */
|
|
35
|
+
function parseClip(value, ctx) {
|
|
36
|
+
const v = value.trim();
|
|
37
|
+
if (/^auto$/i.test(v))
|
|
38
|
+
return null;
|
|
39
|
+
const m = /^rect\(([^)]*)\)$/i.exec(v);
|
|
40
|
+
if (!m)
|
|
41
|
+
return undefined;
|
|
42
|
+
const parts = m[1].includes(',')
|
|
43
|
+
? m[1].split(',').map((p) => p.trim())
|
|
44
|
+
: m[1].trim().split(/\s+/);
|
|
45
|
+
if (parts.length !== 4)
|
|
46
|
+
return undefined;
|
|
47
|
+
const edges = [];
|
|
48
|
+
for (const part of parts) {
|
|
49
|
+
if (/^auto$/i.test(part)) {
|
|
50
|
+
edges.push(null);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const len = parseLength(part, ctx);
|
|
54
|
+
if (typeof len !== 'number')
|
|
55
|
+
return undefined;
|
|
56
|
+
edges.push(len);
|
|
57
|
+
}
|
|
58
|
+
return { top: edges[0], right: edges[1], bottom: edges[2], left: edges[3] };
|
|
59
|
+
}
|
|
16
60
|
/** The properties that inherit. Named once, so `inherit()` and the `inherit`
|
|
17
61
|
* keyword cannot disagree about the list. */
|
|
18
|
-
|
|
62
|
+
/** The fields a style takes from its parent's (CSS 2.1's "Inherited:
|
|
63
|
+
* yes"), which `inherit` copies one by one; the test holds the two to the
|
|
64
|
+
* same list. */
|
|
65
|
+
export const INHERITED = [
|
|
19
66
|
'color',
|
|
67
|
+
'textFillColor',
|
|
20
68
|
'fontFamily',
|
|
21
69
|
'fontSize',
|
|
22
70
|
'fontWeight',
|
|
@@ -24,22 +72,49 @@ const INHERITED = [
|
|
|
24
72
|
'lineHeight',
|
|
25
73
|
'lineHeightIsLength',
|
|
26
74
|
'textAlign',
|
|
75
|
+
'textAlignLast',
|
|
76
|
+
'textJustify',
|
|
77
|
+
'blockEllipsis',
|
|
78
|
+
'alignBlocks',
|
|
27
79
|
'textIndent',
|
|
28
80
|
'textTransform',
|
|
81
|
+
'textUnderlineOffset',
|
|
29
82
|
'letterSpacing',
|
|
30
83
|
'wordSpacing',
|
|
84
|
+
'textShadow',
|
|
85
|
+
'fontVariantNumeric',
|
|
86
|
+
'fontVariantCaps',
|
|
87
|
+
'fontVariantLigatures',
|
|
88
|
+
'fontVariantPosition',
|
|
89
|
+
'fontKerning',
|
|
90
|
+
'fontFeatureSettings',
|
|
91
|
+
'tabSize',
|
|
92
|
+
'tabSizeIsLength',
|
|
31
93
|
'whiteSpace',
|
|
94
|
+
'overflowWrap',
|
|
95
|
+
'wordBreak',
|
|
96
|
+
'lineBreakAnywhere',
|
|
97
|
+
'textWrapStyle',
|
|
32
98
|
'direction',
|
|
33
99
|
'visibility',
|
|
34
100
|
'listStyleType',
|
|
35
101
|
'listStylePosition',
|
|
102
|
+
'listStyleImage',
|
|
36
103
|
'cursor',
|
|
37
104
|
'borderCollapse',
|
|
38
105
|
'borderSpacing',
|
|
106
|
+
'borderSpacingY',
|
|
107
|
+
'captionSide',
|
|
108
|
+
'emptyCells',
|
|
39
109
|
'tableTextAlignSet',
|
|
110
|
+
'quotes',
|
|
111
|
+
'custom',
|
|
40
112
|
];
|
|
41
|
-
export function initialStyle(look) {
|
|
42
|
-
|
|
113
|
+
export function initialStyle(look, scale = 1) {
|
|
114
|
+
// `medium`, in device pixels
|
|
115
|
+
const medium = BORDER_WIDTH_KEYWORDS.medium * scale;
|
|
116
|
+
// a copy of the literal, not the literal: see `copyStyle`
|
|
117
|
+
return copyStyle({
|
|
43
118
|
color: look.color,
|
|
44
119
|
fontFamily: look.fontFamily,
|
|
45
120
|
fontSize: look.fontSize,
|
|
@@ -48,35 +123,95 @@ export function initialStyle(look) {
|
|
|
48
123
|
lineHeight: 'normal',
|
|
49
124
|
lineHeightIsLength: false,
|
|
50
125
|
textAlign: 'start',
|
|
126
|
+
textAlignLast: 'auto',
|
|
127
|
+
textJustify: 'auto',
|
|
128
|
+
alignBlocks: null,
|
|
51
129
|
textIndent: 0,
|
|
52
130
|
textTransform: 'none',
|
|
53
131
|
letterSpacing: 0,
|
|
54
132
|
wordSpacing: 0,
|
|
133
|
+
textShadow: null,
|
|
134
|
+
fontVariantNumeric: '',
|
|
135
|
+
fontVariantCaps: '',
|
|
136
|
+
fontVariantLigatures: '',
|
|
137
|
+
fontVariantPosition: '',
|
|
138
|
+
fontKerning: '',
|
|
139
|
+
fontFeatureSettings: '',
|
|
140
|
+
tabSize: 8,
|
|
141
|
+
tabSizeIsLength: false,
|
|
55
142
|
whiteSpace: 'normal',
|
|
143
|
+
overflowWrap: 'normal',
|
|
144
|
+
wordBreak: 'normal',
|
|
145
|
+
lineBreakAnywhere: false,
|
|
146
|
+
textWrapStyle: 'auto',
|
|
56
147
|
direction: 'ltr',
|
|
148
|
+
unicodeBidi: 'normal',
|
|
57
149
|
visibility: 'visible',
|
|
58
150
|
listStyleType: 'disc',
|
|
59
151
|
listStylePosition: 'outside',
|
|
152
|
+
listStyleImage: null,
|
|
60
153
|
cursor: null,
|
|
61
154
|
borderCollapse: 'separate',
|
|
62
|
-
|
|
155
|
+
// CSS's initial value; a `<table>` gets its 2px from the UA sheet, and
|
|
156
|
+
// an anonymous table, which no sheet names, has none
|
|
157
|
+
borderSpacing: 0,
|
|
158
|
+
borderSpacingY: 0,
|
|
159
|
+
captionSide: 'top',
|
|
160
|
+
emptyCells: 'show',
|
|
63
161
|
tableTextAlignSet: false,
|
|
162
|
+
quotes: DEFAULT_QUOTES,
|
|
163
|
+
custom: null,
|
|
64
164
|
display: 'inline',
|
|
165
|
+
flowRoot: false,
|
|
166
|
+
contain: 0,
|
|
167
|
+
contentVisibility: 'visible',
|
|
168
|
+
overflowClipBox: 'padding-box',
|
|
169
|
+
overflowClipMargin: 0,
|
|
170
|
+
containIntrinsicWidth: null,
|
|
171
|
+
containIntrinsicHeight: null,
|
|
65
172
|
position: 'static',
|
|
66
173
|
float: 'none',
|
|
67
174
|
clear: 'none',
|
|
68
175
|
boxSizing: 'content-box',
|
|
176
|
+
lineClamp: null,
|
|
177
|
+
maxLines: null,
|
|
178
|
+
clampContinue: 'auto',
|
|
179
|
+
blockEllipsis: false,
|
|
180
|
+
webkitBox: null,
|
|
181
|
+
webkitBoxOrient: 'horizontal',
|
|
182
|
+
webkitBoxDirection: 'normal',
|
|
183
|
+
webkitBoxPack: null,
|
|
184
|
+
webkitBoxAlign: null,
|
|
185
|
+
webkitBoxFlex: 0,
|
|
186
|
+
columns: 0,
|
|
187
|
+
textOverflow: 'clip',
|
|
188
|
+
aspectRatio: null,
|
|
189
|
+
objectFit: 'fill',
|
|
190
|
+
appearance: 'auto',
|
|
191
|
+
objectPositionX: { pct: 50 },
|
|
192
|
+
objectPositionY: { pct: 50 },
|
|
69
193
|
overflowX: 'visible',
|
|
70
194
|
overflowY: 'visible',
|
|
195
|
+
clip: null,
|
|
71
196
|
opacity: 1,
|
|
197
|
+
translate: null,
|
|
198
|
+
transformTranslate: null,
|
|
72
199
|
zIndex: AUTO,
|
|
73
200
|
verticalAlign: 'baseline',
|
|
74
201
|
width: AUTO,
|
|
75
202
|
height: AUTO,
|
|
76
|
-
|
|
203
|
+
// `auto`, which is 0 but for a flex item (CSS Flexbox 4.5): what
|
|
204
|
+
// `min-w-0` undoes has to be told from what it is set to
|
|
205
|
+
minWidth: AUTO,
|
|
77
206
|
maxWidth: 'none',
|
|
78
|
-
minHeight:
|
|
207
|
+
minHeight: AUTO,
|
|
79
208
|
maxHeight: 'none',
|
|
209
|
+
widthKeyword: null,
|
|
210
|
+
minWidthKeyword: null,
|
|
211
|
+
minHeightKeyword: null,
|
|
212
|
+
maxWidthKeyword: null,
|
|
213
|
+
maxHeightKeyword: null,
|
|
214
|
+
heightKeyword: null,
|
|
80
215
|
marginTop: 0,
|
|
81
216
|
marginRight: 0,
|
|
82
217
|
marginBottom: 0,
|
|
@@ -85,10 +220,13 @@ export function initialStyle(look) {
|
|
|
85
220
|
paddingRight: 0,
|
|
86
221
|
paddingBottom: 0,
|
|
87
222
|
paddingLeft: 0,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
223
|
+
// `medium`, and drawn as nothing while the style is `none`: a width
|
|
224
|
+
// computes to zero there (CSS 2.1 8.5.1), which is the layout's to
|
|
225
|
+
// apply, so that `border-style: solid` alone brings a border back
|
|
226
|
+
borderTopWidth: medium,
|
|
227
|
+
borderRightWidth: medium,
|
|
228
|
+
borderBottomWidth: medium,
|
|
229
|
+
borderLeftWidth: medium,
|
|
92
230
|
borderTopStyle: 'none',
|
|
93
231
|
borderRightStyle: 'none',
|
|
94
232
|
borderBottomStyle: 'none',
|
|
@@ -100,22 +238,49 @@ export function initialStyle(look) {
|
|
|
100
238
|
borderBottomColor: 'currentColor',
|
|
101
239
|
borderLeftColor: 'currentColor',
|
|
102
240
|
borderRadius: [0, 0, 0, 0],
|
|
241
|
+
borderRadiusY: null,
|
|
242
|
+
boxShadow: null,
|
|
243
|
+
outlineStyle: 'none',
|
|
244
|
+
outlineWidth: medium,
|
|
245
|
+
outlineColor: 'currentColor',
|
|
246
|
+
outlineOffset: 0,
|
|
103
247
|
top: AUTO,
|
|
104
248
|
right: AUTO,
|
|
105
249
|
bottom: AUTO,
|
|
106
250
|
left: AUTO,
|
|
107
251
|
backgroundColor: null,
|
|
108
252
|
backgroundImage: null,
|
|
109
|
-
|
|
253
|
+
backgroundGradient: null,
|
|
254
|
+
backgroundRepeat: REPEAT,
|
|
110
255
|
backgroundSize: 'auto',
|
|
256
|
+
backgroundAttachment: 'scroll',
|
|
111
257
|
backgroundPositionX: 0,
|
|
112
258
|
backgroundPositionY: 0,
|
|
259
|
+
backgroundImages: null,
|
|
260
|
+
backgroundRepeats: null,
|
|
261
|
+
backgroundSizes: null,
|
|
262
|
+
backgroundAttachments: null,
|
|
263
|
+
backgroundPositions: null,
|
|
264
|
+
backgroundClipText: false,
|
|
265
|
+
backgroundClip: 'border-box',
|
|
266
|
+
backgroundOrigin: 'padding-box',
|
|
267
|
+
backgroundClips: null,
|
|
268
|
+
backgroundOrigins: null,
|
|
269
|
+
borderImage: NO_BORDER_IMAGE,
|
|
270
|
+
textFillColor: null,
|
|
113
271
|
textDecorationLine: 'none',
|
|
114
272
|
textDecorationColor: null,
|
|
115
273
|
textDecorationStyle: 'solid',
|
|
274
|
+
textDecorationThickness: null,
|
|
275
|
+
textUnderlineOffset: null,
|
|
276
|
+
underline: null,
|
|
277
|
+
underlineStyle: 'solid',
|
|
278
|
+
underlineThickness: null,
|
|
279
|
+
underlineOffset: null,
|
|
280
|
+
lineThrough: null,
|
|
116
281
|
flexDirection: 'row',
|
|
117
282
|
flexWrap: 'nowrap',
|
|
118
|
-
justifyContent: '
|
|
283
|
+
justifyContent: 'normal',
|
|
119
284
|
alignItems: 'stretch',
|
|
120
285
|
alignSelf: 'auto',
|
|
121
286
|
alignContent: 'stretch',
|
|
@@ -125,8 +290,54 @@ export function initialStyle(look) {
|
|
|
125
290
|
order: 0,
|
|
126
291
|
rowGap: 0,
|
|
127
292
|
columnGap: 0,
|
|
293
|
+
grid: false,
|
|
294
|
+
gridColumns: null,
|
|
295
|
+
gridRows: null,
|
|
296
|
+
gridAutoRows: AUTO_TRACK,
|
|
297
|
+
gridAutoColumns: AUTO_TRACK,
|
|
298
|
+
gridAreas: null,
|
|
299
|
+
gridAutoFlow: 'row',
|
|
300
|
+
gridColumnStart: null,
|
|
301
|
+
gridColumnEnd: null,
|
|
302
|
+
gridRowStart: null,
|
|
303
|
+
gridRowEnd: null,
|
|
304
|
+
justifyItems: 'normal',
|
|
305
|
+
justifySelf: 'auto',
|
|
128
306
|
tableLayout: 'auto',
|
|
129
|
-
|
|
307
|
+
content: 'normal',
|
|
308
|
+
counterReset: null,
|
|
309
|
+
counterIncrement: null,
|
|
310
|
+
counterSet: null,
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
let StyleCopy = null;
|
|
314
|
+
/**
|
|
315
|
+
* A copy of a style, made by a constructor that assigns every field.
|
|
316
|
+
*
|
|
317
|
+
* Never `{ ...style }`. V8 gives an object literal of 128 properties or
|
|
318
|
+
* more a dictionary map — a hash table where a shape would be — and a
|
|
319
|
+
* spread of one adds the copy's properties one at a time: 20 µs a copy at
|
|
320
|
+
* 149 fields, where a literal of 127 spreads in 0.2. A style passed 128 and
|
|
321
|
+
* every `inherit` paid it, and a spread of a style that is not a literal
|
|
322
|
+
* costs 4 µs. A constructor's fields are laid out in the object whatever
|
|
323
|
+
* their number: 0.13 µs a copy, and read as fast as a small literal's.
|
|
324
|
+
*
|
|
325
|
+
* The constructor is written from the fields of the first style copied,
|
|
326
|
+
* which is `initialStyle`'s literal and so every field there is: one added
|
|
327
|
+
* to the style cannot be left out of the copy.
|
|
328
|
+
*/
|
|
329
|
+
export function copyStyle(from) {
|
|
330
|
+
StyleCopy ??= copyConstructor(Object.keys(from));
|
|
331
|
+
return new StyleCopy(from);
|
|
332
|
+
}
|
|
333
|
+
function copyConstructor(fields) {
|
|
334
|
+
const body = fields.map((field) => {
|
|
335
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(field)) {
|
|
336
|
+
throw new Error(`a style field that is not a name: ${field}`);
|
|
337
|
+
}
|
|
338
|
+
return `this.${field} = from.${field};`;
|
|
339
|
+
});
|
|
340
|
+
return new Function('from', body.join('\n'));
|
|
130
341
|
}
|
|
131
342
|
/**
|
|
132
343
|
* A child's starting style: the inherited half of `parent`, everything else
|
|
@@ -134,17 +345,112 @@ export function initialStyle(look) {
|
|
|
134
345
|
* because it is the same object for every element in a pass.
|
|
135
346
|
*/
|
|
136
347
|
export function inherit(parent, initial) {
|
|
137
|
-
const out =
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
348
|
+
const out = copyStyle(initial);
|
|
349
|
+
// `INHERITED`, a field at a time: a loop over the names was three
|
|
350
|
+
// quarters of the cost of every element's style, a store through a
|
|
351
|
+
// computed name costing thirty times one through a written one
|
|
352
|
+
out.color = parent.color;
|
|
353
|
+
out.textFillColor = parent.textFillColor;
|
|
354
|
+
out.fontFamily = parent.fontFamily;
|
|
355
|
+
out.fontSize = parent.fontSize;
|
|
356
|
+
out.fontWeight = parent.fontWeight;
|
|
357
|
+
out.fontStyle = parent.fontStyle;
|
|
358
|
+
out.lineHeight = parent.lineHeight;
|
|
359
|
+
out.lineHeightIsLength = parent.lineHeightIsLength;
|
|
360
|
+
out.textAlign = parent.textAlign;
|
|
361
|
+
out.textAlignLast = parent.textAlignLast;
|
|
362
|
+
out.textJustify = parent.textJustify;
|
|
363
|
+
out.blockEllipsis = parent.blockEllipsis;
|
|
364
|
+
out.alignBlocks = parent.alignBlocks;
|
|
365
|
+
out.textIndent = parent.textIndent;
|
|
366
|
+
out.textTransform = parent.textTransform;
|
|
367
|
+
out.letterSpacing = parent.letterSpacing;
|
|
368
|
+
out.wordSpacing = parent.wordSpacing;
|
|
369
|
+
out.textShadow = parent.textShadow;
|
|
370
|
+
out.fontVariantNumeric = parent.fontVariantNumeric;
|
|
371
|
+
out.fontVariantCaps = parent.fontVariantCaps;
|
|
372
|
+
out.fontVariantLigatures = parent.fontVariantLigatures;
|
|
373
|
+
out.fontVariantPosition = parent.fontVariantPosition;
|
|
374
|
+
out.fontKerning = parent.fontKerning;
|
|
375
|
+
out.fontFeatureSettings = parent.fontFeatureSettings;
|
|
376
|
+
out.tabSize = parent.tabSize;
|
|
377
|
+
out.tabSizeIsLength = parent.tabSizeIsLength;
|
|
378
|
+
out.whiteSpace = parent.whiteSpace;
|
|
379
|
+
out.overflowWrap = parent.overflowWrap;
|
|
380
|
+
out.wordBreak = parent.wordBreak;
|
|
381
|
+
out.lineBreakAnywhere = parent.lineBreakAnywhere;
|
|
382
|
+
out.textWrapStyle = parent.textWrapStyle;
|
|
383
|
+
out.direction = parent.direction;
|
|
384
|
+
out.visibility = parent.visibility;
|
|
385
|
+
out.listStyleType = parent.listStyleType;
|
|
386
|
+
out.listStylePosition = parent.listStylePosition;
|
|
387
|
+
out.listStyleImage = parent.listStyleImage;
|
|
388
|
+
out.cursor = parent.cursor;
|
|
389
|
+
out.borderCollapse = parent.borderCollapse;
|
|
390
|
+
out.borderSpacing = parent.borderSpacing;
|
|
391
|
+
out.borderSpacingY = parent.borderSpacingY;
|
|
392
|
+
out.captionSide = parent.captionSide;
|
|
393
|
+
out.emptyCells = parent.emptyCells;
|
|
394
|
+
out.tableTextAlignSet = parent.tableTextAlignSet;
|
|
395
|
+
out.quotes = parent.quotes;
|
|
396
|
+
out.custom = parent.custom;
|
|
144
397
|
out.borderTopColor = 'currentColor';
|
|
145
398
|
out.borderRightColor = 'currentColor';
|
|
146
399
|
out.borderBottomColor = 'currentColor';
|
|
147
400
|
out.borderLeftColor = 'currentColor';
|
|
401
|
+
out.textUnderlineOffset = parent.textUnderlineOffset;
|
|
402
|
+
// propagated, not inherited: `decorate` drops them where CSS stops them
|
|
403
|
+
out.underline = parent.underline;
|
|
404
|
+
out.underlineStyle = parent.underlineStyle;
|
|
405
|
+
out.underlineThickness = parent.underlineThickness;
|
|
406
|
+
out.underlineOffset = parent.underlineOffset;
|
|
407
|
+
out.lineThrough = parent.lineThrough;
|
|
408
|
+
return out;
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* The properties that apply to `::first-line` and inherit (CSS Pseudo 4,
|
|
412
|
+
* 2.1.1): its fonts, colour, spacing, transform and decorations. Only
|
|
413
|
+
* these are inherited through it by what is on the line; the rest are the
|
|
414
|
+
* parent's own (2.1.2).
|
|
415
|
+
*/
|
|
416
|
+
export const FIRST_LINE_INHERITED = [
|
|
417
|
+
'color',
|
|
418
|
+
'textFillColor',
|
|
419
|
+
'fontFamily',
|
|
420
|
+
'fontSize',
|
|
421
|
+
'fontWeight',
|
|
422
|
+
'fontStyle',
|
|
423
|
+
'lineHeight',
|
|
424
|
+
'lineHeightIsLength',
|
|
425
|
+
'textTransform',
|
|
426
|
+
'textUnderlineOffset',
|
|
427
|
+
'letterSpacing',
|
|
428
|
+
'wordSpacing',
|
|
429
|
+
'textShadow',
|
|
430
|
+
'fontVariantNumeric',
|
|
431
|
+
'fontVariantCaps',
|
|
432
|
+
'fontVariantLigatures',
|
|
433
|
+
'fontVariantPosition',
|
|
434
|
+
'fontKerning',
|
|
435
|
+
'fontFeatureSettings',
|
|
436
|
+
'underline',
|
|
437
|
+
'underlineStyle',
|
|
438
|
+
'underlineThickness',
|
|
439
|
+
'underlineOffset',
|
|
440
|
+
'lineThrough',
|
|
441
|
+
];
|
|
442
|
+
/**
|
|
443
|
+
* What a box on its block's first line inherits from: its parent's style,
|
|
444
|
+
* with the properties `::first-line` takes from the parent's style on that
|
|
445
|
+
* line (CSS Pseudo 4, 2.1.2) — the pseudo-element's own, for the block's
|
|
446
|
+
* children.
|
|
447
|
+
*/
|
|
448
|
+
export function firstLineParent(parent, onLine) {
|
|
449
|
+
const out = copyStyle(parent);
|
|
450
|
+
const to = out;
|
|
451
|
+
const from = onLine;
|
|
452
|
+
for (const name of FIRST_LINE_INHERITED)
|
|
453
|
+
to[name] = from[name];
|
|
148
454
|
return out;
|
|
149
455
|
}
|
|
150
456
|
// --- applying a declaration -------------------------------------------------
|
|
@@ -157,11 +463,19 @@ const COLOR_PROPS = {
|
|
|
157
463
|
'border-bottom-color': 'borderBottomColor',
|
|
158
464
|
'border-left-color': 'borderLeftColor',
|
|
159
465
|
'text-decoration-color': 'textDecorationColor',
|
|
466
|
+
'-webkit-text-fill-color': 'textFillColor',
|
|
160
467
|
};
|
|
161
468
|
const SIDE_PROPS = {
|
|
162
469
|
margin: ['marginTop', 'marginRight', 'marginBottom', 'marginLeft'],
|
|
163
470
|
padding: ['paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft'],
|
|
471
|
+
inset: ['top', 'right', 'bottom', 'left'],
|
|
164
472
|
};
|
|
473
|
+
/** An `outline-style`: a border style but `hidden`, or `auto`. */
|
|
474
|
+
function outlineStyleOf(v) {
|
|
475
|
+
if (v === 'auto')
|
|
476
|
+
return 'auto';
|
|
477
|
+
return BORDER_STYLES.has(v) && v !== 'hidden' ? v : null;
|
|
478
|
+
}
|
|
165
479
|
const BORDER_STYLES = new Set([
|
|
166
480
|
'none',
|
|
167
481
|
'hidden',
|
|
@@ -174,6 +488,8 @@ const BORDER_STYLES = new Set([
|
|
|
174
488
|
'inset',
|
|
175
489
|
'outset',
|
|
176
490
|
]);
|
|
491
|
+
/** The largest font size, in CSS pixels (`font-size`). */
|
|
492
|
+
const MAX_FONT_SIZE = 10000;
|
|
177
493
|
const BORDER_WIDTH_KEYWORDS = {
|
|
178
494
|
thin: 1,
|
|
179
495
|
medium: 3,
|
|
@@ -192,6 +508,13 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
192
508
|
let value = rawValue.trim();
|
|
193
509
|
if (!value)
|
|
194
510
|
return;
|
|
511
|
+
// a logical property is the physical one it stands for, the CSS-wide
|
|
512
|
+
// keywords and a stray `!important` included, so it is read first
|
|
513
|
+
const logical = LOGICAL[name];
|
|
514
|
+
if (logical) {
|
|
515
|
+
applyLogical(style, parent, logical, value.replace(/\s*!\s*important$/i, ''), ctx);
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
195
518
|
// CSS-wide keywords, before anything else parses the value.
|
|
196
519
|
const lower = value.toLowerCase();
|
|
197
520
|
if (lower === 'inherit') {
|
|
@@ -199,11 +522,18 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
199
522
|
return;
|
|
200
523
|
}
|
|
201
524
|
if (lower === 'initial' || lower === 'unset' || lower === 'revert') {
|
|
202
|
-
// `unset` is `inherit` for an inherited property and `initial` otherwise
|
|
203
|
-
// `
|
|
204
|
-
//
|
|
525
|
+
// `unset` is `inherit` for an inherited property and `initial` otherwise.
|
|
526
|
+
// `initial` is the value the property starts at, which an inherited one
|
|
527
|
+
// does not keep: left as it was, `line-height: initial` in a box inside
|
|
528
|
+
// a 200px line height was that line height still, and one after an
|
|
529
|
+
// earlier declaration of the property was that declaration. `revert`
|
|
530
|
+
// would be the UA sheet's value, and leaving the one the cascade has
|
|
531
|
+
// reached is the closest this gets without a third cascade level.
|
|
205
532
|
if (lower === 'unset' && isInherited(name))
|
|
206
533
|
inheritOne(style, parent, name);
|
|
534
|
+
else if (lower !== 'revert' && ctx.initial) {
|
|
535
|
+
inheritOne(style, ctx.initial, name);
|
|
536
|
+
}
|
|
207
537
|
return;
|
|
208
538
|
}
|
|
209
539
|
// `!important` is stripped by the parser; a stray one here is an author
|
|
@@ -216,10 +546,12 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
216
546
|
const parsed = parseColor(value);
|
|
217
547
|
if (parsed === null)
|
|
218
548
|
return;
|
|
219
|
-
// `
|
|
220
|
-
// means
|
|
221
|
-
if (name === 'color'
|
|
549
|
+
// `currentColor` in `color` is the one place the token refers to itself:
|
|
550
|
+
// it means the inherited colour, and so does one in a `color-mix()`
|
|
551
|
+
if (name === 'color') {
|
|
552
|
+
style.color = inkColor(parsed, parent.color);
|
|
222
553
|
return;
|
|
554
|
+
}
|
|
223
555
|
style[color] = parsed;
|
|
224
556
|
return;
|
|
225
557
|
}
|
|
@@ -229,12 +561,89 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
229
561
|
const v = value.toLowerCase();
|
|
230
562
|
if (v === 'inline-block' || v === 'inline-flex')
|
|
231
563
|
style.display = v;
|
|
564
|
+
// a grid container is a flex container to the box tree — its
|
|
565
|
+
// children are blockified items, and it holds its floats — and its
|
|
566
|
+
// own layout (`css-grid.ts`) to the engine
|
|
232
567
|
else if (v === 'grid')
|
|
233
|
-
style.display = '
|
|
568
|
+
style.display = 'flex';
|
|
234
569
|
else if (v === 'inline-grid')
|
|
235
|
-
style.display = 'inline-
|
|
570
|
+
style.display = 'inline-flex';
|
|
571
|
+
else if (v === 'flow-root')
|
|
572
|
+
style.display = 'block';
|
|
573
|
+
// the legacy flexible box, a flex box until `settleClamp` has read
|
|
574
|
+
// its orient, and the prefixed spelling of the modern one
|
|
575
|
+
else if (v === '-webkit-box' || v === '-webkit-flex') {
|
|
576
|
+
style.display = 'flex';
|
|
577
|
+
}
|
|
578
|
+
else if (v === '-webkit-inline-box' || v === '-webkit-inline-flex') {
|
|
579
|
+
style.display = 'inline-flex';
|
|
580
|
+
}
|
|
236
581
|
else if (DISPLAYS.has(v))
|
|
237
582
|
style.display = v;
|
|
583
|
+
else
|
|
584
|
+
return;
|
|
585
|
+
style.flowRoot = v === 'flow-root';
|
|
586
|
+
style.grid = v === 'grid' || v === 'inline-grid';
|
|
587
|
+
style.webkitBox =
|
|
588
|
+
v === '-webkit-box'
|
|
589
|
+
? 'block'
|
|
590
|
+
: v === '-webkit-inline-box'
|
|
591
|
+
? 'inline'
|
|
592
|
+
: null;
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
case '-webkit-box-orient': {
|
|
596
|
+
const v = value.toLowerCase();
|
|
597
|
+
if (v === 'horizontal' || v === 'inline-axis') {
|
|
598
|
+
style.webkitBoxOrient = 'horizontal';
|
|
599
|
+
}
|
|
600
|
+
else if (v === 'vertical' || v === 'block-axis') {
|
|
601
|
+
style.webkitBoxOrient = 'vertical';
|
|
602
|
+
}
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
case '-webkit-box-direction': {
|
|
606
|
+
const v = value.toLowerCase();
|
|
607
|
+
if (v === 'normal' || v === 'reverse')
|
|
608
|
+
style.webkitBoxDirection = v;
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
case '-webkit-box-pack': {
|
|
612
|
+
const v = BOX_PACK.get(value.trim().toLowerCase());
|
|
613
|
+
if (v)
|
|
614
|
+
style.webkitBoxPack = v;
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
case '-webkit-box-align': {
|
|
618
|
+
const v = BOX_ALIGN.get(value.trim().toLowerCase());
|
|
619
|
+
if (v)
|
|
620
|
+
style.webkitBoxAlign = v;
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
case 'column-count':
|
|
624
|
+
case 'column-width':
|
|
625
|
+
case 'columns': {
|
|
626
|
+
// `auto`, or a count, a width or both of them
|
|
627
|
+
const bit = name === 'column-count' ? 1 : name === 'column-width' ? 2 : 3;
|
|
628
|
+
let set = 0;
|
|
629
|
+
for (const part of splitValue(value)) {
|
|
630
|
+
const v = part.toLowerCase();
|
|
631
|
+
if (v === 'auto')
|
|
632
|
+
continue;
|
|
633
|
+
if (/^\d+$/.test(v) && Number(v) >= 1)
|
|
634
|
+
set |= 1;
|
|
635
|
+
else if (parseLength(part, ctx) !== null)
|
|
636
|
+
set |= 2;
|
|
637
|
+
else
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
style.columns = (style.columns & ~bit) | (set & bit);
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
case '-webkit-box-flex': {
|
|
644
|
+
const n = parseNumber(value);
|
|
645
|
+
if (n !== null && n >= 0)
|
|
646
|
+
style.webkitBoxFlex = n;
|
|
238
647
|
return;
|
|
239
648
|
}
|
|
240
649
|
case 'position': {
|
|
@@ -264,6 +673,100 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
264
673
|
const v = value.toLowerCase();
|
|
265
674
|
if (v === 'left' || v === 'right' || v === 'both' || v === 'none')
|
|
266
675
|
style.clear = v;
|
|
676
|
+
else if (v === 'inline-start')
|
|
677
|
+
style.clear = style.direction === 'rtl' ? 'right' : 'left';
|
|
678
|
+
else if (v === 'inline-end')
|
|
679
|
+
style.clear = style.direction === 'rtl' ? 'left' : 'right';
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
case 'line-clamp': {
|
|
683
|
+
// `none`, or a count of lines, an ellipsis or both, and
|
|
684
|
+
// `-webkit-legacy` (CSS Overflow 4, 5.1): the ellipsis a string
|
|
685
|
+
// names is drawn as the one `auto` draws
|
|
686
|
+
const parts = splitValue(value);
|
|
687
|
+
if (parts.length === 1 && parts[0].toLowerCase() === 'none') {
|
|
688
|
+
style.maxLines = null;
|
|
689
|
+
style.clampContinue = 'auto';
|
|
690
|
+
style.blockEllipsis = false;
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
let lines = null;
|
|
694
|
+
let ellipsis = null;
|
|
695
|
+
let legacy = false;
|
|
696
|
+
for (const part of parts) {
|
|
697
|
+
const v = part.toLowerCase();
|
|
698
|
+
if (/^\d+$/.test(v) && lines === null)
|
|
699
|
+
lines = Number(v);
|
|
700
|
+
else if (v === '-webkit-legacy' && !legacy)
|
|
701
|
+
legacy = true;
|
|
702
|
+
else if (ellipsis === null && (v === 'auto' || v === 'no-ellipsis')) {
|
|
703
|
+
ellipsis = v === 'auto';
|
|
704
|
+
}
|
|
705
|
+
else if (ellipsis === null && /^(["']).*\1$/s.test(part)) {
|
|
706
|
+
ellipsis = part.length > 2;
|
|
707
|
+
}
|
|
708
|
+
else
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
if (lines === 0 || (lines === null && ellipsis === null))
|
|
712
|
+
return;
|
|
713
|
+
style.maxLines = lines;
|
|
714
|
+
style.clampContinue = legacy ? 'legacy' : 'collapse';
|
|
715
|
+
style.blockEllipsis = ellipsis ?? true;
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
case '-webkit-line-clamp': {
|
|
719
|
+
// `line-clamp` as it was, which clamps only a vertical `-webkit-box`
|
|
720
|
+
// (5.1.1) and always with an ellipsis
|
|
721
|
+
const v = value.trim().toLowerCase();
|
|
722
|
+
if (v === 'none') {
|
|
723
|
+
style.maxLines = null;
|
|
724
|
+
style.clampContinue = 'auto';
|
|
725
|
+
style.blockEllipsis = true;
|
|
726
|
+
}
|
|
727
|
+
else if (/^\d+$/.test(v) && Number(v) >= 1) {
|
|
728
|
+
style.maxLines = Number(v);
|
|
729
|
+
style.clampContinue = 'legacy';
|
|
730
|
+
style.blockEllipsis = true;
|
|
731
|
+
}
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
case 'max-lines': {
|
|
735
|
+
const v = value.trim().toLowerCase();
|
|
736
|
+
if (v === 'none')
|
|
737
|
+
style.maxLines = null;
|
|
738
|
+
else if (/^\d+$/.test(v) && Number(v) >= 1)
|
|
739
|
+
style.maxLines = Number(v);
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
case 'continue': {
|
|
743
|
+
const v = value.trim().toLowerCase();
|
|
744
|
+
if (v === 'auto')
|
|
745
|
+
style.clampContinue = 'auto';
|
|
746
|
+
else if (v === 'collapse' || v === 'discard') {
|
|
747
|
+
style.clampContinue = 'collapse';
|
|
748
|
+
}
|
|
749
|
+
else if (v === '-webkit-legacy')
|
|
750
|
+
style.clampContinue = 'legacy';
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
case 'block-ellipsis': {
|
|
754
|
+
const v = value.trim();
|
|
755
|
+
const lower = v.toLowerCase();
|
|
756
|
+
if (lower === 'auto' || lower === 'no-ellipsis') {
|
|
757
|
+
style.blockEllipsis = lower === 'auto';
|
|
758
|
+
}
|
|
759
|
+
else if (/^(["']).*\1$/s.test(v))
|
|
760
|
+
style.blockEllipsis = v.length > 2;
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
case 'text-overflow': {
|
|
764
|
+
// one value for the end a line is cut at, or two, start and end
|
|
765
|
+
const parts = splitValue(value).map((p) => p.toLowerCase());
|
|
766
|
+
const end = parts[parts.length - 1];
|
|
767
|
+
if (parts.length <= 2 && (end === 'clip' || end === 'ellipsis')) {
|
|
768
|
+
style.textOverflow = end;
|
|
769
|
+
}
|
|
267
770
|
return;
|
|
268
771
|
}
|
|
269
772
|
case 'box-sizing': {
|
|
@@ -272,6 +775,111 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
272
775
|
style.boxSizing = v;
|
|
273
776
|
return;
|
|
274
777
|
}
|
|
778
|
+
case 'aspect-ratio': {
|
|
779
|
+
const parsed = parseAspectRatio(value);
|
|
780
|
+
if (parsed !== undefined)
|
|
781
|
+
style.aspectRatio = parsed;
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
case 'object-fit': {
|
|
785
|
+
const v = value.trim().toLowerCase();
|
|
786
|
+
if (OBJECT_FITS.has(v))
|
|
787
|
+
style.objectFit = v;
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
case 'appearance':
|
|
791
|
+
case '-webkit-appearance':
|
|
792
|
+
case '-moz-appearance': {
|
|
793
|
+
// every keyword but `none` is some control's own look, which is the
|
|
794
|
+
// only other thing this engine has
|
|
795
|
+
const v = value.trim().toLowerCase();
|
|
796
|
+
if (/^[a-z-]+$/.test(v))
|
|
797
|
+
style.appearance = v === 'none' ? 'none' : 'auto';
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
case 'contain': {
|
|
801
|
+
const bits = containOf(value);
|
|
802
|
+
if (bits !== null)
|
|
803
|
+
style.contain = bits;
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
case 'overflow-clip-margin': {
|
|
807
|
+
let box = null;
|
|
808
|
+
let margin = null;
|
|
809
|
+
// a length that shrinks the box where it is negative (CSS Overflow 4)
|
|
810
|
+
for (const part of splitValue(value)) {
|
|
811
|
+
const word = part.toLowerCase();
|
|
812
|
+
if (box === null && isVisualBox(word)) {
|
|
813
|
+
box = word;
|
|
814
|
+
continue;
|
|
815
|
+
}
|
|
816
|
+
const len = parseLength(part, ctx);
|
|
817
|
+
if (margin !== null || typeof len !== 'number')
|
|
818
|
+
return;
|
|
819
|
+
margin = len;
|
|
820
|
+
}
|
|
821
|
+
if (box === null && margin === null)
|
|
822
|
+
return;
|
|
823
|
+
style.overflowClipBox = box ?? 'padding-box';
|
|
824
|
+
style.overflowClipMargin = margin ?? 0;
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
case 'content-visibility': {
|
|
828
|
+
const v = value.trim().toLowerCase();
|
|
829
|
+
if (v === 'visible' || v === 'auto' || v === 'hidden') {
|
|
830
|
+
style.contentVisibility = v;
|
|
831
|
+
}
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
case 'contain-intrinsic-size':
|
|
835
|
+
case 'contain-intrinsic-width':
|
|
836
|
+
case 'contain-intrinsic-height':
|
|
837
|
+
case 'contain-intrinsic-inline-size':
|
|
838
|
+
case 'contain-intrinsic-block-size': {
|
|
839
|
+
const parts = splitValue(value);
|
|
840
|
+
const sizes = [];
|
|
841
|
+
for (let i = 0; i < parts.length; i += 1) {
|
|
842
|
+
const word = parts[i].toLowerCase();
|
|
843
|
+
// `auto` beside a length remembers a size last laid out, which a
|
|
844
|
+
// document that does not change has not got: the length
|
|
845
|
+
if (word === 'auto' && i + 1 < parts.length)
|
|
846
|
+
continue;
|
|
847
|
+
if (word === 'none') {
|
|
848
|
+
sizes.push(null);
|
|
849
|
+
continue;
|
|
850
|
+
}
|
|
851
|
+
const len = parseLength(parts[i], ctx);
|
|
852
|
+
if (typeof len !== 'number' || len < 0)
|
|
853
|
+
return;
|
|
854
|
+
sizes.push(len);
|
|
855
|
+
}
|
|
856
|
+
if (!sizes.length || sizes.length > 2)
|
|
857
|
+
return;
|
|
858
|
+
const across = name === 'contain-intrinsic-size' ||
|
|
859
|
+
name === 'contain-intrinsic-width' ||
|
|
860
|
+
name === 'contain-intrinsic-inline-size';
|
|
861
|
+
const down = name !== 'contain-intrinsic-width' &&
|
|
862
|
+
name !== 'contain-intrinsic-inline-size';
|
|
863
|
+
if (name === 'contain-intrinsic-size') {
|
|
864
|
+
style.containIntrinsicWidth = sizes[0];
|
|
865
|
+
style.containIntrinsicHeight = sizes.length > 1 ? sizes[1] : sizes[0];
|
|
866
|
+
}
|
|
867
|
+
else if (sizes.length === 1) {
|
|
868
|
+
if (across)
|
|
869
|
+
style.containIntrinsicWidth = sizes[0];
|
|
870
|
+
else if (down)
|
|
871
|
+
style.containIntrinsicHeight = sizes[0];
|
|
872
|
+
}
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
case 'object-position': {
|
|
876
|
+
const pair = positionPair(splitValue(value), ctx);
|
|
877
|
+
if (!pair)
|
|
878
|
+
return;
|
|
879
|
+
style.objectPositionX = pair[0];
|
|
880
|
+
style.objectPositionY = pair[1];
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
275
883
|
case 'overflow':
|
|
276
884
|
case 'overflow-x':
|
|
277
885
|
case 'overflow-y': {
|
|
@@ -284,27 +892,60 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
284
892
|
style.overflowY = y;
|
|
285
893
|
return;
|
|
286
894
|
}
|
|
895
|
+
case 'clip': {
|
|
896
|
+
const clip = parseClip(value, ctx);
|
|
897
|
+
if (clip !== undefined)
|
|
898
|
+
style.clip = clip;
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
287
901
|
case 'opacity': {
|
|
288
902
|
const a = parseAlpha(value);
|
|
289
903
|
if (a !== null)
|
|
290
904
|
style.opacity = a;
|
|
291
905
|
return;
|
|
292
906
|
}
|
|
907
|
+
case 'translate': {
|
|
908
|
+
// `none`, or across, down, and a depth nothing here has
|
|
909
|
+
if (value.trim().toLowerCase() === 'none') {
|
|
910
|
+
style.translate = null;
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
const parts = splitValue(value);
|
|
914
|
+
if (parts.length > 3)
|
|
915
|
+
return;
|
|
916
|
+
const [x, y, z] = parts.map((p) => parseLength(p, ctx));
|
|
917
|
+
if (x == null || x === AUTO || y === null || y === AUTO)
|
|
918
|
+
return;
|
|
919
|
+
if (z !== undefined && typeof z !== 'number')
|
|
920
|
+
return;
|
|
921
|
+
style.translate = [x, y ?? 0];
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
case 'transform': {
|
|
925
|
+
const moved = transformTranslation(value, ctx);
|
|
926
|
+
if (moved !== undefined)
|
|
927
|
+
style.transformTranslate = moved;
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
293
930
|
case 'visibility': {
|
|
294
931
|
const v = value.toLowerCase();
|
|
295
|
-
if (v === 'hidden' || v === 'collapse')
|
|
296
|
-
style.visibility =
|
|
297
|
-
|
|
298
|
-
style.visibility = 'visible';
|
|
932
|
+
if (v === 'hidden' || v === 'collapse' || v === 'visible') {
|
|
933
|
+
style.visibility = v;
|
|
934
|
+
}
|
|
299
935
|
return;
|
|
300
936
|
}
|
|
301
937
|
case 'z-index': {
|
|
302
|
-
|
|
938
|
+
// an integer: `1.5` is none, and a `calc()` is rounded to one, half
|
|
939
|
+
// up (CSS Values 4 10.9)
|
|
940
|
+
const v = value.trim().toLowerCase();
|
|
941
|
+
if (v === 'auto')
|
|
303
942
|
style.zIndex = AUTO;
|
|
304
|
-
else
|
|
305
|
-
|
|
943
|
+
else if (/^[+-]?\d+$/.test(v))
|
|
944
|
+
style.zIndex = Number(v);
|
|
945
|
+
else if (/^[a-z-]+\(/.test(v)) {
|
|
946
|
+
const n = parseNumber(v);
|
|
306
947
|
if (n !== null)
|
|
307
|
-
style.zIndex = Math.
|
|
948
|
+
style.zIndex = Math.floor(n + 0.5);
|
|
308
949
|
}
|
|
309
950
|
return;
|
|
310
951
|
}
|
|
@@ -315,12 +956,16 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
315
956
|
v === 'middle' ||
|
|
316
957
|
v === 'bottom' ||
|
|
317
958
|
v === 'sub' ||
|
|
318
|
-
v === 'super'
|
|
959
|
+
v === 'super' ||
|
|
960
|
+
v === 'text-top' ||
|
|
961
|
+
v === 'text-bottom') {
|
|
319
962
|
style.verticalAlign = v;
|
|
320
963
|
}
|
|
321
964
|
else {
|
|
965
|
+
// a percentage is of the element's own line height, which layout
|
|
966
|
+
// knows
|
|
322
967
|
const len = parseLength(value, ctx);
|
|
323
|
-
if (
|
|
968
|
+
if (len !== null && len !== AUTO)
|
|
324
969
|
style.verticalAlign = len;
|
|
325
970
|
}
|
|
326
971
|
return;
|
|
@@ -330,20 +975,55 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
330
975
|
case 'height':
|
|
331
976
|
case 'min-width':
|
|
332
977
|
case 'min-height': {
|
|
978
|
+
const keyword = contentSizeOf(value, ctx, name.endsWith('width'));
|
|
979
|
+
if (keyword) {
|
|
980
|
+
// `auto` beside it, which is a height's content height already
|
|
981
|
+
style[camel(name)] = AUTO;
|
|
982
|
+
if (name === 'width')
|
|
983
|
+
style.widthKeyword = keyword;
|
|
984
|
+
else if (name === 'min-width')
|
|
985
|
+
style.minWidthKeyword = keyword;
|
|
986
|
+
else if (name === 'min-height')
|
|
987
|
+
style.minHeightKeyword = keyword;
|
|
988
|
+
else
|
|
989
|
+
style.heightKeyword = keyword;
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
// a negative size is no value, and the declaration goes (CSS 2.1
|
|
993
|
+
// 10.2, 10.4, 10.5, 10.7)
|
|
333
994
|
const len = parseLength(value, ctx);
|
|
334
|
-
if (len !== null)
|
|
995
|
+
if (len !== null && notNegative(len)) {
|
|
335
996
|
style[camel(name)] = len;
|
|
997
|
+
if (name === 'width')
|
|
998
|
+
style.widthKeyword = null;
|
|
999
|
+
else if (name === 'min-width')
|
|
1000
|
+
style.minWidthKeyword = null;
|
|
1001
|
+
else if (name === 'min-height')
|
|
1002
|
+
style.minHeightKeyword = null;
|
|
1003
|
+
else
|
|
1004
|
+
style.heightKeyword = null;
|
|
1005
|
+
}
|
|
336
1006
|
return;
|
|
337
1007
|
}
|
|
338
1008
|
case 'max-width':
|
|
339
1009
|
case 'max-height': {
|
|
340
|
-
|
|
1010
|
+
const keyword = contentSizeOf(value, ctx, name === 'max-width');
|
|
1011
|
+
if (keyword || value.toLowerCase() === 'none') {
|
|
341
1012
|
style[camel(name)] = 'none';
|
|
1013
|
+
if (name === 'max-width')
|
|
1014
|
+
style.maxWidthKeyword = keyword;
|
|
1015
|
+
else
|
|
1016
|
+
style.maxHeightKeyword = keyword;
|
|
342
1017
|
return;
|
|
343
1018
|
}
|
|
344
1019
|
const len = parseLength(value, ctx);
|
|
345
|
-
if (len !== null)
|
|
1020
|
+
if (len !== null && notNegative(len)) {
|
|
346
1021
|
style[camel(name)] = len;
|
|
1022
|
+
if (name === 'max-width')
|
|
1023
|
+
style.maxWidthKeyword = null;
|
|
1024
|
+
else
|
|
1025
|
+
style.maxHeightKeyword = null;
|
|
1026
|
+
}
|
|
347
1027
|
return;
|
|
348
1028
|
}
|
|
349
1029
|
case 'top':
|
|
@@ -355,18 +1035,20 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
355
1035
|
style[name] = len;
|
|
356
1036
|
return;
|
|
357
1037
|
}
|
|
1038
|
+
case 'inset':
|
|
358
1039
|
case 'margin':
|
|
359
1040
|
case 'padding': {
|
|
360
1041
|
const keys = SIDE_PROPS[name];
|
|
361
1042
|
const parts = splitValue(value).map((p) => parseLength(p, ctx));
|
|
362
1043
|
if (parts.some((p) => p === null))
|
|
363
1044
|
return;
|
|
1045
|
+
// padding is never `auto` and never negative: a value that is either
|
|
1046
|
+
// is no value, and the declaration goes (CSS 2.1 8.4)
|
|
1047
|
+
if (name === 'padding' && !parts.every(validPadding))
|
|
1048
|
+
return;
|
|
364
1049
|
const sides = fourSides(parts);
|
|
365
|
-
// `padding: auto` is not a thing; a stray one computes to zero rather
|
|
366
|
-
// than making layout branch on an impossible value.
|
|
367
1050
|
for (let i = 0; i < 4; i += 1) {
|
|
368
|
-
|
|
369
|
-
style[keys[i]] = v;
|
|
1051
|
+
style[keys[i]] = sides[i];
|
|
370
1052
|
}
|
|
371
1053
|
return;
|
|
372
1054
|
}
|
|
@@ -381,9 +1063,9 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
381
1063
|
const len = parseLength(value, ctx);
|
|
382
1064
|
if (len === null)
|
|
383
1065
|
return;
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
1066
|
+
if (name.startsWith('padding') && !validPadding(len))
|
|
1067
|
+
return;
|
|
1068
|
+
style[camel(name)] = len;
|
|
387
1069
|
return;
|
|
388
1070
|
}
|
|
389
1071
|
// --- borders ------------------------------------------------------------
|
|
@@ -446,21 +1128,104 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
446
1128
|
style[camel(name)] = v;
|
|
447
1129
|
return;
|
|
448
1130
|
}
|
|
1131
|
+
case 'border-top-left-radius':
|
|
1132
|
+
case 'border-top-right-radius':
|
|
1133
|
+
case 'border-bottom-right-radius':
|
|
1134
|
+
case 'border-bottom-left-radius': {
|
|
1135
|
+
// a radius, or a horizontal one and a vertical one
|
|
1136
|
+
const parts = splitValue(value).map((p) => radiusOf(p, ctx));
|
|
1137
|
+
if (!parts.length || parts.length > 2 || parts.includes(null))
|
|
1138
|
+
return;
|
|
1139
|
+
const [x, y = x] = parts;
|
|
1140
|
+
const corner = CORNERS.indexOf(name);
|
|
1141
|
+
const radii = style.borderRadius.slice();
|
|
1142
|
+
const vertical = (style.borderRadiusY ?? style.borderRadius).slice();
|
|
1143
|
+
radii[corner] = x;
|
|
1144
|
+
vertical[corner] = y;
|
|
1145
|
+
style.borderRadius = radii;
|
|
1146
|
+
style.borderRadiusY = sameRadii(radii, vertical) ? null : vertical;
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
case 'outline': {
|
|
1150
|
+
// width, style and colour in any order, each reset where it is left
|
|
1151
|
+
// out, as `border`'s are
|
|
1152
|
+
let width = BORDER_WIDTH_KEYWORDS.medium * ctx.scale;
|
|
1153
|
+
let outline = 'none';
|
|
1154
|
+
let color = 'currentColor';
|
|
1155
|
+
for (const part of splitValue(value)) {
|
|
1156
|
+
const v = part.toLowerCase();
|
|
1157
|
+
const kind = outlineStyleOf(v);
|
|
1158
|
+
if (kind) {
|
|
1159
|
+
outline = kind;
|
|
1160
|
+
continue;
|
|
1161
|
+
}
|
|
1162
|
+
const w = borderWidth(part, ctx);
|
|
1163
|
+
if (w !== null) {
|
|
1164
|
+
width = w;
|
|
1165
|
+
continue;
|
|
1166
|
+
}
|
|
1167
|
+
const c = v === 'invert' ? 'currentColor' : parseColor(part);
|
|
1168
|
+
if (c === null)
|
|
1169
|
+
return;
|
|
1170
|
+
color = c;
|
|
1171
|
+
}
|
|
1172
|
+
style.outlineStyle = outline;
|
|
1173
|
+
style.outlineWidth = width;
|
|
1174
|
+
style.outlineColor = color;
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
case 'outline-style': {
|
|
1178
|
+
const kind = outlineStyleOf(value.trim().toLowerCase());
|
|
1179
|
+
if (kind)
|
|
1180
|
+
style.outlineStyle = kind;
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
case 'outline-width': {
|
|
1184
|
+
const w = borderWidth(value, ctx);
|
|
1185
|
+
if (w !== null)
|
|
1186
|
+
style.outlineWidth = w;
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
case 'outline-color': {
|
|
1190
|
+
// `invert` is the text's colour where a browser cannot invert
|
|
1191
|
+
const v = value.trim().toLowerCase();
|
|
1192
|
+
const c = v === 'invert' ? 'currentColor' : parseColor(value);
|
|
1193
|
+
if (c !== null)
|
|
1194
|
+
style.outlineColor = c;
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1197
|
+
case 'outline-offset': {
|
|
1198
|
+
const len = parseLength(value, ctx);
|
|
1199
|
+
if (typeof len === 'number')
|
|
1200
|
+
style.outlineOffset = len;
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
1203
|
+
case 'box-shadow': {
|
|
1204
|
+
const shadows = parseBoxShadow(value, ctx);
|
|
1205
|
+
if (shadows !== undefined)
|
|
1206
|
+
style.boxShadow = shadows;
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
case 'text-shadow': {
|
|
1210
|
+
const shadows = parseBoxShadow(value, ctx, true);
|
|
1211
|
+
if (shadows !== undefined)
|
|
1212
|
+
style.textShadow = shadows;
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
449
1215
|
case 'border-radius': {
|
|
450
|
-
//
|
|
451
|
-
//
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
const len = parseLength(p, ctx);
|
|
455
|
-
return typeof len === 'number'
|
|
456
|
-
? len
|
|
457
|
-
: len && typeof len === 'object'
|
|
458
|
-
? 0
|
|
459
|
-
: null;
|
|
460
|
-
});
|
|
461
|
-
if (parts.some((p) => p === null))
|
|
1216
|
+
// one to four radii, and after a `/` one to four more for the
|
|
1217
|
+
// vertical radii where they differ: elliptical corners
|
|
1218
|
+
const halves = value.split('/');
|
|
1219
|
+
if (halves.length > 2)
|
|
462
1220
|
return;
|
|
463
|
-
|
|
1221
|
+
const [horizontal, vertical] = halves.map((half) => splitValue(half).map((p) => radiusOf(p, ctx)));
|
|
1222
|
+
for (const parts of vertical ? [horizontal, vertical] : [horizontal]) {
|
|
1223
|
+
if (!parts.length || parts.length > 4 || parts.includes(null))
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
style.borderRadius = fourSides(horizontal);
|
|
1227
|
+
const y = vertical ? fourSides(vertical) : null;
|
|
1228
|
+
style.borderRadiusY = y && !sameRadii(style.borderRadius, y) ? y : null;
|
|
464
1229
|
return;
|
|
465
1230
|
}
|
|
466
1231
|
// --- background ---------------------------------------------------------
|
|
@@ -469,33 +1234,152 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
469
1234
|
return;
|
|
470
1235
|
}
|
|
471
1236
|
case 'background-image': {
|
|
472
|
-
|
|
1237
|
+
// one a layer; a bad url makes the declaration invalid, and it is
|
|
1238
|
+
// dropped
|
|
1239
|
+
const images = [];
|
|
1240
|
+
for (const part of splitCommas(value)) {
|
|
1241
|
+
const image = backgroundImageOf(part, ctx);
|
|
1242
|
+
if (image === undefined)
|
|
1243
|
+
return;
|
|
1244
|
+
images.push(image);
|
|
1245
|
+
}
|
|
1246
|
+
if (!images.length)
|
|
1247
|
+
return;
|
|
1248
|
+
const [first] = images;
|
|
1249
|
+
style.backgroundImage = typeof first === 'string' ? first : null;
|
|
1250
|
+
style.backgroundGradient = typeof first === 'string' ? null : first;
|
|
1251
|
+
style.backgroundImages = images.length > 1 ? images : null;
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
case 'background-clip':
|
|
1255
|
+
case '-webkit-background-clip': {
|
|
1256
|
+
// `text` the first layer's alone, which paints through the text
|
|
1257
|
+
const clips = layerValues(value, (part) => {
|
|
1258
|
+
const v = part.toLowerCase();
|
|
1259
|
+
return v === 'text' || isVisualBox(v) ? v : null;
|
|
1260
|
+
});
|
|
1261
|
+
if (!clips)
|
|
1262
|
+
return;
|
|
1263
|
+
style.backgroundClipText = clips[0] === 'text';
|
|
1264
|
+
const boxes = clips.map((c) => (c === 'text' ? 'border-box' : c));
|
|
1265
|
+
style.backgroundClip = boxes[0];
|
|
1266
|
+
style.backgroundClips = boxes.length > 1 ? boxes : null;
|
|
1267
|
+
return;
|
|
1268
|
+
}
|
|
1269
|
+
case 'border-image': {
|
|
1270
|
+
const image = readBorderImage(value, ctx);
|
|
1271
|
+
if (image)
|
|
1272
|
+
style.borderImage = image;
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
case 'border-image-source': {
|
|
1276
|
+
const source = backgroundImageOf(value, ctx);
|
|
1277
|
+
if (source === undefined)
|
|
1278
|
+
return;
|
|
1279
|
+
style.borderImage = { ...style.borderImage, source };
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
case 'border-image-slice': {
|
|
1283
|
+
const slice = borderImageSlice(splitValue(value));
|
|
1284
|
+
if (slice)
|
|
1285
|
+
style.borderImage = { ...style.borderImage, ...slice };
|
|
1286
|
+
return;
|
|
1287
|
+
}
|
|
1288
|
+
case 'border-image-width': {
|
|
1289
|
+
const width = sidesOf(splitValue(value), (p) => imageWidthOf(p, ctx));
|
|
1290
|
+
if (width)
|
|
1291
|
+
style.borderImage = { ...style.borderImage, width };
|
|
1292
|
+
return;
|
|
1293
|
+
}
|
|
1294
|
+
case 'border-image-outset': {
|
|
1295
|
+
const outset = sidesOf(splitValue(value), (p) => imageOutsetOf(p, ctx));
|
|
1296
|
+
if (outset)
|
|
1297
|
+
style.borderImage = { ...style.borderImage, outset };
|
|
1298
|
+
return;
|
|
1299
|
+
}
|
|
1300
|
+
case 'border-image-repeat': {
|
|
1301
|
+
const repeat = imageRepeatOf(splitValue(value.toLowerCase()));
|
|
1302
|
+
if (repeat)
|
|
1303
|
+
style.borderImage = { ...style.borderImage, repeat };
|
|
1304
|
+
return;
|
|
1305
|
+
}
|
|
1306
|
+
case 'background-origin': {
|
|
1307
|
+
const origins = layerValues(value, (part) => {
|
|
1308
|
+
const v = part.toLowerCase();
|
|
1309
|
+
return isVisualBox(v) ? v : null;
|
|
1310
|
+
});
|
|
1311
|
+
if (!origins)
|
|
1312
|
+
return;
|
|
1313
|
+
style.backgroundOrigin = origins[0];
|
|
1314
|
+
style.backgroundOrigins = origins.length > 1 ? origins : null;
|
|
473
1315
|
return;
|
|
474
1316
|
}
|
|
475
1317
|
case 'background-repeat': {
|
|
476
|
-
const
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
1318
|
+
const repeats = layerValues(value, (part) => {
|
|
1319
|
+
const words = splitValue(part.toLowerCase());
|
|
1320
|
+
return words.length <= 2 ? readRepeat(words) : null;
|
|
1321
|
+
});
|
|
1322
|
+
if (!repeats)
|
|
1323
|
+
return;
|
|
1324
|
+
style.backgroundRepeat = repeats[0];
|
|
1325
|
+
style.backgroundRepeats = repeats.length > 1 ? repeats : null;
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
case 'background-attachment': {
|
|
1329
|
+
const attachments = layerValues(value, (part) => {
|
|
1330
|
+
const v = part.toLowerCase();
|
|
1331
|
+
return v === 'scroll' || v === 'fixed' || v === 'local' ? v : null;
|
|
1332
|
+
});
|
|
1333
|
+
if (!attachments)
|
|
1334
|
+
return;
|
|
1335
|
+
style.backgroundAttachment = attachments[0];
|
|
1336
|
+
style.backgroundAttachments = attachments.length > 1 ? attachments : null;
|
|
483
1337
|
return;
|
|
484
1338
|
}
|
|
485
1339
|
case 'background-size': {
|
|
486
|
-
const
|
|
487
|
-
if (
|
|
488
|
-
|
|
1340
|
+
const sizes = layerValues(value, (part) => backgroundSizeOf(splitValue(part), ctx));
|
|
1341
|
+
if (!sizes)
|
|
1342
|
+
return;
|
|
1343
|
+
style.backgroundSize = sizes[0];
|
|
1344
|
+
style.backgroundSizes = sizes.length > 1 ? sizes : null;
|
|
489
1345
|
return;
|
|
490
1346
|
}
|
|
491
1347
|
case 'background-position': {
|
|
492
|
-
const
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
1348
|
+
const pairs = layerValues(value, (part) => positionPair(splitValue(part), ctx));
|
|
1349
|
+
if (!pairs)
|
|
1350
|
+
return;
|
|
1351
|
+
[style.backgroundPositionX, style.backgroundPositionY] = pairs[0];
|
|
1352
|
+
style.backgroundPositions = pairs.length > 1 ? pairs : null;
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1355
|
+
// --- generated content --------------------------------------------------
|
|
1356
|
+
case 'content': {
|
|
1357
|
+
const parsed = parseContent(value);
|
|
1358
|
+
if (parsed !== null)
|
|
1359
|
+
style.content = parsed;
|
|
1360
|
+
return;
|
|
1361
|
+
}
|
|
1362
|
+
case 'counter-reset':
|
|
1363
|
+
case 'counter-increment':
|
|
1364
|
+
case 'counter-set': {
|
|
1365
|
+
const reset = name === 'counter-reset';
|
|
1366
|
+
const increment = name === 'counter-increment';
|
|
1367
|
+
const parsed = parseCounterList(value, increment ? 1 : 0, reset);
|
|
1368
|
+
if (parsed === null)
|
|
1369
|
+
return;
|
|
1370
|
+
const list = parsed === 'none' ? null : parsed;
|
|
1371
|
+
if (reset)
|
|
1372
|
+
style.counterReset = list;
|
|
1373
|
+
else if (increment)
|
|
1374
|
+
style.counterIncrement = list;
|
|
1375
|
+
else
|
|
1376
|
+
style.counterSet = list;
|
|
1377
|
+
return;
|
|
1378
|
+
}
|
|
1379
|
+
case 'quotes': {
|
|
1380
|
+
const parsed = parseQuotes(value);
|
|
1381
|
+
if (parsed !== null)
|
|
1382
|
+
style.quotes = parsed;
|
|
499
1383
|
return;
|
|
500
1384
|
}
|
|
501
1385
|
// --- text ---------------------------------------------------------------
|
|
@@ -505,26 +1389,48 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
505
1389
|
}
|
|
506
1390
|
case 'font-family': {
|
|
507
1391
|
// ntk's font matcher takes the CSS list as written and walks it, so the
|
|
508
|
-
// value passes through whole rather than being resolved here
|
|
509
|
-
|
|
510
|
-
|
|
1392
|
+
// value passes through whole rather than being resolved here — once
|
|
1393
|
+
// every name in it is one: a string, or identifiers (CSS 2.1 15.3).
|
|
1394
|
+
// `test!foo, Ahem` is no list, and set Ahem.
|
|
1395
|
+
const names = splitCommas(value);
|
|
1396
|
+
if (!names.every(isFamilyName))
|
|
1397
|
+
return;
|
|
1398
|
+
// and an unquoted name is its identifiers joined by one space each,
|
|
1399
|
+
// however they were spaced: `Courier New` over two lines is
|
|
1400
|
+
// `Courier New`, and was a name no font has
|
|
1401
|
+
const list = names
|
|
1402
|
+
.map((f) => /^['"]/.test(f)
|
|
1403
|
+
? f.replace(/^['"]|['"]$/g, '')
|
|
1404
|
+
: f.trim().replace(/[ \t\n\r\f]+/g, ' '))
|
|
511
1405
|
.filter(Boolean)
|
|
512
1406
|
.join(', ');
|
|
1407
|
+
style.fontFamily = ctx.families ? ctx.families(list) : list;
|
|
513
1408
|
return;
|
|
514
1409
|
}
|
|
515
1410
|
case 'font-size': {
|
|
1411
|
+
// No larger than 10,000 pixels, as Chrome keeps it: a text engine
|
|
1412
|
+
// handed a face at a size of millions shapes and caches glyphs that
|
|
1413
|
+
// size, and a nest of `larger`s gets there on its own
|
|
1414
|
+
const most = MAX_FONT_SIZE * ctx.scale;
|
|
516
1415
|
const kw = keywordFontSize(value, parent.fontSize, ctx.rem);
|
|
517
1416
|
if (kw !== null) {
|
|
518
|
-
style.fontSize = kw;
|
|
1417
|
+
style.fontSize = Math.min(kw, most);
|
|
519
1418
|
return;
|
|
520
1419
|
}
|
|
521
1420
|
// `em` in a `font-size` is relative to the *parent's* size, not this
|
|
522
1421
|
// element's — the one place the unit context has to be overridden.
|
|
1422
|
+
// 0 is a size — the text takes no room, which is what a container of
|
|
1423
|
+
// inline-blocks sets to lose the spaces between them — and a negative
|
|
1424
|
+
// one is no size at all, so the declaration goes
|
|
523
1425
|
const len = parseLength(value, { ...ctx, em: parent.fontSize });
|
|
524
|
-
if (typeof len === 'number')
|
|
525
|
-
|
|
1426
|
+
if (typeof len === 'number') {
|
|
1427
|
+
if (len >= 0)
|
|
1428
|
+
style.fontSize = Math.min(len, most);
|
|
1429
|
+
}
|
|
526
1430
|
else if (len && typeof len === 'object') {
|
|
527
|
-
|
|
1431
|
+
const size = resolve(len, parent.fontSize);
|
|
1432
|
+
if (size >= 0)
|
|
1433
|
+
style.fontSize = Math.min(size, most);
|
|
528
1434
|
}
|
|
529
1435
|
return;
|
|
530
1436
|
}
|
|
@@ -545,19 +1451,28 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
545
1451
|
style.lineHeightIsLength = false;
|
|
546
1452
|
return;
|
|
547
1453
|
}
|
|
1454
|
+
// none of it below nought, which drops the declaration (CSS 2.1
|
|
1455
|
+
// 10.8.1)
|
|
548
1456
|
const n = parseNumber(value);
|
|
549
1457
|
if (n !== null) {
|
|
1458
|
+
if (n < 0)
|
|
1459
|
+
return;
|
|
550
1460
|
style.lineHeight = n;
|
|
551
1461
|
style.lineHeightIsLength = false;
|
|
552
1462
|
return;
|
|
553
1463
|
}
|
|
554
1464
|
const len = parseLength(value, ctx);
|
|
555
1465
|
if (typeof len === 'number') {
|
|
1466
|
+
if (len < 0)
|
|
1467
|
+
return;
|
|
556
1468
|
style.lineHeight = len;
|
|
557
1469
|
style.lineHeightIsLength = true;
|
|
558
1470
|
}
|
|
559
1471
|
else if (len && typeof len === 'object') {
|
|
560
|
-
|
|
1472
|
+
const px = resolve(len, style.fontSize);
|
|
1473
|
+
if (px < 0)
|
|
1474
|
+
return;
|
|
1475
|
+
style.lineHeight = px;
|
|
561
1476
|
style.lineHeightIsLength = true;
|
|
562
1477
|
}
|
|
563
1478
|
return;
|
|
@@ -571,10 +1486,58 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
571
1486
|
v === 'start' ||
|
|
572
1487
|
v === 'end') {
|
|
573
1488
|
style.textAlign = v;
|
|
1489
|
+
style.alignBlocks = null;
|
|
1490
|
+
style.tableTextAlignSet = true;
|
|
1491
|
+
return;
|
|
1492
|
+
}
|
|
1493
|
+
// every line justified, the last as well (CSS Text 3, 7.1)
|
|
1494
|
+
if (v === 'justify-all') {
|
|
1495
|
+
style.textAlign = 'justify';
|
|
1496
|
+
style.textAlignLast = 'justify';
|
|
1497
|
+
style.alignBlocks = null;
|
|
1498
|
+
style.tableTextAlignSet = true;
|
|
1499
|
+
return;
|
|
1500
|
+
}
|
|
1501
|
+
// the value HTML's alignment is given as, the blocks inside aligned
|
|
1502
|
+
// with the text — and which mail writes for itself
|
|
1503
|
+
const aligned = /^-(?:webkit|moz|khtml)-(left|right|center)$/.exec(v);
|
|
1504
|
+
if (aligned) {
|
|
1505
|
+
const side = aligned[1];
|
|
1506
|
+
style.textAlign = side;
|
|
1507
|
+
style.alignBlocks = side;
|
|
574
1508
|
style.tableTextAlignSet = true;
|
|
575
1509
|
}
|
|
576
1510
|
return;
|
|
577
1511
|
}
|
|
1512
|
+
case 'text-align-last': {
|
|
1513
|
+
const v = value.toLowerCase();
|
|
1514
|
+
if (v === 'auto' ||
|
|
1515
|
+
v === 'left' ||
|
|
1516
|
+
v === 'right' ||
|
|
1517
|
+
v === 'center' ||
|
|
1518
|
+
v === 'justify' ||
|
|
1519
|
+
v === 'start' ||
|
|
1520
|
+
v === 'end') {
|
|
1521
|
+
style.textAlignLast = v;
|
|
1522
|
+
}
|
|
1523
|
+
else if (v === 'match-parent') {
|
|
1524
|
+
style.textAlignLast = parent.textAlignLast;
|
|
1525
|
+
}
|
|
1526
|
+
return;
|
|
1527
|
+
}
|
|
1528
|
+
case 'text-justify': {
|
|
1529
|
+
const v = value.toLowerCase();
|
|
1530
|
+
if (v === 'auto' ||
|
|
1531
|
+
v === 'none' ||
|
|
1532
|
+
v === 'inter-word' ||
|
|
1533
|
+
v === 'inter-character') {
|
|
1534
|
+
style.textJustify = v;
|
|
1535
|
+
}
|
|
1536
|
+
else if (v === 'distribute') {
|
|
1537
|
+
style.textJustify = 'inter-character';
|
|
1538
|
+
}
|
|
1539
|
+
return;
|
|
1540
|
+
}
|
|
578
1541
|
case 'text-indent': {
|
|
579
1542
|
const len = parseLength(value, ctx);
|
|
580
1543
|
if (len !== null && len !== AUTO)
|
|
@@ -607,17 +1570,159 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
607
1570
|
else
|
|
608
1571
|
style.wordSpacing = len;
|
|
609
1572
|
}
|
|
1573
|
+
else if (name === 'letter-spacing' && len && isPct(len)) {
|
|
1574
|
+
// a percentage of the font size, as `em` is (CSS Text 4): `200%`
|
|
1575
|
+
// was dropped, where it is twice the letter's size apart
|
|
1576
|
+
style.letterSpacing = resolve(len, ctx.em);
|
|
1577
|
+
}
|
|
610
1578
|
return;
|
|
611
1579
|
}
|
|
612
|
-
case '
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
1580
|
+
case 'font-variant-numeric':
|
|
1581
|
+
case 'font-variant-caps':
|
|
1582
|
+
case 'font-variant-ligatures':
|
|
1583
|
+
case 'font-variant-position': {
|
|
1584
|
+
const kind = name.slice('font-variant-'.length);
|
|
1585
|
+
const variant = parseFontVariant(value, kind);
|
|
1586
|
+
if (variant)
|
|
1587
|
+
Object.assign(style, variant);
|
|
1588
|
+
return;
|
|
1589
|
+
}
|
|
1590
|
+
case 'font-variant': {
|
|
1591
|
+
const variant = parseFontVariant(value, null);
|
|
1592
|
+
if (variant)
|
|
1593
|
+
Object.assign(style, variant);
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
case 'font-kerning': {
|
|
1597
|
+
const v = value.trim().toLowerCase();
|
|
1598
|
+
if (v === 'auto')
|
|
1599
|
+
style.fontKerning = '';
|
|
1600
|
+
else if (v === 'normal')
|
|
1601
|
+
style.fontKerning = 'kern=1';
|
|
1602
|
+
else if (v === 'none')
|
|
1603
|
+
style.fontKerning = 'kern=0';
|
|
1604
|
+
return;
|
|
1605
|
+
}
|
|
1606
|
+
case 'font-feature-settings': {
|
|
1607
|
+
const settings = parseFeatureSettings(value);
|
|
1608
|
+
if (settings !== null)
|
|
1609
|
+
style.fontFeatureSettings = settings;
|
|
1610
|
+
return;
|
|
1611
|
+
}
|
|
1612
|
+
case 'tab-size':
|
|
1613
|
+
case '-moz-tab-size': {
|
|
1614
|
+
// a number of spaces, or a length; neither below nought
|
|
1615
|
+
const v = value.trim();
|
|
1616
|
+
if (/^[+]?(\d+\.?\d*|\.\d+)$/.test(v)) {
|
|
1617
|
+
style.tabSize = Number(v);
|
|
1618
|
+
style.tabSizeIsLength = false;
|
|
1619
|
+
return;
|
|
1620
|
+
}
|
|
1621
|
+
const len = parseLength(v, ctx);
|
|
1622
|
+
if (typeof len === 'number' && len >= 0) {
|
|
1623
|
+
style.tabSize = len;
|
|
1624
|
+
style.tabSizeIsLength = true;
|
|
1625
|
+
}
|
|
1626
|
+
return;
|
|
1627
|
+
}
|
|
1628
|
+
case 'white-space': {
|
|
1629
|
+
const v = value.trim().toLowerCase();
|
|
1630
|
+
if (v === 'normal' ||
|
|
1631
|
+
v === 'nowrap' ||
|
|
1632
|
+
v === 'pre' ||
|
|
1633
|
+
v === 'pre-wrap' ||
|
|
618
1634
|
v === 'pre-line') {
|
|
619
1635
|
style.whiteSpace = v;
|
|
620
1636
|
}
|
|
1637
|
+
else if (v === 'break-spaces') {
|
|
1638
|
+
// `pre-wrap` whose spaces take room at a line's end rather than
|
|
1639
|
+
// hanging past it, which is as near as the engines come
|
|
1640
|
+
style.whiteSpace = 'pre-wrap';
|
|
1641
|
+
}
|
|
1642
|
+
return;
|
|
1643
|
+
}
|
|
1644
|
+
// a word is cut inside itself only where the style says it may be:
|
|
1645
|
+
// `anywhere` as `break-word` is
|
|
1646
|
+
case 'overflow-wrap':
|
|
1647
|
+
case 'word-wrap': {
|
|
1648
|
+
const v = value.trim().toLowerCase();
|
|
1649
|
+
if (v === 'normal')
|
|
1650
|
+
style.overflowWrap = 'normal';
|
|
1651
|
+
else if (v === 'break-word' || v === 'anywhere') {
|
|
1652
|
+
style.overflowWrap = 'break-word';
|
|
1653
|
+
}
|
|
1654
|
+
return;
|
|
1655
|
+
}
|
|
1656
|
+
case 'line-break': {
|
|
1657
|
+
const v = value.trim().toLowerCase();
|
|
1658
|
+
if (v === 'auto' ||
|
|
1659
|
+
v === 'loose' ||
|
|
1660
|
+
v === 'normal' ||
|
|
1661
|
+
v === 'strict' ||
|
|
1662
|
+
v === 'anywhere') {
|
|
1663
|
+
style.lineBreakAnywhere = v === 'anywhere';
|
|
1664
|
+
}
|
|
1665
|
+
return;
|
|
1666
|
+
}
|
|
1667
|
+
case 'word-break': {
|
|
1668
|
+
const v = value.trim().toLowerCase();
|
|
1669
|
+
if (v === 'normal' ||
|
|
1670
|
+
v === 'break-all' ||
|
|
1671
|
+
v === 'keep-all' ||
|
|
1672
|
+
v === 'break-word') {
|
|
1673
|
+
style.wordBreak = v;
|
|
1674
|
+
}
|
|
1675
|
+
return;
|
|
1676
|
+
}
|
|
1677
|
+
// CSS Text 4 splits `white-space` into what it keeps of the white space
|
|
1678
|
+
// and whether its lines wrap; each longhand changes its half of it
|
|
1679
|
+
case 'white-space-collapse': {
|
|
1680
|
+
const v = value.trim().toLowerCase();
|
|
1681
|
+
const collapse = v === 'collapse'
|
|
1682
|
+
? 'collapse'
|
|
1683
|
+
: v === 'preserve' || v === 'break-spaces' || v === 'preserve-spaces'
|
|
1684
|
+
? 'preserve'
|
|
1685
|
+
: v === 'preserve-breaks'
|
|
1686
|
+
? 'preserve-breaks'
|
|
1687
|
+
: null;
|
|
1688
|
+
if (collapse) {
|
|
1689
|
+
style.whiteSpace = whiteSpaceOf(collapse, wrapsIn(style.whiteSpace));
|
|
1690
|
+
}
|
|
1691
|
+
return;
|
|
1692
|
+
}
|
|
1693
|
+
case 'text-wrap':
|
|
1694
|
+
case 'text-wrap-mode':
|
|
1695
|
+
case 'text-wrap-style': {
|
|
1696
|
+
// `text-wrap` is both, and resets the one it leaves out: Tailwind 4
|
|
1697
|
+
// writes `text-nowrap` and `text-balance` in it
|
|
1698
|
+
let wrap = null;
|
|
1699
|
+
let balance = null;
|
|
1700
|
+
for (const word of splitValue(value.toLowerCase())) {
|
|
1701
|
+
const mode = word === 'wrap' || word === 'nowrap';
|
|
1702
|
+
const kind = word === 'auto' ||
|
|
1703
|
+
word === 'balance' ||
|
|
1704
|
+
word === 'pretty' ||
|
|
1705
|
+
word === 'stable';
|
|
1706
|
+
if (mode && wrap === null && name !== 'text-wrap-style') {
|
|
1707
|
+
wrap = word === 'wrap';
|
|
1708
|
+
}
|
|
1709
|
+
else if (kind && balance === null && name !== 'text-wrap-mode') {
|
|
1710
|
+
balance = word === 'balance';
|
|
1711
|
+
}
|
|
1712
|
+
else
|
|
1713
|
+
return;
|
|
1714
|
+
}
|
|
1715
|
+
if (wrap === null && balance === null)
|
|
1716
|
+
return;
|
|
1717
|
+
if (name === 'text-wrap') {
|
|
1718
|
+
wrap ??= true;
|
|
1719
|
+
balance ??= false;
|
|
1720
|
+
}
|
|
1721
|
+
if (wrap !== null) {
|
|
1722
|
+
style.whiteSpace = whiteSpaceOf(COLLAPSE_OF[style.whiteSpace], wrap);
|
|
1723
|
+
}
|
|
1724
|
+
if (balance !== null)
|
|
1725
|
+
style.textWrapStyle = balance ? 'balance' : 'auto';
|
|
621
1726
|
return;
|
|
622
1727
|
}
|
|
623
1728
|
case 'direction': {
|
|
@@ -626,28 +1731,86 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
626
1731
|
style.direction = v;
|
|
627
1732
|
return;
|
|
628
1733
|
}
|
|
1734
|
+
case 'unicode-bidi': {
|
|
1735
|
+
const v = value.trim().toLowerCase();
|
|
1736
|
+
if (v === 'normal' ||
|
|
1737
|
+
v === 'embed' ||
|
|
1738
|
+
v === 'isolate' ||
|
|
1739
|
+
v === 'bidi-override' ||
|
|
1740
|
+
v === 'isolate-override' ||
|
|
1741
|
+
v === 'plaintext') {
|
|
1742
|
+
style.unicodeBidi = v;
|
|
1743
|
+
}
|
|
1744
|
+
return;
|
|
1745
|
+
}
|
|
629
1746
|
case 'text-decoration':
|
|
630
1747
|
case 'text-decoration-line': {
|
|
1748
|
+
// Read whole before any of it is taken: a word the property does not
|
|
1749
|
+
// know, one given twice, or `none` beside a line makes the declaration
|
|
1750
|
+
// invalid, and it is ignored (CSS 2.1 4.2) — `underline overline
|
|
1751
|
+
// line-through diagonal` draws nothing, where it drew a line-through.
|
|
1752
|
+
const lines = [];
|
|
1753
|
+
let none = false;
|
|
1754
|
+
let color = null;
|
|
1755
|
+
let decorationStyle = null;
|
|
1756
|
+
let thickness;
|
|
631
1757
|
for (const part of splitValue(value)) {
|
|
632
1758
|
const v = part.toLowerCase();
|
|
633
|
-
if (v
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
v
|
|
637
|
-
|
|
1759
|
+
if (LINE_KEYWORDS.has(v)) {
|
|
1760
|
+
if (none || lines.includes(v))
|
|
1761
|
+
return;
|
|
1762
|
+
lines.push(v);
|
|
1763
|
+
continue;
|
|
638
1764
|
}
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
style.textDecorationStyle =
|
|
645
|
-
v;
|
|
646
|
-
}
|
|
1765
|
+
if (v === 'none') {
|
|
1766
|
+
if (none || lines.length)
|
|
1767
|
+
return;
|
|
1768
|
+
none = true;
|
|
1769
|
+
continue;
|
|
647
1770
|
}
|
|
1771
|
+
if (name !== 'text-decoration')
|
|
1772
|
+
return;
|
|
1773
|
+
const c = parseColor(part);
|
|
1774
|
+
if (c) {
|
|
1775
|
+
if (color !== null)
|
|
1776
|
+
return;
|
|
1777
|
+
color = c;
|
|
1778
|
+
}
|
|
1779
|
+
else if (DECORATION_STYLES.has(v)) {
|
|
1780
|
+
if (decorationStyle !== null)
|
|
1781
|
+
return;
|
|
1782
|
+
decorationStyle = v;
|
|
1783
|
+
}
|
|
1784
|
+
else {
|
|
1785
|
+
const t = decorationLength(part, ctx);
|
|
1786
|
+
if (t === undefined || thickness !== undefined)
|
|
1787
|
+
return;
|
|
1788
|
+
thickness = t;
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
if (!none && !lines.length && name === 'text-decoration-line')
|
|
1792
|
+
return;
|
|
1793
|
+
style.textDecorationLine = lines.length ? lines.join(' ') : 'none';
|
|
1794
|
+
if (name === 'text-decoration') {
|
|
1795
|
+
// a shorthand: what it does not name goes back to its initial value
|
|
1796
|
+
style.textDecorationColor = color;
|
|
1797
|
+
style.textDecorationStyle = decorationStyle ?? 'solid';
|
|
1798
|
+
style.textDecorationThickness = thickness ?? null;
|
|
648
1799
|
}
|
|
649
1800
|
return;
|
|
650
1801
|
}
|
|
1802
|
+
case 'text-decoration-thickness': {
|
|
1803
|
+
const thickness = decorationLength(value, ctx, 'from-font');
|
|
1804
|
+
if (thickness !== undefined)
|
|
1805
|
+
style.textDecorationThickness = thickness;
|
|
1806
|
+
return;
|
|
1807
|
+
}
|
|
1808
|
+
case 'text-underline-offset': {
|
|
1809
|
+
const offset = decorationLength(value, ctx);
|
|
1810
|
+
if (offset !== undefined)
|
|
1811
|
+
style.textUnderlineOffset = offset;
|
|
1812
|
+
return;
|
|
1813
|
+
}
|
|
651
1814
|
case 'text-decoration-style': {
|
|
652
1815
|
const v = value.toLowerCase();
|
|
653
1816
|
if (DECORATION_STYLES.has(v)) {
|
|
@@ -661,17 +1824,77 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
661
1824
|
}
|
|
662
1825
|
// --- lists --------------------------------------------------------------
|
|
663
1826
|
case 'list-style': {
|
|
1827
|
+
// a type, a position and an image, each at most once and in any
|
|
1828
|
+
// order, and each left out reset (CSS 2.1 12.5.1); a `none` is
|
|
1829
|
+
// whichever of the type and the image is not otherwise given, both
|
|
1830
|
+
// where neither is, and a third thing where both are, which is
|
|
1831
|
+
// nothing and makes the declaration invalid
|
|
1832
|
+
let type = null;
|
|
1833
|
+
let position = null;
|
|
1834
|
+
let image;
|
|
1835
|
+
let nones = 0;
|
|
664
1836
|
for (const part of splitValue(value)) {
|
|
665
1837
|
const v = part.toLowerCase();
|
|
666
|
-
if (v === 'inside' || v === 'outside')
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
1838
|
+
if (v === 'inside' || v === 'outside') {
|
|
1839
|
+
if (position)
|
|
1840
|
+
return;
|
|
1841
|
+
position = v;
|
|
1842
|
+
}
|
|
1843
|
+
else if (v === 'none')
|
|
1844
|
+
nones += 1;
|
|
1845
|
+
else if (v.startsWith('url(') || IMAGE_FUNCTION.test(v)) {
|
|
1846
|
+
if (image !== undefined)
|
|
1847
|
+
return;
|
|
1848
|
+
image = v.startsWith('url(') ? (parseUrl(part) ?? null) : null;
|
|
1849
|
+
}
|
|
1850
|
+
else {
|
|
1851
|
+
if (type !== null)
|
|
1852
|
+
return;
|
|
1853
|
+
type = parseListStyleType(part);
|
|
1854
|
+
if (type === null)
|
|
1855
|
+
return;
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
if (nones > 2)
|
|
1859
|
+
return;
|
|
1860
|
+
if (nones === 2 && (type !== null || image !== undefined))
|
|
1861
|
+
return;
|
|
1862
|
+
if (nones === 1) {
|
|
1863
|
+
if (type !== null && image !== undefined)
|
|
1864
|
+
return;
|
|
1865
|
+
if (type === null)
|
|
1866
|
+
type = 'none';
|
|
1867
|
+
else
|
|
1868
|
+
image = null;
|
|
1869
|
+
}
|
|
1870
|
+
if (nones === 2) {
|
|
1871
|
+
type = 'none';
|
|
1872
|
+
image = null;
|
|
1873
|
+
}
|
|
1874
|
+
style.listStyleType = type ?? 'disc';
|
|
1875
|
+
style.listStylePosition = position ?? 'outside';
|
|
1876
|
+
style.listStyleImage = image ?? null;
|
|
1877
|
+
return;
|
|
1878
|
+
}
|
|
1879
|
+
case 'list-style-image': {
|
|
1880
|
+
const v = value.trim();
|
|
1881
|
+
if (v.toLowerCase() === 'none')
|
|
1882
|
+
style.listStyleImage = null;
|
|
1883
|
+
else if (IMAGE_FUNCTION.test(v.toLowerCase())) {
|
|
1884
|
+
// a gradient is an image this draws no marker as
|
|
1885
|
+
style.listStyleImage = null;
|
|
1886
|
+
}
|
|
1887
|
+
else if (/^url\(/i.test(v)) {
|
|
1888
|
+
const url = parseUrl(v);
|
|
1889
|
+
if (url !== undefined)
|
|
1890
|
+
style.listStyleImage = url;
|
|
670
1891
|
}
|
|
671
1892
|
return;
|
|
672
1893
|
}
|
|
673
1894
|
case 'list-style-type': {
|
|
674
|
-
|
|
1895
|
+
const type = parseListStyleType(value);
|
|
1896
|
+
if (type !== null)
|
|
1897
|
+
style.listStyleType = type;
|
|
675
1898
|
return;
|
|
676
1899
|
}
|
|
677
1900
|
case 'list-style-position': {
|
|
@@ -705,7 +1928,7 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
705
1928
|
return;
|
|
706
1929
|
}
|
|
707
1930
|
case 'justify-content': {
|
|
708
|
-
const v =
|
|
1931
|
+
const v = justifyKeyword(value);
|
|
709
1932
|
if (v)
|
|
710
1933
|
style.justifyContent = v;
|
|
711
1934
|
return;
|
|
@@ -759,23 +1982,239 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
759
1982
|
return;
|
|
760
1983
|
}
|
|
761
1984
|
case 'order': {
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
1985
|
+
// an integer, and nothing else (CSS Flexbox 5.4)
|
|
1986
|
+
const v = value.trim();
|
|
1987
|
+
if (/^[+-]?\d+$/.test(v))
|
|
1988
|
+
style.order = Number(v);
|
|
765
1989
|
return;
|
|
766
1990
|
}
|
|
767
1991
|
case 'gap':
|
|
768
1992
|
case 'row-gap':
|
|
769
|
-
case 'column-gap':
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
1993
|
+
case 'column-gap':
|
|
1994
|
+
// the names CSS Grid 1 first gave them, which browsers still read
|
|
1995
|
+
case 'grid-gap':
|
|
1996
|
+
case 'grid-row-gap':
|
|
1997
|
+
case 'grid-column-gap': {
|
|
1998
|
+
const which = name.replace(/^grid-/, '');
|
|
1999
|
+
// a length or a percentage, not below nought, or `normal`, which is
|
|
2000
|
+
// nought outside columns of text
|
|
2001
|
+
const gap = (part) => {
|
|
2002
|
+
if (part === undefined)
|
|
2003
|
+
return null;
|
|
2004
|
+
if (part.toLowerCase() === 'normal')
|
|
2005
|
+
return 0;
|
|
2006
|
+
const len = parseLength(part, ctx);
|
|
2007
|
+
if (typeof len === 'number')
|
|
2008
|
+
return len >= 0 ? len : null;
|
|
2009
|
+
return len !== null && isPct(len) && !(len.pct < 0) ? len : null;
|
|
2010
|
+
};
|
|
2011
|
+
const parts = splitValue(value);
|
|
2012
|
+
if (!parts.length || parts.length > (which === 'gap' ? 2 : 1))
|
|
2013
|
+
return;
|
|
2014
|
+
const row = gap(parts[0]);
|
|
2015
|
+
const col = parts.length > 1 ? gap(parts[1]) : row;
|
|
2016
|
+
if (row === null || col === null)
|
|
774
2017
|
return;
|
|
775
|
-
if (
|
|
2018
|
+
if (which !== 'column-gap')
|
|
776
2019
|
style.rowGap = row;
|
|
777
|
-
if (
|
|
778
|
-
style.columnGap = col
|
|
2020
|
+
if (which !== 'row-gap')
|
|
2021
|
+
style.columnGap = col;
|
|
2022
|
+
return;
|
|
2023
|
+
}
|
|
2024
|
+
case 'grid-template-columns':
|
|
2025
|
+
case 'grid-template-rows': {
|
|
2026
|
+
const template = value.toLowerCase() === 'none' ? null : parseGridTemplate(value, ctx);
|
|
2027
|
+
if (template === undefined)
|
|
2028
|
+
return;
|
|
2029
|
+
if (name === 'grid-template-columns')
|
|
2030
|
+
style.gridColumns = template;
|
|
2031
|
+
else
|
|
2032
|
+
style.gridRows = template;
|
|
2033
|
+
return;
|
|
2034
|
+
}
|
|
2035
|
+
case 'grid-template-areas': {
|
|
2036
|
+
if (value.trim().toLowerCase() === 'none') {
|
|
2037
|
+
style.gridAreas = null;
|
|
2038
|
+
return;
|
|
2039
|
+
}
|
|
2040
|
+
const parts = trackParts(value);
|
|
2041
|
+
if (!parts?.length || !parts.every((p) => /^["']/.test(p)))
|
|
2042
|
+
return;
|
|
2043
|
+
const areas = parseAreas(parts);
|
|
2044
|
+
if (areas)
|
|
2045
|
+
style.gridAreas = areas;
|
|
2046
|
+
return;
|
|
2047
|
+
}
|
|
2048
|
+
case 'grid-auto-flow': {
|
|
2049
|
+
const words = value.trim().toLowerCase().split(/\s+/);
|
|
2050
|
+
const dense = words.includes('dense');
|
|
2051
|
+
const axis = words.filter((w) => w !== 'dense');
|
|
2052
|
+
if (axis.length > 1 ||
|
|
2053
|
+
words.length > 2 ||
|
|
2054
|
+
(axis.length && axis[0] !== 'row' && axis[0] !== 'column') ||
|
|
2055
|
+
(!dense && !axis.length)) {
|
|
2056
|
+
return;
|
|
2057
|
+
}
|
|
2058
|
+
const flow = axis[0] ?? 'row';
|
|
2059
|
+
style.gridAutoFlow = dense
|
|
2060
|
+
? flow === 'row'
|
|
2061
|
+
? 'row dense'
|
|
2062
|
+
: 'column dense'
|
|
2063
|
+
: flow;
|
|
2064
|
+
return;
|
|
2065
|
+
}
|
|
2066
|
+
case 'grid-auto-rows':
|
|
2067
|
+
case 'grid-auto-columns': {
|
|
2068
|
+
const track = parseGridTrack(splitValue(value)[0] ?? '', ctx);
|
|
2069
|
+
if (!track)
|
|
2070
|
+
return;
|
|
2071
|
+
if (name === 'grid-auto-rows')
|
|
2072
|
+
style.gridAutoRows = track;
|
|
2073
|
+
else
|
|
2074
|
+
style.gridAutoColumns = track;
|
|
2075
|
+
return;
|
|
2076
|
+
}
|
|
2077
|
+
case 'grid-template': {
|
|
2078
|
+
// rows, a slash and columns, or `none` (CSS Grid 1, 7.4)
|
|
2079
|
+
if (value.trim().toLowerCase() === 'none') {
|
|
2080
|
+
style.gridRows = null;
|
|
2081
|
+
style.gridColumns = null;
|
|
2082
|
+
style.gridAreas = null;
|
|
2083
|
+
return;
|
|
2084
|
+
}
|
|
2085
|
+
const template = parseTemplateShorthand(value, ctx);
|
|
2086
|
+
if (!template)
|
|
2087
|
+
return;
|
|
2088
|
+
style.gridRows = template.rows;
|
|
2089
|
+
style.gridColumns = template.columns;
|
|
2090
|
+
style.gridAreas = template.areas;
|
|
2091
|
+
return;
|
|
2092
|
+
}
|
|
2093
|
+
case 'grid': {
|
|
2094
|
+
// a template, or one axis's template and the other's `auto-flow`
|
|
2095
|
+
// tracks (7.8); every longhand it does not name back to its initial
|
|
2096
|
+
const parts = splitTopLevelSlash(value);
|
|
2097
|
+
const flows = (part) => part !== undefined && /(^|\s)auto-flow(\s|$)/i.test(part);
|
|
2098
|
+
const autoTrack = (part) => {
|
|
2099
|
+
const words = splitValue(part).filter((w) => !/^(auto-flow|dense)$/i.test(w));
|
|
2100
|
+
return words.length ? parseGridTrack(words[0], ctx) : AUTO_TRACK;
|
|
2101
|
+
};
|
|
2102
|
+
const dense = (part) => /(^|\s)dense(\s|$)/i.test(part);
|
|
2103
|
+
const axis = (part) => part.trim().toLowerCase() === 'none'
|
|
2104
|
+
? null
|
|
2105
|
+
: parseGridTemplate(part, ctx);
|
|
2106
|
+
if (parts.length === 2 && flows(parts[0])) {
|
|
2107
|
+
const rows = autoTrack(parts[0]);
|
|
2108
|
+
const columns = axis(parts[1]);
|
|
2109
|
+
if (!rows || columns === undefined)
|
|
2110
|
+
return;
|
|
2111
|
+
style.gridRows = null;
|
|
2112
|
+
style.gridColumns = columns;
|
|
2113
|
+
style.gridAreas = null;
|
|
2114
|
+
style.gridAutoRows = rows;
|
|
2115
|
+
style.gridAutoColumns = AUTO_TRACK;
|
|
2116
|
+
style.gridAutoFlow = dense(parts[0]) ? 'row dense' : 'row';
|
|
2117
|
+
return;
|
|
2118
|
+
}
|
|
2119
|
+
if (parts.length === 2 && flows(parts[1])) {
|
|
2120
|
+
const rows = axis(parts[0]);
|
|
2121
|
+
const columns = autoTrack(parts[1]);
|
|
2122
|
+
if (rows === undefined || !columns)
|
|
2123
|
+
return;
|
|
2124
|
+
style.gridRows = rows;
|
|
2125
|
+
style.gridColumns = null;
|
|
2126
|
+
style.gridAreas = null;
|
|
2127
|
+
style.gridAutoRows = AUTO_TRACK;
|
|
2128
|
+
style.gridAutoColumns = columns;
|
|
2129
|
+
style.gridAutoFlow = dense(parts[1]) ? 'column dense' : 'column';
|
|
2130
|
+
return;
|
|
2131
|
+
}
|
|
2132
|
+
const none = value.trim().toLowerCase() === 'none';
|
|
2133
|
+
const template = none ? null : parseTemplateShorthand(value, ctx);
|
|
2134
|
+
if (!none && !template)
|
|
2135
|
+
return;
|
|
2136
|
+
style.gridRows = template?.rows ?? null;
|
|
2137
|
+
style.gridColumns = template?.columns ?? null;
|
|
2138
|
+
style.gridAreas = template?.areas ?? null;
|
|
2139
|
+
style.gridAutoRows = AUTO_TRACK;
|
|
2140
|
+
style.gridAutoColumns = AUTO_TRACK;
|
|
2141
|
+
style.gridAutoFlow = 'row';
|
|
2142
|
+
return;
|
|
2143
|
+
}
|
|
2144
|
+
case 'grid-column':
|
|
2145
|
+
case 'grid-row': {
|
|
2146
|
+
// an end left out is the start where the start is a name, and auto
|
|
2147
|
+
// where it is not (8.4)
|
|
2148
|
+
const parts = splitTopLevelSlash(value);
|
|
2149
|
+
const start = parseGridLine(parts[0]);
|
|
2150
|
+
const end = parts.length > 1 ? parseGridLine(parts[1]) : named(start);
|
|
2151
|
+
if (start === undefined || end === undefined || parts.length > 2)
|
|
2152
|
+
return;
|
|
2153
|
+
if (name === 'grid-column') {
|
|
2154
|
+
style.gridColumnStart = start;
|
|
2155
|
+
style.gridColumnEnd = end;
|
|
2156
|
+
}
|
|
2157
|
+
else {
|
|
2158
|
+
style.gridRowStart = start;
|
|
2159
|
+
style.gridRowEnd = end;
|
|
2160
|
+
}
|
|
2161
|
+
return;
|
|
2162
|
+
}
|
|
2163
|
+
case 'grid-area': {
|
|
2164
|
+
// the lines left out copy a name: the column start the row start's,
|
|
2165
|
+
// and each end its start's (8.4)
|
|
2166
|
+
const parts = splitTopLevelSlash(value).map(parseGridLine);
|
|
2167
|
+
if (parts.length > 4 || parts.some((p) => p === undefined))
|
|
2168
|
+
return;
|
|
2169
|
+
const rowStart = parts[0] ?? null;
|
|
2170
|
+
const columnStart = parts.length > 1 ? parts[1] : named(rowStart);
|
|
2171
|
+
style.gridRowStart = rowStart;
|
|
2172
|
+
style.gridColumnStart = columnStart;
|
|
2173
|
+
style.gridRowEnd = parts.length > 2 ? parts[2] : named(rowStart);
|
|
2174
|
+
style.gridColumnEnd = parts.length > 3 ? parts[3] : named(columnStart);
|
|
2175
|
+
return;
|
|
2176
|
+
}
|
|
2177
|
+
case 'grid-column-start':
|
|
2178
|
+
case 'grid-column-end':
|
|
2179
|
+
case 'grid-row-start':
|
|
2180
|
+
case 'grid-row-end': {
|
|
2181
|
+
const line = parseGridLine(value);
|
|
2182
|
+
if (line === undefined)
|
|
2183
|
+
return;
|
|
2184
|
+
style[camel(name)] = line;
|
|
2185
|
+
return;
|
|
2186
|
+
}
|
|
2187
|
+
case 'justify-items':
|
|
2188
|
+
case 'justify-self': {
|
|
2189
|
+
const v = value.trim().toLowerCase();
|
|
2190
|
+
const keyword = v === 'auto' && name === 'justify-self'
|
|
2191
|
+
? 'auto'
|
|
2192
|
+
: v === 'normal' || v === 'stretch'
|
|
2193
|
+
? v
|
|
2194
|
+
: alignKeyword(v);
|
|
2195
|
+
if (keyword === 'auto' ||
|
|
2196
|
+
keyword === 'normal' ||
|
|
2197
|
+
keyword === 'stretch' ||
|
|
2198
|
+
keyword === 'flex-start' ||
|
|
2199
|
+
keyword === 'flex-end' ||
|
|
2200
|
+
keyword === 'center') {
|
|
2201
|
+
if (name === 'justify-items' && keyword !== 'auto') {
|
|
2202
|
+
style.justifyItems = keyword;
|
|
2203
|
+
}
|
|
2204
|
+
else if (name === 'justify-self')
|
|
2205
|
+
style.justifySelf = keyword;
|
|
2206
|
+
}
|
|
2207
|
+
return;
|
|
2208
|
+
}
|
|
2209
|
+
case 'place-items':
|
|
2210
|
+
case 'place-self': {
|
|
2211
|
+
const parts = splitValue(value);
|
|
2212
|
+
if (!parts.length || parts.length > 2)
|
|
2213
|
+
return;
|
|
2214
|
+
const align = name === 'place-items' ? 'align-items' : 'align-self';
|
|
2215
|
+
const justify = name === 'place-items' ? 'justify-items' : 'justify-self';
|
|
2216
|
+
applyDeclaration(style, parent, align, parts[0], ctx);
|
|
2217
|
+
applyDeclaration(style, parent, justify, parts[1] ?? parts[0], ctx);
|
|
779
2218
|
return;
|
|
780
2219
|
}
|
|
781
2220
|
// --- tables -------------------------------------------------------------
|
|
@@ -785,10 +2224,30 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
785
2224
|
style.borderCollapse = v;
|
|
786
2225
|
return;
|
|
787
2226
|
}
|
|
2227
|
+
case 'caption-side': {
|
|
2228
|
+
const v = value.toLowerCase();
|
|
2229
|
+
if (v === 'top' || v === 'bottom')
|
|
2230
|
+
style.captionSide = v;
|
|
2231
|
+
return;
|
|
2232
|
+
}
|
|
2233
|
+
case 'empty-cells': {
|
|
2234
|
+
const v = value.trim().toLowerCase();
|
|
2235
|
+
if (v === 'show' || v === 'hide')
|
|
2236
|
+
style.emptyCells = v;
|
|
2237
|
+
return;
|
|
2238
|
+
}
|
|
788
2239
|
case 'border-spacing': {
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
2240
|
+
// one length for both, or the columns' and then the rows' (CSS 2.1
|
|
2241
|
+
// 17.6.1), never negative
|
|
2242
|
+
const parts = splitValue(value);
|
|
2243
|
+
if (parts.length < 1 || parts.length > 2)
|
|
2244
|
+
return;
|
|
2245
|
+
const x = parseLength(parts[0], ctx);
|
|
2246
|
+
const y = parts.length > 1 ? parseLength(parts[1], ctx) : x;
|
|
2247
|
+
if (typeof x !== 'number' || typeof y !== 'number' || x < 0 || y < 0)
|
|
2248
|
+
return;
|
|
2249
|
+
style.borderSpacing = x;
|
|
2250
|
+
style.borderSpacingY = y;
|
|
792
2251
|
return;
|
|
793
2252
|
}
|
|
794
2253
|
case 'table-layout': {
|
|
@@ -801,6 +2260,21 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
|
|
|
801
2260
|
return;
|
|
802
2261
|
}
|
|
803
2262
|
}
|
|
2263
|
+
/** `-webkit-box-pack` and `-webkit-box-align`, as `justify-content` and
|
|
2264
|
+
* `align-items` say them. */
|
|
2265
|
+
const BOX_PACK = new Map([
|
|
2266
|
+
['start', 'flex-start'],
|
|
2267
|
+
['end', 'flex-end'],
|
|
2268
|
+
['center', 'center'],
|
|
2269
|
+
['justify', 'space-between'],
|
|
2270
|
+
]);
|
|
2271
|
+
const BOX_ALIGN = new Map([
|
|
2272
|
+
['start', 'flex-start'],
|
|
2273
|
+
['end', 'flex-end'],
|
|
2274
|
+
['center', 'center'],
|
|
2275
|
+
['baseline', 'baseline'],
|
|
2276
|
+
['stretch', 'stretch'],
|
|
2277
|
+
]);
|
|
804
2278
|
const DISPLAYS = new Set([
|
|
805
2279
|
'none',
|
|
806
2280
|
'block',
|
|
@@ -819,6 +2293,16 @@ const DISPLAYS = new Set([
|
|
|
819
2293
|
'table-caption',
|
|
820
2294
|
'table-column',
|
|
821
2295
|
'table-column-group',
|
|
2296
|
+
'contents',
|
|
2297
|
+
]);
|
|
2298
|
+
/** `text-decoration-line`'s lines. `blink` is one a user agent may leave
|
|
2299
|
+
* undrawn (CSS 2.1 16.3.1), and this one does; `overline` is not drawn
|
|
2300
|
+
* yet either. */
|
|
2301
|
+
const LINE_KEYWORDS = new Set([
|
|
2302
|
+
'underline',
|
|
2303
|
+
'overline',
|
|
2304
|
+
'line-through',
|
|
2305
|
+
'blink',
|
|
822
2306
|
]);
|
|
823
2307
|
const DECORATION_STYLES = new Set([
|
|
824
2308
|
'solid',
|
|
@@ -829,23 +2313,148 @@ const DECORATION_STYLES = new Set([
|
|
|
829
2313
|
]);
|
|
830
2314
|
function overflowKeyword(v) {
|
|
831
2315
|
const s = (v ?? '').toLowerCase();
|
|
832
|
-
if (s === 'visible' ||
|
|
2316
|
+
if (s === 'visible' ||
|
|
2317
|
+
s === 'hidden' ||
|
|
2318
|
+
s === 'scroll' ||
|
|
2319
|
+
s === 'auto' ||
|
|
2320
|
+
s === 'clip') {
|
|
833
2321
|
return s;
|
|
834
|
-
|
|
835
|
-
return 'hidden';
|
|
2322
|
+
}
|
|
836
2323
|
return null;
|
|
837
2324
|
}
|
|
2325
|
+
/** `contain`'s containments (CSS Containment 2, 3). */
|
|
2326
|
+
export const CONTAIN_SIZE = 1;
|
|
2327
|
+
export const CONTAIN_INLINE_SIZE = 2;
|
|
2328
|
+
export const CONTAIN_LAYOUT = 4;
|
|
2329
|
+
export const CONTAIN_STYLE = 8;
|
|
2330
|
+
export const CONTAIN_PAINT = 16;
|
|
2331
|
+
/** A `contain` value as its bits, or null where it is none: `strict` and
|
|
2332
|
+
* `content` are shorthands, and the rest a set of the five, `size` and
|
|
2333
|
+
* `inline-size` not both. */
|
|
2334
|
+
function containOf(value) {
|
|
2335
|
+
const words = value.trim().toLowerCase().split(/\s+/);
|
|
2336
|
+
if (words.length === 1) {
|
|
2337
|
+
if (words[0] === 'none')
|
|
2338
|
+
return 0;
|
|
2339
|
+
if (words[0] === 'strict') {
|
|
2340
|
+
return CONTAIN_SIZE | CONTAIN_LAYOUT | CONTAIN_STYLE | CONTAIN_PAINT;
|
|
2341
|
+
}
|
|
2342
|
+
if (words[0] === 'content') {
|
|
2343
|
+
return CONTAIN_LAYOUT | CONTAIN_STYLE | CONTAIN_PAINT;
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
let bits = 0;
|
|
2347
|
+
for (const word of words) {
|
|
2348
|
+
const bit = word === 'size'
|
|
2349
|
+
? CONTAIN_SIZE
|
|
2350
|
+
: word === 'inline-size'
|
|
2351
|
+
? CONTAIN_INLINE_SIZE
|
|
2352
|
+
: word === 'layout'
|
|
2353
|
+
? CONTAIN_LAYOUT
|
|
2354
|
+
: word === 'style'
|
|
2355
|
+
? CONTAIN_STYLE
|
|
2356
|
+
: word === 'paint'
|
|
2357
|
+
? CONTAIN_PAINT
|
|
2358
|
+
: 0;
|
|
2359
|
+
if (!bit || bits & bit)
|
|
2360
|
+
return null;
|
|
2361
|
+
bits |= bit;
|
|
2362
|
+
}
|
|
2363
|
+
if (bits & CONTAIN_SIZE && bits & CONTAIN_INLINE_SIZE)
|
|
2364
|
+
return null;
|
|
2365
|
+
return bits;
|
|
2366
|
+
}
|
|
2367
|
+
/**
|
|
2368
|
+
* The two axes' `overflow` as they compute together (CSS Overflow 3, 3.1):
|
|
2369
|
+
* `visible` and `clip` hold beside each other, and beside a value that
|
|
2370
|
+
* makes the box a scroll container, `visible` is `auto` and `clip` is
|
|
2371
|
+
* `hidden`.
|
|
2372
|
+
*/
|
|
2373
|
+
/**
|
|
2374
|
+
* The containment `content-visibility` brings with it (CSS Containment 2,
|
|
2375
|
+
* 4): `auto` layout, style and paint containment — and size containment
|
|
2376
|
+
* where the box is not on screen, which a document laid out whole never
|
|
2377
|
+
* tells — and `hidden` all four.
|
|
2378
|
+
*/
|
|
2379
|
+
export function settleContentVisibility(style) {
|
|
2380
|
+
if (style.contentVisibility === 'auto') {
|
|
2381
|
+
style.contain |= CONTAIN_LAYOUT | CONTAIN_STYLE | CONTAIN_PAINT;
|
|
2382
|
+
}
|
|
2383
|
+
else if (style.contentVisibility === 'hidden') {
|
|
2384
|
+
style.contain |=
|
|
2385
|
+
CONTAIN_SIZE | CONTAIN_LAYOUT | CONTAIN_STYLE | CONTAIN_PAINT;
|
|
2386
|
+
style.contain &= ~CONTAIN_INLINE_SIZE;
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
export function settleOverflow(style) {
|
|
2390
|
+
const x = style.overflowX;
|
|
2391
|
+
const y = style.overflowY;
|
|
2392
|
+
const scrollsX = x !== 'visible' && x !== 'clip';
|
|
2393
|
+
const scrollsY = y !== 'visible' && y !== 'clip';
|
|
2394
|
+
if (scrollsX === scrollsY)
|
|
2395
|
+
return;
|
|
2396
|
+
if (scrollsX)
|
|
2397
|
+
style.overflowY = y === 'visible' ? 'auto' : 'hidden';
|
|
2398
|
+
else
|
|
2399
|
+
style.overflowX = x === 'visible' ? 'auto' : 'hidden';
|
|
2400
|
+
}
|
|
2401
|
+
function isVisualBox(v) {
|
|
2402
|
+
return v === 'border-box' || v === 'padding-box' || v === 'content-box';
|
|
2403
|
+
}
|
|
2404
|
+
export function scrolls(style) {
|
|
2405
|
+
const x = style.overflowX;
|
|
2406
|
+
const y = style.overflowY;
|
|
2407
|
+
return (x !== 'visible' && x !== 'clip') || (y !== 'visible' && y !== 'clip');
|
|
2408
|
+
}
|
|
2409
|
+
/** A `justify-content`: the writing mode's `start` and `end`, and `left`
|
|
2410
|
+
* and `right`, kept for the flex box to resolve against its direction;
|
|
2411
|
+
* `normal` and `stretch` pack as `flex-start` does (`overflowAligned`). */
|
|
2412
|
+
function justifyKeyword(value) {
|
|
2413
|
+
const v = overflowAligned(value);
|
|
2414
|
+
switch (v) {
|
|
2415
|
+
case 'start':
|
|
2416
|
+
case 'end':
|
|
2417
|
+
case 'left':
|
|
2418
|
+
case 'right':
|
|
2419
|
+
case 'flex-start':
|
|
2420
|
+
case 'flex-end':
|
|
2421
|
+
case 'center':
|
|
2422
|
+
case 'space-between':
|
|
2423
|
+
case 'space-around':
|
|
2424
|
+
case 'space-evenly':
|
|
2425
|
+
return v;
|
|
2426
|
+
case 'normal':
|
|
2427
|
+
case 'stretch':
|
|
2428
|
+
return v;
|
|
2429
|
+
default:
|
|
2430
|
+
return null;
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
/** An alignment's keyword, with `unsafe` before it read past, which is
|
|
2434
|
+
* what an alignment does anyway. A `safe` one sets the subject at the
|
|
2435
|
+
* start where it overflows its room, which is not done here: it is no
|
|
2436
|
+
* value, and the declaration is dropped — which is the start's for the
|
|
2437
|
+
* flex box that overflows, as it was before `safe` was read at all. */
|
|
2438
|
+
function overflowAligned(value) {
|
|
2439
|
+
const v = value.trim().toLowerCase().replace(/\s+/g, ' ');
|
|
2440
|
+
const m = /^unsafe (.+)$/.exec(v);
|
|
2441
|
+
return m ? m[1] : v;
|
|
2442
|
+
}
|
|
838
2443
|
function alignKeyword(value) {
|
|
839
|
-
const v = value
|
|
2444
|
+
const v = overflowAligned(value);
|
|
840
2445
|
switch (v) {
|
|
841
2446
|
case 'start':
|
|
842
2447
|
case 'flex-start':
|
|
2448
|
+
case 'self-start':
|
|
843
2449
|
case 'left':
|
|
844
2450
|
return 'flex-start';
|
|
845
2451
|
case 'end':
|
|
846
2452
|
case 'flex-end':
|
|
2453
|
+
case 'self-end':
|
|
847
2454
|
case 'right':
|
|
848
2455
|
return 'flex-end';
|
|
2456
|
+
case 'first baseline':
|
|
2457
|
+
return 'baseline';
|
|
849
2458
|
case 'center':
|
|
850
2459
|
return 'center';
|
|
851
2460
|
case 'stretch':
|
|
@@ -865,23 +2474,316 @@ function borderWidth(value, ctx) {
|
|
|
865
2474
|
// The keywords are CSS pixels that never pass through `parseLength`, so
|
|
866
2475
|
// they take the display scale here.
|
|
867
2476
|
if (kw !== undefined)
|
|
868
|
-
return kw * ctx.scale;
|
|
2477
|
+
return snapBorderWidth(kw * ctx.scale);
|
|
869
2478
|
const len = parseLength(value, ctx);
|
|
870
|
-
|
|
2479
|
+
// a negative width is not a width: the declaration is dropped, and the
|
|
2480
|
+
// one before it stands (CSS 2.1 8.5.1)
|
|
2481
|
+
return typeof len === 'number' && len >= 0 ? snapBorderWidth(len) : null;
|
|
2482
|
+
}
|
|
2483
|
+
/**
|
|
2484
|
+
* A border's or an outline's width in whole device pixels, as it computes
|
|
2485
|
+
* (CSS Values 4, "snap as a border width"): down to the pixel, and a
|
|
2486
|
+
* hairline narrower than one pixel up to it — so two borders of 49.75px
|
|
2487
|
+
* leave the box between them its two pixels, and a 0.5px rule is drawn.
|
|
2488
|
+
* The fraction a length in `em` comes to beside a whole number is not
|
|
2489
|
+
* taken for less than it.
|
|
2490
|
+
*/
|
|
2491
|
+
function snapBorderWidth(width) {
|
|
2492
|
+
if (width > 0 && width < 1)
|
|
2493
|
+
return 1;
|
|
2494
|
+
return Math.floor(width + 1e-6);
|
|
871
2495
|
}
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
2496
|
+
const HORIZONTAL = { left: 0, center: 50, right: 100 };
|
|
2497
|
+
const VERTICAL = { top: 0, center: 50, bottom: 100 };
|
|
2498
|
+
/**
|
|
2499
|
+
* A `background-position` as its horizontal and vertical parts. A keyword
|
|
2500
|
+
* says which axis it is on, so one value alone is centred on the other —
|
|
2501
|
+
* `bottom` is the bottom, midway across — and two keywords come in either
|
|
2502
|
+
* order; with a length or a percentage among two, the first is across and
|
|
2503
|
+
* the second down (CSS 2.1 14.2.1). Three or four are CSS3's edge form:
|
|
2504
|
+
* `right 10px center` is ten pixels in from the right, midway down.
|
|
2505
|
+
*/
|
|
2506
|
+
function positionPair(parts, ctx) {
|
|
2507
|
+
if (parts.length === 0 || parts.length > 4)
|
|
875
2508
|
return null;
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
2509
|
+
const words = parts.map((p) => p.toLowerCase());
|
|
2510
|
+
if (parts.length > 2)
|
|
2511
|
+
return edgePosition(words, ctx);
|
|
2512
|
+
const keyword = (w) => w in HORIZONTAL || w in VERTICAL;
|
|
2513
|
+
const pct = (n) => (n === 0 ? 0 : { pct: n });
|
|
2514
|
+
if (parts.length === 1) {
|
|
2515
|
+
const [w] = words;
|
|
2516
|
+
if (w in VERTICAL && !(w in HORIZONTAL))
|
|
2517
|
+
return [pct(50), pct(VERTICAL[w])];
|
|
2518
|
+
if (keyword(w))
|
|
2519
|
+
return [pct(HORIZONTAL[w]), pct(50)];
|
|
2520
|
+
const x = positionLength(w, ctx);
|
|
2521
|
+
return x === null ? null : [x, pct(50)];
|
|
2522
|
+
}
|
|
2523
|
+
const [a, b] = words;
|
|
2524
|
+
if (keyword(a) && keyword(b)) {
|
|
2525
|
+
const swap = (a in VERTICAL && !(a in HORIZONTAL)) ||
|
|
2526
|
+
(b in HORIZONTAL && !(b in VERTICAL));
|
|
2527
|
+
const [h, v] = swap ? [b, a] : [a, b];
|
|
2528
|
+
if (!(h in HORIZONTAL) || !(v in VERTICAL))
|
|
2529
|
+
return null;
|
|
2530
|
+
return [pct(HORIZONTAL[h]), pct(VERTICAL[v])];
|
|
2531
|
+
}
|
|
2532
|
+
if (a in VERTICAL && !(a in HORIZONTAL))
|
|
2533
|
+
return null;
|
|
2534
|
+
if (b in HORIZONTAL && !(b in VERTICAL))
|
|
2535
|
+
return null;
|
|
2536
|
+
const x = keyword(a) ? pct(HORIZONTAL[a]) : positionLength(a, ctx);
|
|
2537
|
+
const y = keyword(b) ? pct(VERTICAL[b]) : positionLength(b, ctx);
|
|
2538
|
+
return x === null || y === null ? null : [x, y];
|
|
2539
|
+
}
|
|
2540
|
+
/** A position's length or percentage — `auto` is none. */
|
|
2541
|
+
function positionLength(word, ctx) {
|
|
2542
|
+
const len = parseLength(word, ctx);
|
|
2543
|
+
return len === AUTO ? null : len;
|
|
884
2544
|
}
|
|
2545
|
+
/** The edge form: a keyword for each axis, each but `center` with an
|
|
2546
|
+
* offset in from its edge or not. */
|
|
2547
|
+
function edgePosition(words, ctx) {
|
|
2548
|
+
const keyword = (w) => w !== undefined && (w in HORIZONTAL || w in VERTICAL);
|
|
2549
|
+
const groups = [];
|
|
2550
|
+
for (let i = 0; i < words.length; i += 1) {
|
|
2551
|
+
const word = words[i];
|
|
2552
|
+
if (!keyword(word))
|
|
2553
|
+
return null;
|
|
2554
|
+
let offset = null;
|
|
2555
|
+
if (i + 1 < words.length && !keyword(words[i + 1])) {
|
|
2556
|
+
if (word === 'center')
|
|
2557
|
+
return null;
|
|
2558
|
+
offset = positionLength(words[i + 1], ctx);
|
|
2559
|
+
if (offset === null)
|
|
2560
|
+
return null;
|
|
2561
|
+
i += 1;
|
|
2562
|
+
}
|
|
2563
|
+
groups.push({ word, offset });
|
|
2564
|
+
}
|
|
2565
|
+
if (groups.length !== 2)
|
|
2566
|
+
return null;
|
|
2567
|
+
let [h, v] = groups;
|
|
2568
|
+
if ((h.word in VERTICAL && !(h.word in HORIZONTAL)) ||
|
|
2569
|
+
(v.word in HORIZONTAL && !(v.word in VERTICAL))) {
|
|
2570
|
+
[h, v] = [v, h];
|
|
2571
|
+
}
|
|
2572
|
+
if (!(h.word in HORIZONTAL) || !(v.word in VERTICAL))
|
|
2573
|
+
return null;
|
|
2574
|
+
const x = fromEdge(HORIZONTAL[h.word], h.offset);
|
|
2575
|
+
const y = fromEdge(VERTICAL[v.word], v.offset);
|
|
2576
|
+
return x === null || y === null ? null : [x, y];
|
|
2577
|
+
}
|
|
2578
|
+
function fromEdge(at, offset) {
|
|
2579
|
+
if (offset === null)
|
|
2580
|
+
return at === 0 ? 0 : { pct: at };
|
|
2581
|
+
if (at === 0)
|
|
2582
|
+
return offset;
|
|
2583
|
+
// in from the right or the bottom: `100% - offset`, which a comparison
|
|
2584
|
+
// (`right min(10%, 20px)`) is not the sum for
|
|
2585
|
+
if (typeof offset === 'number')
|
|
2586
|
+
return { pct: 100, px: -offset };
|
|
2587
|
+
if (offset === AUTO || offset.of)
|
|
2588
|
+
return null;
|
|
2589
|
+
return { pct: 100 - offset.pct, px: -(offset.px ?? 0) };
|
|
2590
|
+
}
|
|
2591
|
+
const ONCE = { times: 1 };
|
|
2592
|
+
const ALL = { pct: 100 };
|
|
2593
|
+
export const NO_BORDER_IMAGE = {
|
|
2594
|
+
source: null,
|
|
2595
|
+
slice: [ALL, ALL, ALL, ALL],
|
|
2596
|
+
fill: false,
|
|
2597
|
+
width: [ONCE, ONCE, ONCE, ONCE],
|
|
2598
|
+
outset: [0, 0, 0, 0],
|
|
2599
|
+
repeat: ['stretch', 'stretch'],
|
|
2600
|
+
};
|
|
2601
|
+
/** One to four values, one a side as `margin` takes them; null where one
|
|
2602
|
+
* is not a value. */
|
|
2603
|
+
function sidesOf(parts, read) {
|
|
2604
|
+
if (!parts.length || parts.length > 4)
|
|
2605
|
+
return null;
|
|
2606
|
+
const values = [];
|
|
2607
|
+
for (const part of parts) {
|
|
2608
|
+
const v = read(part);
|
|
2609
|
+
if (v === null)
|
|
2610
|
+
return null;
|
|
2611
|
+
values.push(v);
|
|
2612
|
+
}
|
|
2613
|
+
return fourSides(values);
|
|
2614
|
+
}
|
|
2615
|
+
/** A slice's depth: a number, image pixels, or a percentage; neither may
|
|
2616
|
+
* be negative. */
|
|
2617
|
+
function sliceOf(part) {
|
|
2618
|
+
if (part.endsWith('%')) {
|
|
2619
|
+
const n = parseNumber(part.slice(0, -1));
|
|
2620
|
+
return n !== null && n >= 0 ? { pct: n } : null;
|
|
2621
|
+
}
|
|
2622
|
+
const n = parseNumber(part);
|
|
2623
|
+
return n !== null && n >= 0 ? n : null;
|
|
2624
|
+
}
|
|
2625
|
+
/** `border-image-slice`: one to four depths and `fill`, before them or
|
|
2626
|
+
* after them. */
|
|
2627
|
+
function borderImageSlice(parts) {
|
|
2628
|
+
const words = parts.map((p) => p.toLowerCase());
|
|
2629
|
+
const at = words.indexOf('fill');
|
|
2630
|
+
let fill = false;
|
|
2631
|
+
if (at >= 0) {
|
|
2632
|
+
if (at !== 0 && at !== words.length - 1)
|
|
2633
|
+
return null;
|
|
2634
|
+
fill = true;
|
|
2635
|
+
words.splice(at, 1);
|
|
2636
|
+
}
|
|
2637
|
+
const slice = sidesOf(words, sliceOf);
|
|
2638
|
+
return slice ? { slice, fill } : null;
|
|
2639
|
+
}
|
|
2640
|
+
/** A border image's width over a side: a multiple, a length or a
|
|
2641
|
+
* percentage of the image area, or `auto`; none negative. */
|
|
2642
|
+
function imageWidthOf(part, ctx) {
|
|
2643
|
+
const n = parseNumber(part);
|
|
2644
|
+
if (n !== null)
|
|
2645
|
+
return n >= 0 ? { times: n } : null;
|
|
2646
|
+
const len = parseLength(part, ctx);
|
|
2647
|
+
if (len === null)
|
|
2648
|
+
return null;
|
|
2649
|
+
if (typeof len === 'number')
|
|
2650
|
+
return len >= 0 ? len : null;
|
|
2651
|
+
return len === 'auto' || (len.pct >= 0 && !len.px) ? len : null;
|
|
2652
|
+
}
|
|
2653
|
+
/** How far a border image reaches out on a side: a multiple, or a
|
|
2654
|
+
* length; neither negative. */
|
|
2655
|
+
function imageOutsetOf(part, ctx) {
|
|
2656
|
+
const n = parseNumber(part);
|
|
2657
|
+
if (n !== null)
|
|
2658
|
+
return n >= 0 ? { times: n } : null;
|
|
2659
|
+
const len = parseLength(part, ctx);
|
|
2660
|
+
return typeof len === 'number' && len >= 0 ? len : null;
|
|
2661
|
+
}
|
|
2662
|
+
const IMAGE_REPEATS = new Set(['stretch', 'repeat', 'round', 'space']);
|
|
2663
|
+
/** `border-image-repeat`: one keyword for both ways, or two. */
|
|
2664
|
+
function imageRepeatOf(words) {
|
|
2665
|
+
if (!words.length || words.length > 2)
|
|
2666
|
+
return null;
|
|
2667
|
+
if (!words.every((w) => IMAGE_REPEATS.has(w)))
|
|
2668
|
+
return null;
|
|
2669
|
+
const [x, y = x] = words;
|
|
2670
|
+
return [x, y];
|
|
2671
|
+
}
|
|
2672
|
+
/**
|
|
2673
|
+
* The `border-image` shorthand: the image, the slices — with a width after
|
|
2674
|
+
* a slash and an outset after a second — and the repeat, in any order; each
|
|
2675
|
+
* at most once, and what it does not name reset (CSS Backgrounds 3, 6.8).
|
|
2676
|
+
*/
|
|
2677
|
+
function readBorderImage(value, ctx) {
|
|
2678
|
+
const parts = splitValue(value).flatMap(splitSlash);
|
|
2679
|
+
const image = { ...NO_BORDER_IMAGE };
|
|
2680
|
+
let seen = 0;
|
|
2681
|
+
const once = (bit) => {
|
|
2682
|
+
if (seen & bit)
|
|
2683
|
+
return false;
|
|
2684
|
+
seen |= bit;
|
|
2685
|
+
return true;
|
|
2686
|
+
};
|
|
2687
|
+
const isSlice = (p) => p.toLowerCase() === 'fill' || sliceOf(p) !== null;
|
|
2688
|
+
for (let i = 0; i < parts.length;) {
|
|
2689
|
+
const part = parts[i];
|
|
2690
|
+
const v = part.toLowerCase();
|
|
2691
|
+
if (v === 'none' || v.startsWith('url(') || IMAGE_FUNCTION.test(v)) {
|
|
2692
|
+
const source = backgroundImageOf(part, ctx);
|
|
2693
|
+
if (source === undefined || !once(1))
|
|
2694
|
+
return null;
|
|
2695
|
+
image.source = source;
|
|
2696
|
+
i += 1;
|
|
2697
|
+
}
|
|
2698
|
+
else if (IMAGE_REPEATS.has(v)) {
|
|
2699
|
+
let end = i + 1;
|
|
2700
|
+
if (end < parts.length && IMAGE_REPEATS.has(parts[end].toLowerCase())) {
|
|
2701
|
+
end += 1;
|
|
2702
|
+
}
|
|
2703
|
+
const repeat = imageRepeatOf(parts.slice(i, end).map((p) => p.toLowerCase()));
|
|
2704
|
+
if (!repeat || !once(2))
|
|
2705
|
+
return null;
|
|
2706
|
+
image.repeat = repeat;
|
|
2707
|
+
i = end;
|
|
2708
|
+
}
|
|
2709
|
+
else if (isSlice(part)) {
|
|
2710
|
+
let end = i;
|
|
2711
|
+
while (end < parts.length && isSlice(parts[end]))
|
|
2712
|
+
end += 1;
|
|
2713
|
+
const slice = borderImageSlice(parts.slice(i, end));
|
|
2714
|
+
if (!slice || !once(4))
|
|
2715
|
+
return null;
|
|
2716
|
+
image.slice = slice.slice;
|
|
2717
|
+
image.fill = slice.fill;
|
|
2718
|
+
i = end;
|
|
2719
|
+
if (parts[i] !== '/')
|
|
2720
|
+
continue;
|
|
2721
|
+
// `/ width`, `/ width / outset` or `/ / outset`
|
|
2722
|
+
i += 1;
|
|
2723
|
+
end = i;
|
|
2724
|
+
while (end < parts.length && imageWidthOf(parts[end], ctx) !== null) {
|
|
2725
|
+
end += 1;
|
|
2726
|
+
}
|
|
2727
|
+
if (end > i) {
|
|
2728
|
+
const width = sidesOf(parts.slice(i, end), (p) => imageWidthOf(p, ctx));
|
|
2729
|
+
if (!width)
|
|
2730
|
+
return null;
|
|
2731
|
+
image.width = width;
|
|
2732
|
+
}
|
|
2733
|
+
const widened = end > i;
|
|
2734
|
+
i = end;
|
|
2735
|
+
if (parts[i] !== '/') {
|
|
2736
|
+
if (!widened)
|
|
2737
|
+
return null;
|
|
2738
|
+
continue;
|
|
2739
|
+
}
|
|
2740
|
+
i += 1;
|
|
2741
|
+
end = i;
|
|
2742
|
+
while (end < parts.length && imageOutsetOf(parts[end], ctx) !== null) {
|
|
2743
|
+
end += 1;
|
|
2744
|
+
}
|
|
2745
|
+
const outset = sidesOf(parts.slice(i, end), (p) => imageOutsetOf(p, ctx));
|
|
2746
|
+
if (!outset)
|
|
2747
|
+
return null;
|
|
2748
|
+
image.outset = outset;
|
|
2749
|
+
i = end;
|
|
2750
|
+
}
|
|
2751
|
+
else {
|
|
2752
|
+
return null;
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
return image;
|
|
2756
|
+
}
|
|
2757
|
+
function readRepeat(words) {
|
|
2758
|
+
if (words.length === 1) {
|
|
2759
|
+
const [w] = words;
|
|
2760
|
+
if (w === 'repeat-x')
|
|
2761
|
+
return repeatPair('repeat', 'no-repeat');
|
|
2762
|
+
if (w === 'repeat-y')
|
|
2763
|
+
return repeatPair('no-repeat', 'repeat');
|
|
2764
|
+
return isRepeatMode(w) ? repeatPair(w, w) : null;
|
|
2765
|
+
}
|
|
2766
|
+
if (words.length !== 2)
|
|
2767
|
+
return null;
|
|
2768
|
+
const [x, y] = words;
|
|
2769
|
+
return isRepeatMode(x) && isRepeatMode(y) ? repeatPair(x, y) : null;
|
|
2770
|
+
}
|
|
2771
|
+
const REPEATS = new Set(['repeat', 'space', 'round', 'no-repeat']);
|
|
2772
|
+
function isRepeatMode(word) {
|
|
2773
|
+
return REPEATS.has(word);
|
|
2774
|
+
}
|
|
2775
|
+
const REPEAT_PAIRS = new Map();
|
|
2776
|
+
/** The one pair for each way of repeating. */
|
|
2777
|
+
function repeatPair(x, y) {
|
|
2778
|
+
const key = `${x} ${y}`;
|
|
2779
|
+
let pair = REPEAT_PAIRS.get(key);
|
|
2780
|
+
if (!pair) {
|
|
2781
|
+
pair = Object.freeze([x, y]);
|
|
2782
|
+
REPEAT_PAIRS.set(key, pair);
|
|
2783
|
+
}
|
|
2784
|
+
return pair;
|
|
2785
|
+
}
|
|
2786
|
+
const REPEAT = repeatPair('repeat', 'repeat');
|
|
885
2787
|
function applyBorderShorthand(style, name, value, ctx) {
|
|
886
2788
|
const sides = name === 'border'
|
|
887
2789
|
? ['Top', 'Right', 'Bottom', 'Left']
|
|
@@ -891,30 +2793,66 @@ function applyBorderShorthand(style, name, value, ctx) {
|
|
|
891
2793
|
// `border: none` and `border: 0` both mean "no border", and neither names
|
|
892
2794
|
// all three components — so the shorthand resets all three first, which is
|
|
893
2795
|
// what the spec says and what an author relies on to undo a UA border.
|
|
894
|
-
let width =
|
|
2796
|
+
let width = BORDER_WIDTH_KEYWORDS.medium * ctx.scale;
|
|
895
2797
|
let borderStyle = 'none';
|
|
896
2798
|
let color = 'currentColor';
|
|
2799
|
+
// each of the three at most once (`<line-width> || <line-style> ||
|
|
2800
|
+
// <color>`): `red solid 16px red` is no border, where the second colour
|
|
2801
|
+
// was taken for the first
|
|
2802
|
+
let seen = 0;
|
|
897
2803
|
for (const part of splitValue(value)) {
|
|
898
2804
|
const v = part.toLowerCase();
|
|
899
2805
|
if (BORDER_STYLES.has(v)) {
|
|
2806
|
+
if (seen & 1)
|
|
2807
|
+
return;
|
|
2808
|
+
seen |= 1;
|
|
900
2809
|
borderStyle = v;
|
|
901
2810
|
continue;
|
|
902
2811
|
}
|
|
903
2812
|
const w = borderWidth(part, ctx);
|
|
904
2813
|
if (w !== null) {
|
|
2814
|
+
if (seen & 2)
|
|
2815
|
+
return;
|
|
2816
|
+
seen |= 2;
|
|
905
2817
|
width = w;
|
|
906
2818
|
continue;
|
|
907
2819
|
}
|
|
908
2820
|
const c = parseColor(part);
|
|
909
|
-
if (c !== null)
|
|
2821
|
+
if (c !== null) {
|
|
2822
|
+
if (seen & 4)
|
|
2823
|
+
return;
|
|
2824
|
+
seen |= 4;
|
|
910
2825
|
color = c;
|
|
2826
|
+
continue;
|
|
2827
|
+
}
|
|
2828
|
+
// a negative width makes the whole shorthand invalid
|
|
2829
|
+
const len = parseLength(part, ctx);
|
|
2830
|
+
if (typeof len === 'number' && len < 0)
|
|
2831
|
+
return;
|
|
2832
|
+
}
|
|
2833
|
+
// A width with no style is kept rather than zeroed — the layout draws
|
|
2834
|
+
// nothing for `none` — so a later `border-style` alone finds it.
|
|
2835
|
+
if (name === 'border') {
|
|
2836
|
+
// named, not built: preflight's `border: 0 solid` is on every element,
|
|
2837
|
+
// and twelve stores through a computed name were most of its cost
|
|
2838
|
+
style.borderTopWidth = width;
|
|
2839
|
+
style.borderRightWidth = width;
|
|
2840
|
+
style.borderBottomWidth = width;
|
|
2841
|
+
style.borderLeftWidth = width;
|
|
2842
|
+
style.borderTopStyle = borderStyle;
|
|
2843
|
+
style.borderRightStyle = borderStyle;
|
|
2844
|
+
style.borderBottomStyle = borderStyle;
|
|
2845
|
+
style.borderLeftStyle = borderStyle;
|
|
2846
|
+
if (color !== null) {
|
|
2847
|
+
style.borderTopColor = color;
|
|
2848
|
+
style.borderRightColor = color;
|
|
2849
|
+
style.borderBottomColor = color;
|
|
2850
|
+
style.borderLeftColor = color;
|
|
2851
|
+
}
|
|
2852
|
+
return;
|
|
911
2853
|
}
|
|
912
|
-
// A shorthand with a style but no width takes the initial `medium`; one
|
|
913
|
-
// with neither paints nothing, so the width is irrelevant.
|
|
914
|
-
const effective = borderStyle === 'none' || borderStyle === 'hidden' ? 0 : (width ?? 3);
|
|
915
2854
|
for (const side of sides) {
|
|
916
|
-
style[`border${side}Width`] =
|
|
917
|
-
effective;
|
|
2855
|
+
style[`border${side}Width`] = width;
|
|
918
2856
|
style[`border${side}Style`] =
|
|
919
2857
|
borderStyle;
|
|
920
2858
|
if (color !== null)
|
|
@@ -922,53 +2860,533 @@ function applyBorderShorthand(style, name, value, ctx) {
|
|
|
922
2860
|
color;
|
|
923
2861
|
}
|
|
924
2862
|
}
|
|
2863
|
+
/** A font family name: quoted, or a sequence of identifiers. */
|
|
2864
|
+
function isFamilyName(name) {
|
|
2865
|
+
const n = name.trim();
|
|
2866
|
+
if (n[0] === '"' || n[0] === "'")
|
|
2867
|
+
return true;
|
|
2868
|
+
if (!n)
|
|
2869
|
+
return false;
|
|
2870
|
+
for (const word of n.split(/\s+/)) {
|
|
2871
|
+
if (!startsIdent(word, 0) || readIdent(word, 0).end !== word.length) {
|
|
2872
|
+
return false;
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
return true;
|
|
2876
|
+
}
|
|
925
2877
|
function applyBackgroundShorthand(style, value, ctx) {
|
|
926
|
-
//
|
|
927
|
-
//
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
2878
|
+
// A layer a comma group, top first: each has to be a layer, and only the
|
|
2879
|
+
// last, which is painted against the box, may have a colour, or the
|
|
2880
|
+
// declaration is none
|
|
2881
|
+
const texts = splitCommas(value);
|
|
2882
|
+
const layers = [];
|
|
2883
|
+
for (let k = 0; k < texts.length; k += 1) {
|
|
2884
|
+
const layer = readBackgroundLayer(texts[k], ctx, k === texts.length - 1);
|
|
2885
|
+
if (!layer)
|
|
2886
|
+
return;
|
|
2887
|
+
layers.push(layer);
|
|
2888
|
+
}
|
|
2889
|
+
if (!layers.length)
|
|
2890
|
+
return;
|
|
2891
|
+
const [top] = layers;
|
|
2892
|
+
style.backgroundClipText = false;
|
|
2893
|
+
style.backgroundColor = layers[layers.length - 1].color;
|
|
2894
|
+
style.backgroundImage = top.image;
|
|
2895
|
+
style.backgroundGradient = top.gradient;
|
|
2896
|
+
style.backgroundRepeat = top.repeat;
|
|
2897
|
+
style.backgroundSize = top.size;
|
|
2898
|
+
style.backgroundAttachment = top.attachment;
|
|
2899
|
+
style.backgroundOrigin = top.origin;
|
|
2900
|
+
style.backgroundClip = top.clip;
|
|
2901
|
+
[style.backgroundPositionX, style.backgroundPositionY] = top.position ?? [
|
|
2902
|
+
0, 0,
|
|
2903
|
+
];
|
|
2904
|
+
const many = layers.length > 1;
|
|
2905
|
+
style.backgroundImages = many
|
|
2906
|
+
? layers.map((l) => l.image ?? l.gradient)
|
|
2907
|
+
: null;
|
|
2908
|
+
style.backgroundRepeats = many ? layers.map((l) => l.repeat) : null;
|
|
2909
|
+
style.backgroundSizes = many ? layers.map((l) => l.size) : null;
|
|
2910
|
+
style.backgroundAttachments = many ? layers.map((l) => l.attachment) : null;
|
|
2911
|
+
style.backgroundOrigins = many ? layers.map((l) => l.origin) : null;
|
|
2912
|
+
style.backgroundClips = many ? layers.map((l) => l.clip) : null;
|
|
2913
|
+
style.backgroundPositions = many
|
|
2914
|
+
? layers.map((l) => l.position ?? [0, 0])
|
|
2915
|
+
: null;
|
|
2916
|
+
}
|
|
2917
|
+
/** One layer's `background-image`: a url, a gradient — none where it is
|
|
2918
|
+
* one this does not draw, as though there were none — or none; undefined
|
|
2919
|
+
* where it is not an image at all. */
|
|
2920
|
+
function backgroundImageOf(text, ctx) {
|
|
2921
|
+
const v = text.trim();
|
|
2922
|
+
if (IMAGE_FUNCTION.test(v.toLowerCase()))
|
|
2923
|
+
return parseLinearGradient(v, ctx);
|
|
2924
|
+
return parseUrl(v);
|
|
2925
|
+
}
|
|
2926
|
+
/** A background property's values, one a comma-separated layer, each read
|
|
2927
|
+
* by `read`; null where one is not a value, which drops the declaration. */
|
|
2928
|
+
function layerValues(value, read) {
|
|
2929
|
+
const out = [];
|
|
2930
|
+
for (const part of splitCommas(value)) {
|
|
2931
|
+
const v = read(part.trim());
|
|
2932
|
+
if (v === null)
|
|
2933
|
+
return null;
|
|
2934
|
+
out.push(v);
|
|
2935
|
+
}
|
|
2936
|
+
return out.length ? out : null;
|
|
2937
|
+
}
|
|
2938
|
+
/**
|
|
2939
|
+
* One layer of the `background` shorthand, read whole before anything is
|
|
2940
|
+
* set: each part at most once, and a token that is none of them — a
|
|
2941
|
+
* string, `red\;`, a second colour — makes the declaration invalid, and it
|
|
2942
|
+
* is dropped rather than resetting the background it meant to replace
|
|
2943
|
+
* (CSS 2.1 4.2). What CSS3 adds is read too, so that a declaration a
|
|
2944
|
+
* browser keeps is kept: `/ cover` after the position, `space` and `round`,
|
|
2945
|
+
* a gradient — drawn as nothing, over the layer's colour.
|
|
2946
|
+
*/
|
|
2947
|
+
function readBackgroundLayer(text, ctx, last) {
|
|
2948
|
+
const parts = splitValue(text).flatMap(splitSlash);
|
|
2949
|
+
const layer = {
|
|
2950
|
+
color: null,
|
|
2951
|
+
image: null,
|
|
2952
|
+
gradient: null,
|
|
2953
|
+
repeat: REPEAT,
|
|
2954
|
+
size: 'auto',
|
|
2955
|
+
attachment: 'scroll',
|
|
2956
|
+
origin: 'padding-box',
|
|
2957
|
+
clip: 'border-box',
|
|
2958
|
+
position: null,
|
|
2959
|
+
};
|
|
2960
|
+
let seen = 0;
|
|
2961
|
+
const once = (bit) => {
|
|
2962
|
+
if (seen & bit)
|
|
2963
|
+
return false;
|
|
2964
|
+
seen |= bit;
|
|
2965
|
+
return true;
|
|
2966
|
+
};
|
|
2967
|
+
let boxes = 0;
|
|
2968
|
+
for (let i = 0; i < parts.length;) {
|
|
2969
|
+
const part = parts[i];
|
|
935
2970
|
const v = part.toLowerCase();
|
|
936
|
-
if (v.startsWith('url(')) {
|
|
937
|
-
|
|
938
|
-
|
|
2971
|
+
if (v === 'none' || v.startsWith('url(') || IMAGE_FUNCTION.test(v)) {
|
|
2972
|
+
if (!once(1))
|
|
2973
|
+
return null;
|
|
2974
|
+
if (v.startsWith('url(')) {
|
|
2975
|
+
const url = parseUrl(part);
|
|
2976
|
+
if (url === undefined)
|
|
2977
|
+
return null;
|
|
2978
|
+
layer.image = url;
|
|
2979
|
+
}
|
|
2980
|
+
else if (v !== 'none')
|
|
2981
|
+
layer.gradient = parseLinearGradient(part, ctx);
|
|
2982
|
+
i += 1;
|
|
939
2983
|
}
|
|
940
|
-
if (v === 'repeat' ||
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
v
|
|
944
|
-
|
|
945
|
-
|
|
2984
|
+
else if (v === 'repeat-x' || v === 'repeat-y' || REPEATS.has(v)) {
|
|
2985
|
+
if (!once(2))
|
|
2986
|
+
return null;
|
|
2987
|
+
const pair = REPEATS.has(v) && REPEATS.has(parts[i + 1]?.toLowerCase());
|
|
2988
|
+
const repeat = readRepeat(pair ? [v, parts[i + 1].toLowerCase()] : [v]);
|
|
2989
|
+
if (!repeat)
|
|
2990
|
+
return null;
|
|
2991
|
+
layer.repeat = repeat;
|
|
2992
|
+
i += pair ? 2 : 1;
|
|
946
2993
|
}
|
|
947
|
-
if (v === '
|
|
948
|
-
|
|
949
|
-
|
|
2994
|
+
else if (v === 'scroll' || v === 'fixed' || v === 'local') {
|
|
2995
|
+
if (!once(4))
|
|
2996
|
+
return null;
|
|
2997
|
+
layer.attachment = v;
|
|
2998
|
+
i += 1;
|
|
950
2999
|
}
|
|
951
|
-
if (v
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
3000
|
+
else if (isVisualBox(v)) {
|
|
3001
|
+
// one box is both the origin and the clip, and a second the clip
|
|
3002
|
+
if ((boxes += 1) > 2)
|
|
3003
|
+
return null;
|
|
3004
|
+
if (boxes === 1)
|
|
3005
|
+
layer.origin = v;
|
|
3006
|
+
layer.clip = v;
|
|
3007
|
+
i += 1;
|
|
958
3008
|
}
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
3009
|
+
else if (isPositionPart(v, ctx)) {
|
|
3010
|
+
if (!once(8))
|
|
3011
|
+
return null;
|
|
3012
|
+
let end = i;
|
|
3013
|
+
while (end < parts.length &&
|
|
3014
|
+
isPositionPart(parts[end].toLowerCase(), ctx)) {
|
|
3015
|
+
end += 1;
|
|
3016
|
+
}
|
|
3017
|
+
layer.position = positionPair(parts.slice(i, end), ctx);
|
|
3018
|
+
if (!layer.position)
|
|
3019
|
+
return null;
|
|
3020
|
+
i = end;
|
|
3021
|
+
if (parts[i] !== '/')
|
|
3022
|
+
continue;
|
|
3023
|
+
// `/ <size>`: `cover`, `contain`, or one or two lengths
|
|
3024
|
+
const first = parts[i + 1]?.toLowerCase();
|
|
3025
|
+
if (first === 'cover' || first === 'contain') {
|
|
3026
|
+
layer.size = first;
|
|
3027
|
+
i += 2;
|
|
3028
|
+
}
|
|
3029
|
+
else if (first !== undefined && isSizePart(first, ctx)) {
|
|
3030
|
+
const two = i + 2 < parts.length && isSizePart(parts[i + 2].toLowerCase(), ctx);
|
|
3031
|
+
layer.size =
|
|
3032
|
+
backgroundSizeOf(parts.slice(i + 1, i + (two ? 3 : 2)), ctx) ??
|
|
3033
|
+
'auto';
|
|
3034
|
+
i += two ? 3 : 2;
|
|
3035
|
+
}
|
|
3036
|
+
else {
|
|
3037
|
+
return null;
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
3040
|
+
else {
|
|
3041
|
+
const color = parseColor(part);
|
|
3042
|
+
if (color === null || !last || !once(16))
|
|
3043
|
+
return null;
|
|
3044
|
+
layer.color = color;
|
|
3045
|
+
i += 1;
|
|
3046
|
+
}
|
|
3047
|
+
}
|
|
3048
|
+
return layer;
|
|
3049
|
+
}
|
|
3050
|
+
/**
|
|
3051
|
+
* A `linear-gradient()`, or null for any other image function and for one
|
|
3052
|
+
* that is no gradient: a direction by angle, by side or by corner, with a
|
|
3053
|
+
* colour interpolation method (`in oklab`, which Tailwind 4 writes) read and
|
|
3054
|
+
* not honoured, and at least two stops, each with no position, one, or two;
|
|
3055
|
+
* a bare percentage between stops, a hint, is passed over.
|
|
3056
|
+
*/
|
|
3057
|
+
function parseLinearGradient(text, ctx) {
|
|
3058
|
+
const m = /^linear-gradient\((.*)\)$/is.exec(text.trim());
|
|
3059
|
+
if (!m)
|
|
3060
|
+
return null;
|
|
3061
|
+
const args = splitCommas(m[1]).map((a) => a.trim());
|
|
3062
|
+
const out = { angle: Math.PI, corner: null, stops: [] };
|
|
3063
|
+
const first = (args[0] ?? '')
|
|
3064
|
+
.toLowerCase()
|
|
3065
|
+
.replace(/\bin\s+[a-z-]+(?:\s+(?:shorter|longer|increasing|decreasing)\s+hue)?/, '')
|
|
3066
|
+
.trim();
|
|
3067
|
+
let from = 0;
|
|
3068
|
+
if (!first || first.startsWith('to ')) {
|
|
3069
|
+
from = 1;
|
|
3070
|
+
const sides = first.split(/\s+/).slice(1).sort();
|
|
3071
|
+
const side = sides.join(' ');
|
|
3072
|
+
const SIDES = {
|
|
3073
|
+
top: 0,
|
|
3074
|
+
right: Math.PI / 2,
|
|
3075
|
+
bottom: Math.PI,
|
|
3076
|
+
left: (3 * Math.PI) / 2,
|
|
3077
|
+
};
|
|
3078
|
+
if (sides.length === 1 && side in SIDES)
|
|
3079
|
+
out.angle = SIDES[side];
|
|
3080
|
+
else if (sides.length === 2) {
|
|
3081
|
+
const [a, b] = sides;
|
|
3082
|
+
const vertical = a === 'bottom' || a === 'top' ? a : b;
|
|
3083
|
+
const horizontal = a === 'left' || a === 'right' ? a : b;
|
|
3084
|
+
if (vertical === horizontal)
|
|
3085
|
+
return null;
|
|
3086
|
+
out.corner = `${vertical} ${horizontal}`;
|
|
3087
|
+
}
|
|
3088
|
+
else if (sides.length)
|
|
3089
|
+
return null;
|
|
3090
|
+
}
|
|
3091
|
+
else {
|
|
3092
|
+
const angle = /^(-?\d*\.?\d+)(deg|rad|grad|turn)?$/.exec(first);
|
|
3093
|
+
if (angle) {
|
|
3094
|
+
const n = Number(angle[1]);
|
|
3095
|
+
const unit = angle[2] ?? (n === 0 ? 'deg' : '');
|
|
3096
|
+
const turns = {
|
|
3097
|
+
deg: 1 / 360,
|
|
3098
|
+
rad: 1 / (2 * Math.PI),
|
|
3099
|
+
grad: 1 / 400,
|
|
3100
|
+
turn: 1,
|
|
3101
|
+
};
|
|
3102
|
+
if (!(unit in turns))
|
|
3103
|
+
return null;
|
|
3104
|
+
out.angle = n * turns[unit] * 2 * Math.PI;
|
|
3105
|
+
from = 1;
|
|
3106
|
+
}
|
|
3107
|
+
}
|
|
3108
|
+
for (const arg of args.slice(from)) {
|
|
3109
|
+
const parts = splitValue(arg);
|
|
3110
|
+
const color = parseColor(parts[0] ?? '');
|
|
3111
|
+
if (color === null) {
|
|
3112
|
+
// a hint, the midpoint of the transition, which this does not move
|
|
3113
|
+
if (parts.length === 1 && parseLength(parts[0], ctx) !== null)
|
|
3114
|
+
continue;
|
|
3115
|
+
return null;
|
|
3116
|
+
}
|
|
3117
|
+
if (parts.length > 3)
|
|
3118
|
+
return null;
|
|
3119
|
+
const at = parts.slice(1).map((p) => parseLength(p, ctx));
|
|
3120
|
+
if (at.some((p) => p === null || p === AUTO))
|
|
3121
|
+
return null;
|
|
3122
|
+
if (!at.length)
|
|
3123
|
+
out.stops.push({ color, at: null });
|
|
3124
|
+
for (const p of at)
|
|
3125
|
+
out.stops.push({ color, at: p });
|
|
3126
|
+
}
|
|
3127
|
+
return out.stops.length >= 2 ? out : null;
|
|
3128
|
+
}
|
|
3129
|
+
/** The images CSS3 has beyond `url()`, which a layer may name: a linear
|
|
3130
|
+
* gradient is drawn, and the rest draw as nothing. */
|
|
3131
|
+
const IMAGE_FUNCTION = /^(?:-(?:webkit|moz|o|ms)-)?(?:(?:repeating-)?(?:linear|radial|conic)-gradient|gradient|image-set|cross-fade|element|paint)\(/;
|
|
3132
|
+
function isPositionPart(word, ctx) {
|
|
3133
|
+
return (word in HORIZONTAL || word in VERTICAL || positionLength(word, ctx) !== null);
|
|
3134
|
+
}
|
|
3135
|
+
function isSizePart(word, ctx) {
|
|
3136
|
+
if (word === 'auto')
|
|
3137
|
+
return true;
|
|
3138
|
+
const len = positionLength(word, ctx);
|
|
3139
|
+
if (len === null || len === AUTO)
|
|
3140
|
+
return false;
|
|
3141
|
+
return typeof len === 'number' ? len >= 0 : len.pct >= 0;
|
|
3142
|
+
}
|
|
3143
|
+
/** What each `white-space` keeps of the white space (CSS Text 4, 3.1). */
|
|
3144
|
+
const COLLAPSE_OF = {
|
|
3145
|
+
normal: 'collapse',
|
|
3146
|
+
nowrap: 'collapse',
|
|
3147
|
+
pre: 'preserve',
|
|
3148
|
+
'pre-wrap': 'preserve',
|
|
3149
|
+
'pre-line': 'preserve-breaks',
|
|
3150
|
+
};
|
|
3151
|
+
function wrapsIn(whiteSpace) {
|
|
3152
|
+
return whiteSpace !== 'nowrap' && whiteSpace !== 'pre';
|
|
3153
|
+
}
|
|
3154
|
+
/** The `white-space` of a collapse and a wrap. Kept breaks and no wrapping
|
|
3155
|
+
* have no keyword of their own, and keep their breaks, as `pre-line`. */
|
|
3156
|
+
function whiteSpaceOf(collapse, wrap) {
|
|
3157
|
+
if (collapse === 'collapse')
|
|
3158
|
+
return wrap ? 'normal' : 'nowrap';
|
|
3159
|
+
if (collapse === 'preserve')
|
|
3160
|
+
return wrap ? 'pre-wrap' : 'pre';
|
|
3161
|
+
return 'pre-line';
|
|
3162
|
+
}
|
|
3163
|
+
/**
|
|
3164
|
+
* The translation a `transform` makes: the sum of its translate functions'
|
|
3165
|
+
* and its matrices' — `translate(-50%, -50%)`, Tailwind 3's `translate(
|
|
3166
|
+
* var(--tw-translate-x), var(--tw-translate-y)) rotate(…) skewX(…) …` —
|
|
3167
|
+
* with the functions that rotate, scale, skew or add perspective read and
|
|
3168
|
+
* not drawn. Null for `none`, and undefined for what is no transform list,
|
|
3169
|
+
* which drops the declaration.
|
|
3170
|
+
*/
|
|
3171
|
+
function transformTranslation(value, ctx) {
|
|
3172
|
+
if (value.trim().toLowerCase() === 'none')
|
|
3173
|
+
return null;
|
|
3174
|
+
let x = 0;
|
|
3175
|
+
let y = 0;
|
|
3176
|
+
const parts = splitValue(value);
|
|
3177
|
+
if (!parts.length)
|
|
3178
|
+
return undefined;
|
|
3179
|
+
for (const part of parts) {
|
|
3180
|
+
const m = /^([a-z0-9]+)\((.*)\)$/is.exec(part.trim());
|
|
3181
|
+
if (!m)
|
|
3182
|
+
return undefined;
|
|
3183
|
+
const name = m[1].toLowerCase();
|
|
3184
|
+
const args = splitCommas(m[2]).map((a) => a.trim());
|
|
3185
|
+
let dx = 0;
|
|
3186
|
+
let dy = 0;
|
|
3187
|
+
if (name === 'translate' || name === 'translate3d') {
|
|
3188
|
+
dx = parseLength(args[0] ?? '', ctx);
|
|
3189
|
+
dy = args[1] === undefined ? 0 : parseLength(args[1], ctx);
|
|
3190
|
+
}
|
|
3191
|
+
else if (name === 'translatex')
|
|
3192
|
+
dx = parseLength(args[0] ?? '', ctx);
|
|
3193
|
+
else if (name === 'translatey')
|
|
3194
|
+
dy = parseLength(args[0] ?? '', ctx);
|
|
3195
|
+
else if (name === 'matrix' || name === 'matrix3d') {
|
|
3196
|
+
// the translation is the last column's
|
|
3197
|
+
const n = args.map(Number);
|
|
3198
|
+
if (n.length !== (name === 'matrix' ? 6 : 16) || n.some(isNaN)) {
|
|
3199
|
+
return undefined;
|
|
3200
|
+
}
|
|
3201
|
+
[dx, dy] = name === 'matrix' ? [n[4], n[5]] : [n[12], n[13]];
|
|
3202
|
+
}
|
|
3203
|
+
else if (!TRANSFORMS.has(name))
|
|
3204
|
+
return undefined;
|
|
3205
|
+
if (dx === null || dx === AUTO || dy === null || dy === AUTO) {
|
|
3206
|
+
return undefined;
|
|
3207
|
+
}
|
|
3208
|
+
const sx = addLen(x, dx);
|
|
3209
|
+
const sy = addLen(y, dy);
|
|
3210
|
+
if (sx === null || sy === null)
|
|
3211
|
+
return undefined;
|
|
3212
|
+
x = sx;
|
|
3213
|
+
y = sy;
|
|
3214
|
+
}
|
|
3215
|
+
return [x, y];
|
|
3216
|
+
}
|
|
3217
|
+
/** The transform functions read and not drawn. */
|
|
3218
|
+
const TRANSFORMS = new Set([
|
|
3219
|
+
'translatez',
|
|
3220
|
+
'rotate',
|
|
3221
|
+
'rotatex',
|
|
3222
|
+
'rotatey',
|
|
3223
|
+
'rotatez',
|
|
3224
|
+
'rotate3d',
|
|
3225
|
+
'scale',
|
|
3226
|
+
'scalex',
|
|
3227
|
+
'scaley',
|
|
3228
|
+
'scalez',
|
|
3229
|
+
'scale3d',
|
|
3230
|
+
'skew',
|
|
3231
|
+
'skewx',
|
|
3232
|
+
'skewy',
|
|
3233
|
+
'perspective',
|
|
3234
|
+
]);
|
|
3235
|
+
/** Two lengths added, a percentage and a length kept apart; null where
|
|
3236
|
+
* one is a `min()` or a `max()`, which a sum cannot be kept of. */
|
|
3237
|
+
function addLen(a, b) {
|
|
3238
|
+
if (a === AUTO || b === AUTO)
|
|
3239
|
+
return null;
|
|
3240
|
+
if (typeof a === 'number' && typeof b === 'number')
|
|
3241
|
+
return a + b;
|
|
3242
|
+
if (a === 0)
|
|
3243
|
+
return b;
|
|
3244
|
+
if (b === 0)
|
|
3245
|
+
return a;
|
|
3246
|
+
const pa = typeof a === 'number' ? { pct: 0, px: a } : a;
|
|
3247
|
+
const pb = typeof b === 'number' ? { pct: 0, px: b } : b;
|
|
3248
|
+
if (pa.of || pb.of)
|
|
3249
|
+
return null;
|
|
3250
|
+
return { pct: pa.pct + pb.pct, px: (pa.px ?? 0) + (pb.px ?? 0) };
|
|
3251
|
+
}
|
|
3252
|
+
/** An intrinsic size keyword, with the prefixes browsers still read, or
|
|
3253
|
+
* `fit-content()` of a length or a percentage (CSS Sizing 3, 3.1) — a
|
|
3254
|
+
* width's, where a height's is its content height as the keyword is;
|
|
3255
|
+
* null for anything else. */
|
|
3256
|
+
function contentSizeOf(value, ctx, inline) {
|
|
3257
|
+
const word = value.trim().toLowerCase();
|
|
3258
|
+
if (word === 'stretch' ||
|
|
3259
|
+
word === '-webkit-fill-available' ||
|
|
3260
|
+
word === '-moz-available') {
|
|
3261
|
+
return 'stretch';
|
|
3262
|
+
}
|
|
3263
|
+
const v = word.replace(/^-(?:webkit|moz)-/, '');
|
|
3264
|
+
if (v === 'min-content' || v === 'max-content' || v === 'fit-content') {
|
|
3265
|
+
return v;
|
|
3266
|
+
}
|
|
3267
|
+
const m = /^fit-content\((.*)\)$/s.exec(v);
|
|
3268
|
+
if (!m)
|
|
3269
|
+
return null;
|
|
3270
|
+
const fit = parseLength(m[1], ctx);
|
|
3271
|
+
if (fit === null || fit === AUTO || !notNegative(fit))
|
|
3272
|
+
return null;
|
|
3273
|
+
return inline ? { fit } : 'fit-content';
|
|
3274
|
+
}
|
|
3275
|
+
/** A `background-size`: `cover`, `contain`, `auto`, or one or two of a
|
|
3276
|
+
* length, a percentage and `auto`, the second `auto` where there is none;
|
|
3277
|
+
* null for what is none of these. */
|
|
3278
|
+
function backgroundSizeOf(parts, ctx) {
|
|
3279
|
+
const words = parts.map((p) => p.toLowerCase());
|
|
3280
|
+
if (words.length === 1 && (words[0] === 'cover' || words[0] === 'contain')) {
|
|
3281
|
+
return words[0];
|
|
3282
|
+
}
|
|
3283
|
+
if (!words.length || words.length > 2)
|
|
3284
|
+
return null;
|
|
3285
|
+
if (!words.every((w) => isSizePart(w, ctx)))
|
|
3286
|
+
return null;
|
|
3287
|
+
const axis = (w) => {
|
|
3288
|
+
if (w === undefined || w === 'auto')
|
|
3289
|
+
return 'auto';
|
|
3290
|
+
const len = positionLength(w, ctx);
|
|
3291
|
+
return len === null || len === AUTO ? 'auto' : len;
|
|
3292
|
+
};
|
|
3293
|
+
const width = axis(words[0]);
|
|
3294
|
+
const height = axis(words[1]);
|
|
3295
|
+
return width === 'auto' && height === 'auto' ? 'auto' : [width, height];
|
|
3296
|
+
}
|
|
3297
|
+
/** `center/cover` is three tokens, as `center / cover` is. */
|
|
3298
|
+
function splitSlash(part) {
|
|
3299
|
+
if (!part.includes('/') || part.includes('('))
|
|
3300
|
+
return [part];
|
|
3301
|
+
return part.split(/(\/)/).filter(Boolean);
|
|
3302
|
+
}
|
|
3303
|
+
/** Each `font-variant` keyword: the longhand it belongs to and the features
|
|
3304
|
+
* it stands for (CSS Fonts 3, 6). */
|
|
3305
|
+
const VARIANTS = {
|
|
3306
|
+
'lining-nums': ['numeric', 'lnum=1'],
|
|
3307
|
+
'oldstyle-nums': ['numeric', 'onum=1'],
|
|
3308
|
+
'proportional-nums': ['numeric', 'pnum=1'],
|
|
3309
|
+
'tabular-nums': ['numeric', 'tnum=1'],
|
|
3310
|
+
'diagonal-fractions': ['numeric', 'frac=1'],
|
|
3311
|
+
'stacked-fractions': ['numeric', 'afrc=1'],
|
|
3312
|
+
ordinal: ['numeric', 'ordn=1'],
|
|
3313
|
+
'slashed-zero': ['numeric', 'zero=1'],
|
|
3314
|
+
'small-caps': ['caps', 'smcp=1'],
|
|
3315
|
+
'all-small-caps': ['caps', 'c2sc=1,smcp=1'],
|
|
3316
|
+
'petite-caps': ['caps', 'pcap=1'],
|
|
3317
|
+
'all-petite-caps': ['caps', 'c2pc=1,pcap=1'],
|
|
3318
|
+
unicase: ['caps', 'unic=1'],
|
|
3319
|
+
'titling-caps': ['caps', 'titl=1'],
|
|
3320
|
+
'common-ligatures': ['ligatures', 'liga=1,clig=1'],
|
|
3321
|
+
'no-common-ligatures': ['ligatures', 'liga=0,clig=0'],
|
|
3322
|
+
'discretionary-ligatures': ['ligatures', 'dlig=1'],
|
|
3323
|
+
'no-discretionary-ligatures': ['ligatures', 'dlig=0'],
|
|
3324
|
+
'historical-ligatures': ['ligatures', 'hlig=1'],
|
|
3325
|
+
'no-historical-ligatures': ['ligatures', 'hlig=0'],
|
|
3326
|
+
contextual: ['ligatures', 'calt=1'],
|
|
3327
|
+
'no-contextual': ['ligatures', 'calt=0'],
|
|
3328
|
+
sub: ['position', 'subs=1'],
|
|
3329
|
+
super: ['position', 'sups=1'],
|
|
3330
|
+
};
|
|
3331
|
+
const VARIANT_FIELDS = {
|
|
3332
|
+
numeric: 'fontVariantNumeric',
|
|
3333
|
+
caps: 'fontVariantCaps',
|
|
3334
|
+
ligatures: 'fontVariantLigatures',
|
|
3335
|
+
position: 'fontVariantPosition',
|
|
3336
|
+
};
|
|
3337
|
+
const ALL_VARIANTS = [
|
|
3338
|
+
'numeric',
|
|
3339
|
+
'caps',
|
|
3340
|
+
'ligatures',
|
|
3341
|
+
'position',
|
|
3342
|
+
];
|
|
3343
|
+
/**
|
|
3344
|
+
* A `font-variant` longhand's value, or the shorthand's (`kind` null), as
|
|
3345
|
+
* the fields it sets; null for a value it does not take. The shorthand
|
|
3346
|
+
* sets all four, the ones it names nothing of back to `normal`.
|
|
3347
|
+
*/
|
|
3348
|
+
function parseFontVariant(value, kind) {
|
|
3349
|
+
const words = value.trim().toLowerCase().split(/\s+/);
|
|
3350
|
+
const kinds = kind ? [kind] : ALL_VARIANTS;
|
|
3351
|
+
const named = {};
|
|
3352
|
+
if (words.length === 1 && words[0] === 'normal') {
|
|
3353
|
+
// every longhand the value sets goes back to none
|
|
3354
|
+
}
|
|
3355
|
+
else if (words.length === 1 &&
|
|
3356
|
+
words[0] === 'none' &&
|
|
3357
|
+
(kind === null || kind === 'ligatures')) {
|
|
3358
|
+
named.ligatures = ['liga=0,clig=0,dlig=0,hlig=0,calt=0'];
|
|
3359
|
+
}
|
|
3360
|
+
else {
|
|
3361
|
+
for (const word of words) {
|
|
3362
|
+
const known = VARIANTS[word];
|
|
3363
|
+
if (!known || !kinds.includes(known[0]))
|
|
3364
|
+
return null;
|
|
3365
|
+
(named[known[0]] ??= []).push(known[1]);
|
|
963
3366
|
}
|
|
964
|
-
const pos = backgroundPosition(part, ctx);
|
|
965
|
-
if (pos !== null)
|
|
966
|
-
positions.push(pos);
|
|
967
3367
|
}
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
3368
|
+
const fields = {};
|
|
3369
|
+
for (const k of kinds) {
|
|
3370
|
+
fields[VARIANT_FIELDS[k]] = (named[k] ?? []).join(',');
|
|
3371
|
+
}
|
|
3372
|
+
return fields;
|
|
3373
|
+
}
|
|
3374
|
+
/** `font-feature-settings`: `"tnum", "liga" 0, "ss01" on` as `tnum=1,…`,
|
|
3375
|
+
* '' for `normal`, and null for a value that is neither. */
|
|
3376
|
+
function parseFeatureSettings(value) {
|
|
3377
|
+
const v = value.trim();
|
|
3378
|
+
if (v.toLowerCase() === 'normal')
|
|
3379
|
+
return '';
|
|
3380
|
+
const pairs = [];
|
|
3381
|
+
for (const part of splitCommas(v)) {
|
|
3382
|
+
const m = /^(["'])([\x20-\x7e]{4})\1(?:\s+(on|off|\d+))?$/i.exec(part.trim());
|
|
3383
|
+
if (!m)
|
|
3384
|
+
return null;
|
|
3385
|
+
const setting = (m[3] ?? 'on').toLowerCase();
|
|
3386
|
+
const n = setting === 'on' ? 1 : setting === 'off' ? 0 : Number(setting);
|
|
3387
|
+
pairs.push(`${m[2]}=${n}`);
|
|
971
3388
|
}
|
|
3389
|
+
return pairs.join(',');
|
|
972
3390
|
}
|
|
973
3391
|
function applyFontShorthand(style, parent, value, ctx) {
|
|
974
3392
|
// The `font: caption | menu | …` system forms name a font this renderer
|
|
@@ -976,26 +3394,58 @@ function applyFontShorthand(style, parent, value, ctx) {
|
|
|
976
3394
|
const parts = splitValue(value);
|
|
977
3395
|
if (parts.length < 2)
|
|
978
3396
|
return;
|
|
3397
|
+
let fontStyle = 'normal';
|
|
3398
|
+
let weight = null;
|
|
3399
|
+
let smallCaps = false;
|
|
979
3400
|
let i = 0;
|
|
980
3401
|
for (; i < parts.length; i += 1) {
|
|
981
3402
|
const v = parts[i].toLowerCase();
|
|
982
3403
|
if (v === 'italic' || v === 'oblique')
|
|
983
|
-
|
|
3404
|
+
fontStyle = v;
|
|
984
3405
|
else if (v === 'bold' ||
|
|
985
3406
|
v === 'bolder' ||
|
|
986
3407
|
v === 'lighter' ||
|
|
987
3408
|
/^\d{3}$/.test(v)) {
|
|
988
|
-
|
|
3409
|
+
weight = v;
|
|
989
3410
|
}
|
|
990
|
-
else if (v === '
|
|
3411
|
+
else if (v === 'small-caps')
|
|
3412
|
+
smallCaps = true;
|
|
3413
|
+
else if (v === 'normal')
|
|
991
3414
|
continue;
|
|
992
3415
|
else
|
|
993
3416
|
break;
|
|
994
3417
|
}
|
|
995
|
-
|
|
996
|
-
|
|
3418
|
+
// `12px/1.5`, or the same with space round the slash
|
|
3419
|
+
let [sizeText, lineText] = (parts[i] ?? '').split('/');
|
|
3420
|
+
let next = i + 1;
|
|
3421
|
+
if (lineText === '' || (lineText === undefined && parts[next]?.[0] === '/')) {
|
|
3422
|
+
const slash = lineText === undefined ? parts[next++].slice(1) : '';
|
|
3423
|
+
lineText = slash || parts[next++];
|
|
3424
|
+
}
|
|
3425
|
+
const family = parts.slice(next).join(' ');
|
|
3426
|
+
// A size and a family, or the value is not a font and the declaration is
|
|
3427
|
+
// dropped whole, as CSS drops any value it cannot read — a line height
|
|
3428
|
+
// below nought included: `font: 4em/-2em serif` set the text at 4em.
|
|
3429
|
+
const size = keywordFontSize(sizeText ?? '', parent.fontSize, ctx.rem) ??
|
|
3430
|
+
parseLength(sizeText ?? '', { ...ctx, em: parent.fontSize });
|
|
3431
|
+
if (!family || size === null || size === AUTO)
|
|
3432
|
+
return;
|
|
3433
|
+
if (lineText && negativeLength(lineText, ctx))
|
|
997
3434
|
return;
|
|
998
|
-
|
|
3435
|
+
// What the shorthand does not name goes back to its initial value rather
|
|
3436
|
+
// than keeping the parent's (CSS 2.1 15.8): `p { font: 12pt serif }`
|
|
3437
|
+
// inside a document set at `20px/1em` has lines of normal height, not 20px.
|
|
3438
|
+
style.fontStyle = fontStyle;
|
|
3439
|
+
style.fontWeight = weight ? parseWeight(weight, parent.fontWeight) : 400;
|
|
3440
|
+
// the variants too, save the small capitals it may name; not the
|
|
3441
|
+
// features `font-feature-settings` sets, which the shorthand leaves
|
|
3442
|
+
style.fontVariantNumeric = '';
|
|
3443
|
+
style.fontVariantCaps = smallCaps ? 'smcp=1' : '';
|
|
3444
|
+
style.fontVariantLigatures = '';
|
|
3445
|
+
style.fontVariantPosition = '';
|
|
3446
|
+
style.fontKerning = '';
|
|
3447
|
+
style.lineHeight = 'normal';
|
|
3448
|
+
style.lineHeightIsLength = false;
|
|
999
3449
|
applyDeclaration(style, parent, 'font-size', sizeText, ctx);
|
|
1000
3450
|
if (lineText) {
|
|
1001
3451
|
applyDeclaration(style, parent, 'line-height', lineText, {
|
|
@@ -1003,9 +3453,17 @@ function applyFontShorthand(style, parent, value, ctx) {
|
|
|
1003
3453
|
em: style.fontSize,
|
|
1004
3454
|
});
|
|
1005
3455
|
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
3456
|
+
applyDeclaration(style, parent, 'font-family', family, ctx);
|
|
3457
|
+
}
|
|
3458
|
+
/** Whether a line height is a number or a length below nought. */
|
|
3459
|
+
function negativeLength(value, ctx) {
|
|
3460
|
+
const n = parseNumber(value);
|
|
3461
|
+
if (n !== null)
|
|
3462
|
+
return n < 0;
|
|
3463
|
+
const len = parseLength(value, ctx);
|
|
3464
|
+
if (typeof len === 'number')
|
|
3465
|
+
return len < 0;
|
|
3466
|
+
return !!len && typeof len === 'object' && resolve(len, 100) < 0;
|
|
1009
3467
|
}
|
|
1010
3468
|
function applyFlexShorthand(style, value, ctx) {
|
|
1011
3469
|
const v = value.trim().toLowerCase();
|
|
@@ -1025,6 +3483,10 @@ function applyFlexShorthand(style, value, ctx) {
|
|
|
1025
3483
|
const numbers = [];
|
|
1026
3484
|
let basis = null;
|
|
1027
3485
|
for (const part of parts) {
|
|
3486
|
+
if (part.toLowerCase() === 'content') {
|
|
3487
|
+
basis = 'content';
|
|
3488
|
+
continue;
|
|
3489
|
+
}
|
|
1028
3490
|
const n = parseNumber(part);
|
|
1029
3491
|
if (n !== null && numbers.length < 2 && !part.includes('%')) {
|
|
1030
3492
|
numbers.push(n);
|
|
@@ -1042,11 +3504,644 @@ function applyFlexShorthand(style, value, ctx) {
|
|
|
1042
3504
|
style.flexShrink = numbers.length > 1 ? numbers[1] : 1;
|
|
1043
3505
|
style.flexBasis = basis ?? (numbers.length ? 0 : AUTO);
|
|
1044
3506
|
}
|
|
3507
|
+
// --- grid ------------------------------------------------------------------
|
|
3508
|
+
const AUTO_TRACK = { min: 'auto', max: 'auto' };
|
|
3509
|
+
/** One track's sizing, or null for what is not one. */
|
|
3510
|
+
function parseGridTrack(token, ctx) {
|
|
3511
|
+
const t = token.trim().toLowerCase();
|
|
3512
|
+
if (!t)
|
|
3513
|
+
return null;
|
|
3514
|
+
if (t === 'auto')
|
|
3515
|
+
return AUTO_TRACK;
|
|
3516
|
+
if (t === 'min-content' || t === 'max-content')
|
|
3517
|
+
return { min: t, max: t };
|
|
3518
|
+
const fr = /^(\d*\.?\d+)fr$/.exec(t);
|
|
3519
|
+
if (fr)
|
|
3520
|
+
return { min: 'auto', max: { fr: Number(fr[1]) } };
|
|
3521
|
+
const fn = /^(minmax|fit-content)\((.*)\)$/s.exec(t);
|
|
3522
|
+
if (fn) {
|
|
3523
|
+
if (fn[1] === 'fit-content') {
|
|
3524
|
+
const fit = parseLength(fn[2].trim(), ctx);
|
|
3525
|
+
if (fit === null || fit === AUTO)
|
|
3526
|
+
return null;
|
|
3527
|
+
if (typeof fit === 'number' && fit < 0)
|
|
3528
|
+
return null;
|
|
3529
|
+
return { min: AUTO, max: { fit } };
|
|
3530
|
+
}
|
|
3531
|
+
const [a, b] = splitCommas(fn[2]).map((p) => p.trim());
|
|
3532
|
+
if (a === undefined || b === undefined)
|
|
3533
|
+
return null;
|
|
3534
|
+
const min = parseGridTrack(a, ctx);
|
|
3535
|
+
const max = parseGridTrack(b, ctx);
|
|
3536
|
+
// an `fr` is no minimum
|
|
3537
|
+
if (!min ||
|
|
3538
|
+
!max ||
|
|
3539
|
+
(typeof min.max === 'object' && min.max !== null && 'fr' in min.max))
|
|
3540
|
+
return null;
|
|
3541
|
+
return { min: min.min, max: max.max };
|
|
3542
|
+
}
|
|
3543
|
+
const len = parseLength(token.trim(), ctx);
|
|
3544
|
+
if (len === null || len === AUTO)
|
|
3545
|
+
return null;
|
|
3546
|
+
return { min: len, max: len };
|
|
3547
|
+
}
|
|
3548
|
+
/**
|
|
3549
|
+
* A track list's parts: a bracketed group of line names as one part, a
|
|
3550
|
+
* quoted string as one, and a size or a function as one — a group of two
|
|
3551
|
+
* names was two parts, and the second was no size. Undefined where a
|
|
3552
|
+
* bracket or a quote is not closed.
|
|
3553
|
+
*/
|
|
3554
|
+
function trackParts(value) {
|
|
3555
|
+
const out = [];
|
|
3556
|
+
const text = value.trim();
|
|
3557
|
+
let i = 0;
|
|
3558
|
+
while (i < text.length) {
|
|
3559
|
+
const ch = text[i];
|
|
3560
|
+
if (/\s/.test(ch)) {
|
|
3561
|
+
i += 1;
|
|
3562
|
+
continue;
|
|
3563
|
+
}
|
|
3564
|
+
const close = ch === '[' ? ']' : ch === '"' || ch === "'" ? ch : null;
|
|
3565
|
+
if (close) {
|
|
3566
|
+
const end = text.indexOf(close, i + 1);
|
|
3567
|
+
if (end < 0)
|
|
3568
|
+
return undefined;
|
|
3569
|
+
out.push(text.slice(i, end + 1));
|
|
3570
|
+
i = end + 1;
|
|
3571
|
+
continue;
|
|
3572
|
+
}
|
|
3573
|
+
let depth = 0;
|
|
3574
|
+
let j = i;
|
|
3575
|
+
for (; j < text.length; j += 1) {
|
|
3576
|
+
const c = text[j];
|
|
3577
|
+
if (c === '(')
|
|
3578
|
+
depth += 1;
|
|
3579
|
+
else if (c === ')')
|
|
3580
|
+
depth -= 1;
|
|
3581
|
+
else if (depth === 0 && /[\s["']/.test(c))
|
|
3582
|
+
break;
|
|
3583
|
+
}
|
|
3584
|
+
out.push(text.slice(i, j));
|
|
3585
|
+
i = j;
|
|
3586
|
+
}
|
|
3587
|
+
return out;
|
|
3588
|
+
}
|
|
3589
|
+
/** The names in a bracketed group of line names, or undefined for a part
|
|
3590
|
+
* that is not one or names what no line may be called. */
|
|
3591
|
+
function lineNames(part) {
|
|
3592
|
+
if (!part.startsWith('[') || !part.endsWith(']'))
|
|
3593
|
+
return undefined;
|
|
3594
|
+
const names = part.slice(1, -1).trim().split(/\s+/).filter(Boolean);
|
|
3595
|
+
return names.every(isLineName) ? names : undefined;
|
|
3596
|
+
}
|
|
3597
|
+
/** A `<custom-ident>` a grid line may take: not `span` nor `auto`. */
|
|
3598
|
+
function isLineName(word) {
|
|
3599
|
+
return (/^-?[a-zA-Z_\u0080-\uffff][\w\u0080-\uffff-]*$/.test(word) &&
|
|
3600
|
+
!/^(span|auto|inherit|initial|unset|default)$/i.test(word));
|
|
3601
|
+
}
|
|
3602
|
+
/**
|
|
3603
|
+
* A track list (CSS Grid 1, 7.2): lengths, `fr`s, `auto`, `minmax()`,
|
|
3604
|
+
* `fit-content()`, `repeat()` with a count or with `auto-fill`/`auto-fit`,
|
|
3605
|
+
* whose count the width decides, and the names of the lines between them.
|
|
3606
|
+
* Undefined for a list that is none.
|
|
3607
|
+
*/
|
|
3608
|
+
function parseGridTemplate(value, ctx) {
|
|
3609
|
+
const out = { tracks: [], names: [[]], repeat: null };
|
|
3610
|
+
// where a group of names goes: the line after the last track, or the
|
|
3611
|
+
// line after an auto repeat until a track follows it
|
|
3612
|
+
let pending = null;
|
|
3613
|
+
const read = (parts, into, names, top) => {
|
|
3614
|
+
for (const part of parts) {
|
|
3615
|
+
if (part.startsWith('[')) {
|
|
3616
|
+
const group = lineNames(part);
|
|
3617
|
+
if (!group)
|
|
3618
|
+
return false;
|
|
3619
|
+
(top && pending ? pending : names[names.length - 1]).push(...group);
|
|
3620
|
+
continue;
|
|
3621
|
+
}
|
|
3622
|
+
const repeat = /^repeat\((.*)\)$/is.exec(part);
|
|
3623
|
+
if (repeat) {
|
|
3624
|
+
const [count, ...rest] = splitCommas(repeat[1]);
|
|
3625
|
+
const inner = [];
|
|
3626
|
+
const innerNames = [[]];
|
|
3627
|
+
const innerParts = trackParts(rest.join(','));
|
|
3628
|
+
if (!innerParts ||
|
|
3629
|
+
!read(innerParts, inner, innerNames, false) ||
|
|
3630
|
+
!inner.length) {
|
|
3631
|
+
return false;
|
|
3632
|
+
}
|
|
3633
|
+
const n = count.trim().toLowerCase();
|
|
3634
|
+
if (n === 'auto-fill' || n === 'auto-fit') {
|
|
3635
|
+
if (out.repeat || !top)
|
|
3636
|
+
return false;
|
|
3637
|
+
out.repeat = {
|
|
3638
|
+
at: into.length,
|
|
3639
|
+
tracks: inner,
|
|
3640
|
+
names: innerNames,
|
|
3641
|
+
after: [],
|
|
3642
|
+
fit: n === 'auto-fit',
|
|
3643
|
+
};
|
|
3644
|
+
pending = out.repeat.after;
|
|
3645
|
+
continue;
|
|
3646
|
+
}
|
|
3647
|
+
const times = Number(n);
|
|
3648
|
+
if (!Number.isInteger(times) || times < 1 || times > 1000)
|
|
3649
|
+
return false;
|
|
3650
|
+
for (let i = 0; i < times; i += 1) {
|
|
3651
|
+
names[names.length - 1].push(...innerNames[0]);
|
|
3652
|
+
for (let t = 0; t < inner.length; t += 1) {
|
|
3653
|
+
into.push(inner[t]);
|
|
3654
|
+
names.push([...innerNames[t + 1]]);
|
|
3655
|
+
}
|
|
3656
|
+
}
|
|
3657
|
+
if (top)
|
|
3658
|
+
pending = null;
|
|
3659
|
+
continue;
|
|
3660
|
+
}
|
|
3661
|
+
const track = parseGridTrack(part, ctx);
|
|
3662
|
+
if (!track)
|
|
3663
|
+
return false;
|
|
3664
|
+
into.push(track);
|
|
3665
|
+
names.push([]);
|
|
3666
|
+
if (top)
|
|
3667
|
+
pending = null;
|
|
3668
|
+
}
|
|
3669
|
+
return true;
|
|
3670
|
+
};
|
|
3671
|
+
const parts = trackParts(value);
|
|
3672
|
+
if (!parts || !read(parts, out.tracks, out.names, true))
|
|
3673
|
+
return undefined;
|
|
3674
|
+
if (!out.tracks.length && !out.repeat)
|
|
3675
|
+
return undefined;
|
|
3676
|
+
return out;
|
|
3677
|
+
}
|
|
3678
|
+
/**
|
|
3679
|
+
* `grid-template-areas`' strings (CSS Grid 1, 7.3): a row each, a cell to a
|
|
3680
|
+
* word or a run of dots, every row as wide as the first and every name a
|
|
3681
|
+
* rectangle. Undefined where they are not.
|
|
3682
|
+
*/
|
|
3683
|
+
function parseAreas(strings) {
|
|
3684
|
+
const grid = [];
|
|
3685
|
+
for (const quoted of strings) {
|
|
3686
|
+
const cells = [];
|
|
3687
|
+
const text = quoted.slice(1, -1);
|
|
3688
|
+
const re = /\s*(?:(\.+)|([\w\u0080-\uffff-]+)|(\S))/g;
|
|
3689
|
+
let m;
|
|
3690
|
+
while ((m = re.exec(text)) && m[0].trim()) {
|
|
3691
|
+
if (m[3])
|
|
3692
|
+
return undefined;
|
|
3693
|
+
cells.push(m[1] ? null : m[2]);
|
|
3694
|
+
}
|
|
3695
|
+
if (!cells.length)
|
|
3696
|
+
return undefined;
|
|
3697
|
+
if (grid.length && cells.length !== grid[0].length)
|
|
3698
|
+
return undefined;
|
|
3699
|
+
grid.push(cells);
|
|
3700
|
+
}
|
|
3701
|
+
const areas = new Map();
|
|
3702
|
+
grid.forEach((cells, r) => cells.forEach((name, c) => {
|
|
3703
|
+
if (name === null)
|
|
3704
|
+
return;
|
|
3705
|
+
const area = areas.get(name);
|
|
3706
|
+
if (!area)
|
|
3707
|
+
areas.set(name, { rows: [r, r + 1], columns: [c, c + 1] });
|
|
3708
|
+
else {
|
|
3709
|
+
area.rows[1] = Math.max(area.rows[1], r + 1);
|
|
3710
|
+
area.columns[1] = Math.max(area.columns[1], c + 1);
|
|
3711
|
+
}
|
|
3712
|
+
}));
|
|
3713
|
+
// each name fills the rectangle its first and last cells make, and
|
|
3714
|
+
// nothing else
|
|
3715
|
+
let cellsNamed = 0;
|
|
3716
|
+
for (const cells of grid)
|
|
3717
|
+
for (const name of cells)
|
|
3718
|
+
if (name)
|
|
3719
|
+
cellsNamed += 1;
|
|
3720
|
+
let rectangles = 0;
|
|
3721
|
+
for (const [name, { rows, columns }] of areas) {
|
|
3722
|
+
for (let r = rows[0]; r < rows[1]; r += 1) {
|
|
3723
|
+
for (let c = columns[0]; c < columns[1]; c += 1) {
|
|
3724
|
+
if (grid[r][c] !== name)
|
|
3725
|
+
return undefined;
|
|
3726
|
+
}
|
|
3727
|
+
}
|
|
3728
|
+
rectangles += (rows[1] - rows[0]) * (columns[1] - columns[0]);
|
|
3729
|
+
}
|
|
3730
|
+
if (rectangles !== cellsNamed)
|
|
3731
|
+
return undefined;
|
|
3732
|
+
return { rows: grid.length, columns: grid[0].length, areas };
|
|
3733
|
+
}
|
|
3734
|
+
/**
|
|
3735
|
+
* `grid-template`'s rows and columns either side of a slash (7.4): the
|
|
3736
|
+
* rows as track list, or as the strings of `grid-template-areas`, each
|
|
3737
|
+
* string's row the size after it or `auto`, with the names of the lines
|
|
3738
|
+
* before and after it. Undefined for no value.
|
|
3739
|
+
*/
|
|
3740
|
+
function parseTemplateShorthand(value, ctx) {
|
|
3741
|
+
const parts = splitTopLevelSlash(value);
|
|
3742
|
+
if (parts.length !== 2)
|
|
3743
|
+
return undefined;
|
|
3744
|
+
const columnsNone = parts[1].trim().toLowerCase() === 'none';
|
|
3745
|
+
const columns = columnsNone ? null : parseGridTemplate(parts[1], ctx);
|
|
3746
|
+
if (columns === undefined)
|
|
3747
|
+
return undefined;
|
|
3748
|
+
if (!/["']/.test(parts[0])) {
|
|
3749
|
+
const rows = parts[0].trim().toLowerCase() === 'none'
|
|
3750
|
+
? null
|
|
3751
|
+
: parseGridTemplate(parts[0], ctx);
|
|
3752
|
+
return rows === undefined ? undefined : { rows, columns, areas: null };
|
|
3753
|
+
}
|
|
3754
|
+
// the strings' form takes no `repeat()` for its columns
|
|
3755
|
+
if (columns?.repeat)
|
|
3756
|
+
return undefined;
|
|
3757
|
+
const pieces = trackParts(parts[0]);
|
|
3758
|
+
if (!pieces)
|
|
3759
|
+
return undefined;
|
|
3760
|
+
const tracks = [];
|
|
3761
|
+
const names = [[]];
|
|
3762
|
+
const strings = [];
|
|
3763
|
+
// after a string, and before its size, if it has one
|
|
3764
|
+
let open = false;
|
|
3765
|
+
for (const piece of pieces) {
|
|
3766
|
+
if (piece.startsWith('[')) {
|
|
3767
|
+
const group = lineNames(piece);
|
|
3768
|
+
if (!group)
|
|
3769
|
+
return undefined;
|
|
3770
|
+
if (open) {
|
|
3771
|
+
tracks.push(AUTO_TRACK);
|
|
3772
|
+
names.push([]);
|
|
3773
|
+
open = false;
|
|
3774
|
+
}
|
|
3775
|
+
names[names.length - 1].push(...group);
|
|
3776
|
+
continue;
|
|
3777
|
+
}
|
|
3778
|
+
if (/^["']/.test(piece)) {
|
|
3779
|
+
if (open) {
|
|
3780
|
+
tracks.push(AUTO_TRACK);
|
|
3781
|
+
names.push([]);
|
|
3782
|
+
}
|
|
3783
|
+
strings.push(piece);
|
|
3784
|
+
open = true;
|
|
3785
|
+
continue;
|
|
3786
|
+
}
|
|
3787
|
+
if (!open)
|
|
3788
|
+
return undefined;
|
|
3789
|
+
const track = parseGridTrack(piece, ctx);
|
|
3790
|
+
if (!track)
|
|
3791
|
+
return undefined;
|
|
3792
|
+
tracks.push(track);
|
|
3793
|
+
names.push([]);
|
|
3794
|
+
open = false;
|
|
3795
|
+
}
|
|
3796
|
+
if (open) {
|
|
3797
|
+
tracks.push(AUTO_TRACK);
|
|
3798
|
+
names.push([]);
|
|
3799
|
+
}
|
|
3800
|
+
const areas = parseAreas(strings);
|
|
3801
|
+
if (!areas)
|
|
3802
|
+
return undefined;
|
|
3803
|
+
if (columns && columns.tracks.length !== areas.columns)
|
|
3804
|
+
return undefined;
|
|
3805
|
+
return { rows: { tracks, names, repeat: null }, columns, areas };
|
|
3806
|
+
}
|
|
3807
|
+
/**
|
|
3808
|
+
* A grid line (CSS Grid 1, 8.3): `auto`; a number; a name; a number and a
|
|
3809
|
+
* name, in either order; or `span` with a number, a name or both.
|
|
3810
|
+
* Undefined for a value that is none of them.
|
|
3811
|
+
*/
|
|
3812
|
+
function parseGridLine(value) {
|
|
3813
|
+
if (value === undefined)
|
|
3814
|
+
return null;
|
|
3815
|
+
const words = value.trim().split(/\s+/).filter(Boolean);
|
|
3816
|
+
if (words.length === 1 && words[0].toLowerCase() === 'auto')
|
|
3817
|
+
return null;
|
|
3818
|
+
let span = false;
|
|
3819
|
+
let count = null;
|
|
3820
|
+
let name = null;
|
|
3821
|
+
for (const word of words) {
|
|
3822
|
+
if (word.toLowerCase() === 'span') {
|
|
3823
|
+
if (span)
|
|
3824
|
+
return undefined;
|
|
3825
|
+
span = true;
|
|
3826
|
+
}
|
|
3827
|
+
else if (/^[+-]?\d+$/.test(word)) {
|
|
3828
|
+
if (count !== null)
|
|
3829
|
+
return undefined;
|
|
3830
|
+
count = Number(word);
|
|
3831
|
+
}
|
|
3832
|
+
else if (isLineName(word)) {
|
|
3833
|
+
if (name !== null)
|
|
3834
|
+
return undefined;
|
|
3835
|
+
name = word;
|
|
3836
|
+
}
|
|
3837
|
+
else
|
|
3838
|
+
return undefined;
|
|
3839
|
+
}
|
|
3840
|
+
// `span` goes first or last, never between a number and a name
|
|
3841
|
+
if (span && words.length === 3 && words[1].toLowerCase() === 'span') {
|
|
3842
|
+
return undefined;
|
|
3843
|
+
}
|
|
3844
|
+
if (span) {
|
|
3845
|
+
if (count !== null && count < 1)
|
|
3846
|
+
return undefined;
|
|
3847
|
+
const out = { span: count ?? 1 };
|
|
3848
|
+
if (name !== null)
|
|
3849
|
+
out.name = name;
|
|
3850
|
+
return out;
|
|
3851
|
+
}
|
|
3852
|
+
if (count !== null) {
|
|
3853
|
+
if (count === 0)
|
|
3854
|
+
return undefined;
|
|
3855
|
+
return name === null ? { line: count } : { line: count, name };
|
|
3856
|
+
}
|
|
3857
|
+
return name === null ? undefined : { name };
|
|
3858
|
+
}
|
|
3859
|
+
/** A grid line that is a name alone, which a shorthand copies to the
|
|
3860
|
+
* line it leaves out; auto for any other. */
|
|
3861
|
+
function named(line) {
|
|
3862
|
+
return line && 'name' in line && !('line' in line) && !('span' in line)
|
|
3863
|
+
? line
|
|
3864
|
+
: null;
|
|
3865
|
+
}
|
|
3866
|
+
/** A value's parts either side of a top-level `/`. */
|
|
3867
|
+
function splitTopLevelSlash(value) {
|
|
3868
|
+
const out = [];
|
|
3869
|
+
let depth = 0;
|
|
3870
|
+
let start = 0;
|
|
3871
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
3872
|
+
const c = value[i];
|
|
3873
|
+
if (c === '(')
|
|
3874
|
+
depth += 1;
|
|
3875
|
+
else if (c === ')')
|
|
3876
|
+
depth -= 1;
|
|
3877
|
+
else if (c === '/' && depth === 0) {
|
|
3878
|
+
out.push(value.slice(start, i).trim());
|
|
3879
|
+
start = i + 1;
|
|
3880
|
+
}
|
|
3881
|
+
}
|
|
3882
|
+
out.push(value.slice(start).trim());
|
|
3883
|
+
return out;
|
|
3884
|
+
}
|
|
3885
|
+
// --- logical properties ----------------------------------------------------
|
|
3886
|
+
const OBJECT_FITS = new Set(['fill', 'contain', 'cover', 'none', 'scale-down']);
|
|
3887
|
+
/**
|
|
3888
|
+
* `aspect-ratio` (CSS Sizing 4, 5.1): `auto`, a ratio — one number, or
|
|
3889
|
+
* two with a `/` between — or both, in either order. Undefined where it is
|
|
3890
|
+
* none of that, and the declaration is dropped; a ratio with a nought in it
|
|
3891
|
+
* is none, and `auto` is what is left.
|
|
3892
|
+
*/
|
|
3893
|
+
function parseAspectRatio(value) {
|
|
3894
|
+
let auto = false;
|
|
3895
|
+
let rest = value.trim().toLowerCase();
|
|
3896
|
+
const word = /(?:^auto\s+|\s+auto$|^auto$)/.exec(rest);
|
|
3897
|
+
if (word) {
|
|
3898
|
+
auto = true;
|
|
3899
|
+
rest = rest.replace(word[0], '').trim();
|
|
3900
|
+
if (!rest)
|
|
3901
|
+
return null;
|
|
3902
|
+
}
|
|
3903
|
+
const m = /^(\d*\.?\d+(?:e[+-]?\d+)?)(?:\s*\/\s*(\d*\.?\d+(?:e[+-]?\d+)?))?$/.exec(rest);
|
|
3904
|
+
if (!m)
|
|
3905
|
+
return undefined;
|
|
3906
|
+
const w = Number(m[1]);
|
|
3907
|
+
const h = m[2] === undefined ? 1 : Number(m[2]);
|
|
3908
|
+
if (!(w > 0 && h > 0))
|
|
3909
|
+
return null;
|
|
3910
|
+
return { ratio: w / h, auto };
|
|
3911
|
+
}
|
|
3912
|
+
/**
|
|
3913
|
+
* A `box-shadow` (CSS Backgrounds 3, 7.1): `none`, or shadows front to
|
|
3914
|
+
* back, each two offsets and then a blur and a spread, with a colour and
|
|
3915
|
+
* `inset` on either side of the lengths. Undefined where the value is none
|
|
3916
|
+
* of that, and the declaration is dropped. A shadow no colour can be seen
|
|
3917
|
+
* in is left out — Tailwind writes four of them, `0 0 #0000`, under every
|
|
3918
|
+
* one it means.
|
|
3919
|
+
*/
|
|
3920
|
+
/** `box-shadow`'s shadows, or with `text` `text-shadow`'s, which have no
|
|
3921
|
+
* spread and are never inset (CSS Text Decoration 3, 4): null for none,
|
|
3922
|
+
* and undefined for a value that is not one. */
|
|
3923
|
+
function parseBoxShadow(value, ctx, text = false) {
|
|
3924
|
+
if (value.trim().toLowerCase() === 'none')
|
|
3925
|
+
return null;
|
|
3926
|
+
const out = [];
|
|
3927
|
+
for (const part of splitCommas(value)) {
|
|
3928
|
+
let inset = false;
|
|
3929
|
+
let color = null;
|
|
3930
|
+
const lengths = [];
|
|
3931
|
+
// the lengths are written together, with nothing between them
|
|
3932
|
+
let closed = false;
|
|
3933
|
+
for (const token of splitValue(part.trim())) {
|
|
3934
|
+
if (token.toLowerCase() === 'inset') {
|
|
3935
|
+
if (inset || text)
|
|
3936
|
+
return undefined;
|
|
3937
|
+
inset = true;
|
|
3938
|
+
closed = lengths.length > 0;
|
|
3939
|
+
continue;
|
|
3940
|
+
}
|
|
3941
|
+
const len = parseLength(token, ctx);
|
|
3942
|
+
if (typeof len === 'number') {
|
|
3943
|
+
if (closed || lengths.length === (text ? 3 : 4))
|
|
3944
|
+
return undefined;
|
|
3945
|
+
lengths.push(len);
|
|
3946
|
+
continue;
|
|
3947
|
+
}
|
|
3948
|
+
const c = parseColor(token);
|
|
3949
|
+
if (c === null || color !== null)
|
|
3950
|
+
return undefined;
|
|
3951
|
+
color = c;
|
|
3952
|
+
closed = lengths.length > 0;
|
|
3953
|
+
}
|
|
3954
|
+
if (lengths.length < 2)
|
|
3955
|
+
return undefined;
|
|
3956
|
+
const [x, y, blur = 0, spread = 0] = lengths;
|
|
3957
|
+
if (blur < 0)
|
|
3958
|
+
return undefined;
|
|
3959
|
+
const ink = color ?? 'currentColor';
|
|
3960
|
+
if (alphaOf(ink) === 0 || isTransparent(ink))
|
|
3961
|
+
continue;
|
|
3962
|
+
out.push({ x, y, blur, spread, color: ink, inset });
|
|
3963
|
+
}
|
|
3964
|
+
return out.length ? out : null;
|
|
3965
|
+
}
|
|
3966
|
+
/** A corner's radius: a length or a percentage, and neither negative nor
|
|
3967
|
+
* `auto`; null where it is none. */
|
|
3968
|
+
function radiusOf(text, ctx) {
|
|
3969
|
+
const len = parseLength(text, ctx);
|
|
3970
|
+
if (len === null || len === AUTO)
|
|
3971
|
+
return null;
|
|
3972
|
+
if (typeof len === 'number')
|
|
3973
|
+
return len < 0 ? null : len;
|
|
3974
|
+
return len.pct < 0 && !len.px && !len.of ? null : len;
|
|
3975
|
+
}
|
|
3976
|
+
/** Whether two corners' radii are the same values. */
|
|
3977
|
+
function sameRadii(a, b) {
|
|
3978
|
+
for (let i = 0; i < 4; i += 1) {
|
|
3979
|
+
const x = a[i];
|
|
3980
|
+
const y = b[i];
|
|
3981
|
+
if (x === y)
|
|
3982
|
+
continue;
|
|
3983
|
+
if (typeof x !== 'object' || typeof y !== 'object')
|
|
3984
|
+
return false;
|
|
3985
|
+
if (x.pct !== y.pct || x.px !== y.px || x.of || y.of)
|
|
3986
|
+
return false;
|
|
3987
|
+
}
|
|
3988
|
+
return true;
|
|
3989
|
+
}
|
|
3990
|
+
/** The four corners, in `border-radius`'s order. */
|
|
3991
|
+
const CORNERS = [
|
|
3992
|
+
'border-top-left-radius',
|
|
3993
|
+
'border-top-right-radius',
|
|
3994
|
+
'border-bottom-right-radius',
|
|
3995
|
+
'border-bottom-left-radius',
|
|
3996
|
+
];
|
|
3997
|
+
/**
|
|
3998
|
+
* CSS Logical Properties 1, in the horizontal writing mode `<Html>` lays
|
|
3999
|
+
* out: the inline axis is the line's, its start the left of a
|
|
4000
|
+
* left-to-right element and the right of a right-to-left one, and the block
|
|
4001
|
+
* axis runs down. Tailwind 4 writes its spacing in them — `px-4` is
|
|
4002
|
+
* `padding-inline`, `mx-auto` is `margin-inline: auto` — and every modern
|
|
4003
|
+
* reset writes some.
|
|
4004
|
+
*/
|
|
4005
|
+
const LOGICAL = (() => {
|
|
4006
|
+
const out = {};
|
|
4007
|
+
const length = (part, ctx) => parseLength(part, ctx) !== null;
|
|
4008
|
+
const groups = [
|
|
4009
|
+
['margin', (side) => `margin-${side}`, length],
|
|
4010
|
+
[
|
|
4011
|
+
'padding',
|
|
4012
|
+
(side) => `padding-${side}`,
|
|
4013
|
+
(part, ctx) => validPadding(parseLength(part, ctx)),
|
|
4014
|
+
],
|
|
4015
|
+
['inset', (side) => side, length],
|
|
4016
|
+
[
|
|
4017
|
+
'border-*-width',
|
|
4018
|
+
(side) => `border-${side}-width`,
|
|
4019
|
+
(part, ctx) => borderWidth(part, ctx) !== null,
|
|
4020
|
+
],
|
|
4021
|
+
[
|
|
4022
|
+
'border-*-style',
|
|
4023
|
+
(side) => `border-${side}-style`,
|
|
4024
|
+
(part) => BORDER_STYLES.has(part.toLowerCase()),
|
|
4025
|
+
],
|
|
4026
|
+
[
|
|
4027
|
+
'border-*-color',
|
|
4028
|
+
(side) => `border-${side}-color`,
|
|
4029
|
+
(part) => parseColor(part) !== null,
|
|
4030
|
+
],
|
|
4031
|
+
];
|
|
4032
|
+
for (const [group, physical, valid] of groups) {
|
|
4033
|
+
for (const axis of ['inline', 'block']) {
|
|
4034
|
+
const name = group.includes('*')
|
|
4035
|
+
? group.replace('*', axis)
|
|
4036
|
+
: `${group}-${axis}`;
|
|
4037
|
+
out[name] = { kind: 'axis', axis, same: false, physical, valid };
|
|
4038
|
+
for (const end of ['start', 'end']) {
|
|
4039
|
+
const side = `${axis}-${end}`;
|
|
4040
|
+
const longhand = group.includes('*')
|
|
4041
|
+
? group.replace('*', side)
|
|
4042
|
+
: `${group}-${side}`;
|
|
4043
|
+
out[longhand] = { kind: 'side', side, physical };
|
|
4044
|
+
}
|
|
4045
|
+
}
|
|
4046
|
+
}
|
|
4047
|
+
for (const axis of ['inline', 'block']) {
|
|
4048
|
+
out[`border-${axis}`] = {
|
|
4049
|
+
kind: 'axis',
|
|
4050
|
+
axis,
|
|
4051
|
+
same: true,
|
|
4052
|
+
physical: (side) => `border-${side}`,
|
|
4053
|
+
valid: () => true,
|
|
4054
|
+
};
|
|
4055
|
+
for (const end of ['start', 'end']) {
|
|
4056
|
+
out[`border-${axis}-${end}`] = {
|
|
4057
|
+
kind: 'side',
|
|
4058
|
+
side: `${axis}-${end}`,
|
|
4059
|
+
physical: (side) => `border-${side}`,
|
|
4060
|
+
};
|
|
4061
|
+
}
|
|
4062
|
+
}
|
|
4063
|
+
const sizes = [
|
|
4064
|
+
['inline-size', 'width'],
|
|
4065
|
+
['block-size', 'height'],
|
|
4066
|
+
['min-inline-size', 'min-width'],
|
|
4067
|
+
['min-block-size', 'min-height'],
|
|
4068
|
+
['max-inline-size', 'max-width'],
|
|
4069
|
+
['max-block-size', 'max-height'],
|
|
4070
|
+
];
|
|
4071
|
+
for (const [name, physical] of sizes)
|
|
4072
|
+
out[name] = { kind: 'size', physical };
|
|
4073
|
+
for (const block of ['start', 'end']) {
|
|
4074
|
+
for (const inline of ['start', 'end']) {
|
|
4075
|
+
out[`border-${block}-${inline}-radius`] = {
|
|
4076
|
+
kind: 'corner',
|
|
4077
|
+
block,
|
|
4078
|
+
inline,
|
|
4079
|
+
};
|
|
4080
|
+
}
|
|
4081
|
+
}
|
|
4082
|
+
return out;
|
|
4083
|
+
})();
|
|
4084
|
+
/** The physical side a logical one is, by the element's direction. */
|
|
4085
|
+
function physicalSide(side, rtl) {
|
|
4086
|
+
switch (side) {
|
|
4087
|
+
case 'inline-start':
|
|
4088
|
+
return rtl ? 'right' : 'left';
|
|
4089
|
+
case 'inline-end':
|
|
4090
|
+
return rtl ? 'left' : 'right';
|
|
4091
|
+
case 'block-start':
|
|
4092
|
+
return 'top';
|
|
4093
|
+
default:
|
|
4094
|
+
return 'bottom';
|
|
4095
|
+
}
|
|
4096
|
+
}
|
|
4097
|
+
/**
|
|
4098
|
+
* A logical declaration, as the physical ones it stands for. The direction
|
|
4099
|
+
* is the element's as far as the cascade has got: its parent's, or one it
|
|
4100
|
+
* set itself earlier in cascade order, which is where HTML's `dir` puts it.
|
|
4101
|
+
* A two-value shorthand with a part that is no value for its property is
|
|
4102
|
+
* none, and the declaration goes, as a physical shorthand's does.
|
|
4103
|
+
*/
|
|
4104
|
+
function applyLogical(style, parent, logical, value, ctx) {
|
|
4105
|
+
const rtl = style.direction === 'rtl';
|
|
4106
|
+
switch (logical.kind) {
|
|
4107
|
+
case 'side':
|
|
4108
|
+
applyDeclaration(style, parent, logical.physical(physicalSide(logical.side, rtl)), value, ctx);
|
|
4109
|
+
return;
|
|
4110
|
+
case 'size':
|
|
4111
|
+
applyDeclaration(style, parent, logical.physical, value, ctx);
|
|
4112
|
+
return;
|
|
4113
|
+
case 'corner': {
|
|
4114
|
+
const vertical = logical.block === 'start' ? 'top' : 'bottom';
|
|
4115
|
+
const left = (logical.inline === 'start') !== rtl;
|
|
4116
|
+
applyDeclaration(style, parent, `border-${vertical}-${left ? 'left' : 'right'}-radius`, value, ctx);
|
|
4117
|
+
return;
|
|
4118
|
+
}
|
|
4119
|
+
default: {
|
|
4120
|
+
const start = physicalSide(`${logical.axis}-start`, rtl);
|
|
4121
|
+
const end = physicalSide(`${logical.axis}-end`, rtl);
|
|
4122
|
+
let parts;
|
|
4123
|
+
if (logical.same || CSS_WIDE.has(value.toLowerCase())) {
|
|
4124
|
+
parts = [value, value];
|
|
4125
|
+
}
|
|
4126
|
+
else {
|
|
4127
|
+
parts = splitValue(value);
|
|
4128
|
+
if (parts.length === 1)
|
|
4129
|
+
parts.push(parts[0]);
|
|
4130
|
+
if (parts.length !== 2 || !parts.every((p) => logical.valid(p, ctx)))
|
|
4131
|
+
return;
|
|
4132
|
+
}
|
|
4133
|
+
applyDeclaration(style, parent, logical.physical(start), parts[0], ctx);
|
|
4134
|
+
applyDeclaration(style, parent, logical.physical(end), parts[1], ctx);
|
|
4135
|
+
}
|
|
4136
|
+
}
|
|
4137
|
+
}
|
|
4138
|
+
const CSS_WIDE = new Set(['inherit', 'initial', 'unset', 'revert']);
|
|
1045
4139
|
function camel(name) {
|
|
1046
4140
|
return name.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
1047
4141
|
}
|
|
1048
4142
|
const INHERITED_NAMES = new Set([
|
|
1049
4143
|
'color',
|
|
4144
|
+
'-webkit-text-fill-color',
|
|
1050
4145
|
'font',
|
|
1051
4146
|
'font-family',
|
|
1052
4147
|
'font-size',
|
|
@@ -1054,60 +4149,448 @@ const INHERITED_NAMES = new Set([
|
|
|
1054
4149
|
'font-style',
|
|
1055
4150
|
'line-height',
|
|
1056
4151
|
'text-align',
|
|
4152
|
+
'text-align-last',
|
|
4153
|
+
'text-justify',
|
|
4154
|
+
'block-ellipsis',
|
|
1057
4155
|
'text-indent',
|
|
1058
4156
|
'text-transform',
|
|
1059
4157
|
'letter-spacing',
|
|
1060
4158
|
'word-spacing',
|
|
1061
4159
|
'white-space',
|
|
4160
|
+
'overflow-wrap',
|
|
4161
|
+
'word-wrap',
|
|
4162
|
+
'word-break',
|
|
4163
|
+
'line-break',
|
|
4164
|
+
'white-space-collapse',
|
|
4165
|
+
'text-wrap',
|
|
4166
|
+
'text-wrap-mode',
|
|
4167
|
+
'text-wrap-style',
|
|
1062
4168
|
'direction',
|
|
1063
4169
|
'visibility',
|
|
1064
4170
|
'list-style',
|
|
1065
4171
|
'list-style-type',
|
|
1066
4172
|
'list-style-position',
|
|
4173
|
+
'list-style-image',
|
|
1067
4174
|
'cursor',
|
|
1068
4175
|
'border-collapse',
|
|
1069
4176
|
'border-spacing',
|
|
4177
|
+
'empty-cells',
|
|
4178
|
+
'quotes',
|
|
1070
4179
|
]);
|
|
1071
|
-
function isInherited(name) {
|
|
4180
|
+
export function isInherited(name) {
|
|
1072
4181
|
return INHERITED_NAMES.has(name);
|
|
1073
4182
|
}
|
|
1074
4183
|
/** `prop: inherit` — take the parent's computed value for whatever longhands
|
|
1075
4184
|
* the property names. Shorthands copy each of their longhands. */
|
|
4185
|
+
/** A property back to its initial value: what a declaration whose `var()`
|
|
4186
|
+
* has no value comes to, where the property is not inherited. */
|
|
4187
|
+
export function initialOne(style, initial, name) {
|
|
4188
|
+
const keys = INHERIT_TARGETS[name];
|
|
4189
|
+
if (!keys)
|
|
4190
|
+
return;
|
|
4191
|
+
for (const key of keys) {
|
|
4192
|
+
style[key] = initial[key];
|
|
4193
|
+
}
|
|
4194
|
+
}
|
|
1076
4195
|
function inheritOne(style, parent, name) {
|
|
1077
4196
|
const keys = INHERIT_TARGETS[name];
|
|
1078
4197
|
if (!keys)
|
|
1079
4198
|
return;
|
|
4199
|
+
// A border colour left to `currentColor` inherits as the keyword and
|
|
4200
|
+
// takes the child's own colour (CSS Color 4) — not the parent's colour,
|
|
4201
|
+
// which is what CSS 2.1 computed it to.
|
|
1080
4202
|
for (const key of keys) {
|
|
1081
4203
|
style[key] = parent[key];
|
|
1082
4204
|
}
|
|
1083
4205
|
}
|
|
4206
|
+
const SIDES = ['Top', 'Right', 'Bottom', 'Left'];
|
|
4207
|
+
const sides = (make) => SIDES.map(make);
|
|
1084
4208
|
const INHERIT_TARGETS = {
|
|
1085
4209
|
color: ['color'],
|
|
4210
|
+
'-webkit-text-fill-color': ['textFillColor'],
|
|
4211
|
+
'background-clip': [
|
|
4212
|
+
'backgroundClipText',
|
|
4213
|
+
'backgroundClip',
|
|
4214
|
+
'backgroundClips',
|
|
4215
|
+
],
|
|
4216
|
+
'-webkit-background-clip': [
|
|
4217
|
+
'backgroundClipText',
|
|
4218
|
+
'backgroundClip',
|
|
4219
|
+
'backgroundClips',
|
|
4220
|
+
],
|
|
4221
|
+
'background-origin': ['backgroundOrigin', 'backgroundOrigins'],
|
|
4222
|
+
'border-image': ['borderImage'],
|
|
4223
|
+
'border-image-source': ['borderImage'],
|
|
4224
|
+
'border-image-slice': ['borderImage'],
|
|
4225
|
+
'border-image-width': ['borderImage'],
|
|
4226
|
+
'border-image-outset': ['borderImage'],
|
|
4227
|
+
'border-image-repeat': ['borderImage'],
|
|
1086
4228
|
'font-family': ['fontFamily'],
|
|
1087
4229
|
'font-size': ['fontSize'],
|
|
1088
4230
|
'font-weight': ['fontWeight'],
|
|
1089
4231
|
'font-style': ['fontStyle'],
|
|
1090
|
-
|
|
4232
|
+
// the unit travels with the height: a length inherited as a bare number
|
|
4233
|
+
// would be read as a multiple of the font size
|
|
4234
|
+
font: [
|
|
4235
|
+
'fontFamily',
|
|
4236
|
+
'fontSize',
|
|
4237
|
+
'fontWeight',
|
|
4238
|
+
'fontStyle',
|
|
4239
|
+
'lineHeight',
|
|
4240
|
+
'lineHeightIsLength',
|
|
4241
|
+
'fontVariantNumeric',
|
|
4242
|
+
'fontVariantCaps',
|
|
4243
|
+
'fontVariantLigatures',
|
|
4244
|
+
'fontVariantPosition',
|
|
4245
|
+
'fontKerning',
|
|
4246
|
+
],
|
|
1091
4247
|
'line-height': ['lineHeight', 'lineHeightIsLength'],
|
|
1092
|
-
'text-align': ['textAlign'],
|
|
4248
|
+
'text-align': ['textAlign', 'alignBlocks'],
|
|
4249
|
+
'text-align-last': ['textAlignLast'],
|
|
4250
|
+
'text-justify': ['textJustify'],
|
|
1093
4251
|
'text-indent': ['textIndent'],
|
|
1094
4252
|
'text-transform': ['textTransform'],
|
|
1095
4253
|
'letter-spacing': ['letterSpacing'],
|
|
1096
4254
|
'word-spacing': ['wordSpacing'],
|
|
4255
|
+
'font-variant': [
|
|
4256
|
+
'fontVariantNumeric',
|
|
4257
|
+
'fontVariantCaps',
|
|
4258
|
+
'fontVariantLigatures',
|
|
4259
|
+
'fontVariantPosition',
|
|
4260
|
+
],
|
|
4261
|
+
'font-variant-numeric': ['fontVariantNumeric'],
|
|
4262
|
+
'font-variant-caps': ['fontVariantCaps'],
|
|
4263
|
+
'font-variant-ligatures': ['fontVariantLigatures'],
|
|
4264
|
+
'font-variant-position': ['fontVariantPosition'],
|
|
4265
|
+
'font-kerning': ['fontKerning'],
|
|
4266
|
+
'font-feature-settings': ['fontFeatureSettings'],
|
|
4267
|
+
'tab-size': ['tabSize', 'tabSizeIsLength'],
|
|
4268
|
+
'-moz-tab-size': ['tabSize', 'tabSizeIsLength'],
|
|
1097
4269
|
'white-space': ['whiteSpace'],
|
|
4270
|
+
'overflow-wrap': ['overflowWrap'],
|
|
4271
|
+
'word-wrap': ['overflowWrap'],
|
|
4272
|
+
'word-break': ['wordBreak'],
|
|
4273
|
+
'line-break': ['lineBreakAnywhere'],
|
|
4274
|
+
'white-space-collapse': ['whiteSpace'],
|
|
4275
|
+
'text-wrap': ['whiteSpace', 'textWrapStyle'],
|
|
4276
|
+
'text-wrap-mode': ['whiteSpace'],
|
|
4277
|
+
'text-wrap-style': ['textWrapStyle'],
|
|
1098
4278
|
direction: ['direction'],
|
|
4279
|
+
'unicode-bidi': ['unicodeBidi'],
|
|
1099
4280
|
visibility: ['visibility'],
|
|
1100
|
-
'list-style': ['listStyleType', 'listStylePosition'],
|
|
4281
|
+
'list-style': ['listStyleType', 'listStylePosition', 'listStyleImage'],
|
|
1101
4282
|
'list-style-type': ['listStyleType'],
|
|
1102
4283
|
'list-style-position': ['listStylePosition'],
|
|
4284
|
+
'list-style-image': ['listStyleImage'],
|
|
1103
4285
|
cursor: ['cursor'],
|
|
1104
4286
|
'border-collapse': ['borderCollapse'],
|
|
1105
|
-
'
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
4287
|
+
'caption-side': ['captionSide'],
|
|
4288
|
+
'empty-cells': ['emptyCells'],
|
|
4289
|
+
quotes: ['quotes'],
|
|
4290
|
+
content: ['content'],
|
|
4291
|
+
'counter-reset': ['counterReset'],
|
|
4292
|
+
'counter-increment': ['counterIncrement'],
|
|
4293
|
+
'counter-set': ['counterSet'],
|
|
4294
|
+
'border-spacing': ['borderSpacing', 'borderSpacingY'],
|
|
4295
|
+
display: ['display', 'flowRoot', 'webkitBox', 'grid'],
|
|
4296
|
+
width: ['width', 'widthKeyword'],
|
|
4297
|
+
height: ['height', 'heightKeyword'],
|
|
4298
|
+
'min-width': ['minWidth', 'minWidthKeyword'],
|
|
4299
|
+
'max-width': ['maxWidth', 'maxWidthKeyword'],
|
|
4300
|
+
'min-height': ['minHeight', 'minHeightKeyword'],
|
|
4301
|
+
'max-height': ['maxHeight', 'maxHeightKeyword'],
|
|
4302
|
+
'box-sizing': ['boxSizing'],
|
|
4303
|
+
margin: sides((s) => `margin${s}`),
|
|
4304
|
+
'margin-top': ['marginTop'],
|
|
4305
|
+
'margin-right': ['marginRight'],
|
|
4306
|
+
'margin-bottom': ['marginBottom'],
|
|
4307
|
+
'margin-left': ['marginLeft'],
|
|
4308
|
+
padding: sides((s) => `padding${s}`),
|
|
4309
|
+
'padding-top': ['paddingTop'],
|
|
4310
|
+
'padding-right': ['paddingRight'],
|
|
4311
|
+
'padding-bottom': ['paddingBottom'],
|
|
4312
|
+
'padding-left': ['paddingLeft'],
|
|
4313
|
+
border: [
|
|
4314
|
+
...sides((s) => `border${s}Width`),
|
|
4315
|
+
...sides((s) => `border${s}Style`),
|
|
4316
|
+
...sides((s) => `border${s}Color`),
|
|
4317
|
+
],
|
|
4318
|
+
'border-width': sides((s) => `border${s}Width`),
|
|
4319
|
+
'border-style': sides((s) => `border${s}Style`),
|
|
4320
|
+
'border-color': sides((s) => `border${s}Color`),
|
|
4321
|
+
...Object.fromEntries(SIDES.flatMap((s) => {
|
|
4322
|
+
const side = s.toLowerCase();
|
|
4323
|
+
return [
|
|
4324
|
+
[
|
|
4325
|
+
`border-${side}`,
|
|
4326
|
+
[`border${s}Width`, `border${s}Style`, `border${s}Color`],
|
|
4327
|
+
],
|
|
4328
|
+
[`border-${side}-width`, [`border${s}Width`]],
|
|
4329
|
+
[`border-${side}-style`, [`border${s}Style`]],
|
|
4330
|
+
[`border-${side}-color`, [`border${s}Color`]],
|
|
4331
|
+
];
|
|
4332
|
+
})),
|
|
4333
|
+
'box-shadow': ['boxShadow'],
|
|
4334
|
+
outline: ['outlineStyle', 'outlineWidth', 'outlineColor'],
|
|
4335
|
+
'outline-style': ['outlineStyle'],
|
|
4336
|
+
'outline-width': ['outlineWidth'],
|
|
4337
|
+
'outline-color': ['outlineColor'],
|
|
4338
|
+
'outline-offset': ['outlineOffset'],
|
|
4339
|
+
'text-shadow': ['textShadow'],
|
|
4340
|
+
'line-clamp': ['maxLines', 'clampContinue', 'blockEllipsis'],
|
|
4341
|
+
'-webkit-line-clamp': ['maxLines', 'clampContinue', 'blockEllipsis'],
|
|
4342
|
+
'max-lines': ['maxLines'],
|
|
4343
|
+
continue: ['clampContinue'],
|
|
4344
|
+
'block-ellipsis': ['blockEllipsis'],
|
|
4345
|
+
'-webkit-box-orient': ['webkitBoxOrient'],
|
|
4346
|
+
'-webkit-box-direction': ['webkitBoxDirection'],
|
|
4347
|
+
'-webkit-box-pack': ['webkitBoxPack'],
|
|
4348
|
+
'-webkit-box-align': ['webkitBoxAlign'],
|
|
4349
|
+
'-webkit-box-flex': ['webkitBoxFlex'],
|
|
4350
|
+
columns: ['columns'],
|
|
4351
|
+
'column-count': ['columns'],
|
|
4352
|
+
'column-width': ['columns'],
|
|
4353
|
+
'text-overflow': ['textOverflow'],
|
|
4354
|
+
// a flex box's and its items', and a grid's
|
|
4355
|
+
'flex-direction': ['flexDirection'],
|
|
4356
|
+
'flex-wrap': ['flexWrap'],
|
|
4357
|
+
'flex-flow': ['flexDirection', 'flexWrap'],
|
|
4358
|
+
'justify-content': ['justifyContent'],
|
|
4359
|
+
'align-items': ['alignItems'],
|
|
4360
|
+
'align-self': ['alignSelf'],
|
|
4361
|
+
'align-content': ['alignContent'],
|
|
4362
|
+
flex: ['flexGrow', 'flexShrink', 'flexBasis'],
|
|
4363
|
+
'flex-grow': ['flexGrow'],
|
|
4364
|
+
'flex-shrink': ['flexShrink'],
|
|
4365
|
+
'flex-basis': ['flexBasis'],
|
|
4366
|
+
order: ['order'],
|
|
4367
|
+
gap: ['rowGap', 'columnGap'],
|
|
4368
|
+
'row-gap': ['rowGap'],
|
|
4369
|
+
'column-gap': ['columnGap'],
|
|
4370
|
+
'grid-gap': ['rowGap', 'columnGap'],
|
|
4371
|
+
'grid-row-gap': ['rowGap'],
|
|
4372
|
+
'grid-column-gap': ['columnGap'],
|
|
4373
|
+
'grid-template-columns': ['gridColumns'],
|
|
4374
|
+
'grid-template-rows': ['gridRows'],
|
|
4375
|
+
'grid-auto-rows': ['gridAutoRows'],
|
|
4376
|
+
'grid-auto-columns': ['gridAutoColumns'],
|
|
4377
|
+
'grid-template-areas': ['gridAreas'],
|
|
4378
|
+
'grid-auto-flow': ['gridAutoFlow'],
|
|
4379
|
+
'grid-template': ['gridRows', 'gridColumns', 'gridAreas'],
|
|
4380
|
+
grid: [
|
|
4381
|
+
'gridRows',
|
|
4382
|
+
'gridColumns',
|
|
4383
|
+
'gridAreas',
|
|
4384
|
+
'gridAutoRows',
|
|
4385
|
+
'gridAutoColumns',
|
|
4386
|
+
'gridAutoFlow',
|
|
4387
|
+
],
|
|
4388
|
+
'grid-area': [
|
|
4389
|
+
'gridRowStart',
|
|
4390
|
+
'gridColumnStart',
|
|
4391
|
+
'gridRowEnd',
|
|
4392
|
+
'gridColumnEnd',
|
|
4393
|
+
],
|
|
4394
|
+
'grid-row': ['gridRowStart', 'gridRowEnd'],
|
|
4395
|
+
'grid-row-start': ['gridRowStart'],
|
|
4396
|
+
'grid-row-end': ['gridRowEnd'],
|
|
4397
|
+
'grid-column': ['gridColumnStart', 'gridColumnEnd'],
|
|
4398
|
+
'grid-column-start': ['gridColumnStart'],
|
|
4399
|
+
'grid-column-end': ['gridColumnEnd'],
|
|
4400
|
+
'justify-items': ['justifyItems'],
|
|
4401
|
+
'justify-self': ['justifySelf'],
|
|
4402
|
+
'place-items': ['alignItems', 'justifyItems'],
|
|
4403
|
+
'place-self': ['alignSelf', 'justifySelf'],
|
|
4404
|
+
'border-radius': ['borderRadius', 'borderRadiusY'],
|
|
4405
|
+
'border-top-left-radius': ['borderRadius', 'borderRadiusY'],
|
|
4406
|
+
'border-top-right-radius': ['borderRadius', 'borderRadiusY'],
|
|
4407
|
+
'border-bottom-right-radius': ['borderRadius', 'borderRadiusY'],
|
|
4408
|
+
'border-bottom-left-radius': ['borderRadius', 'borderRadiusY'],
|
|
4409
|
+
inset: ['top', 'right', 'bottom', 'left'],
|
|
4410
|
+
background: [
|
|
4411
|
+
'backgroundColor',
|
|
4412
|
+
'backgroundImage',
|
|
4413
|
+
'backgroundGradient',
|
|
4414
|
+
'backgroundRepeat',
|
|
4415
|
+
'backgroundSize',
|
|
4416
|
+
'backgroundAttachment',
|
|
4417
|
+
'backgroundPositionX',
|
|
4418
|
+
'backgroundPositionY',
|
|
4419
|
+
'backgroundImages',
|
|
4420
|
+
'backgroundRepeats',
|
|
4421
|
+
'backgroundSizes',
|
|
4422
|
+
'backgroundAttachments',
|
|
4423
|
+
'backgroundPositions',
|
|
4424
|
+
'backgroundClipText',
|
|
4425
|
+
'backgroundClip',
|
|
4426
|
+
'backgroundOrigin',
|
|
4427
|
+
'backgroundClips',
|
|
4428
|
+
'backgroundOrigins',
|
|
4429
|
+
],
|
|
1109
4430
|
'background-color': ['backgroundColor'],
|
|
4431
|
+
'background-image': [
|
|
4432
|
+
'backgroundImage',
|
|
4433
|
+
'backgroundGradient',
|
|
4434
|
+
'backgroundImages',
|
|
4435
|
+
],
|
|
4436
|
+
'background-repeat': ['backgroundRepeat', 'backgroundRepeats'],
|
|
4437
|
+
'background-size': ['backgroundSize', 'backgroundSizes'],
|
|
4438
|
+
'background-attachment': ['backgroundAttachment', 'backgroundAttachments'],
|
|
4439
|
+
'background-position': [
|
|
4440
|
+
'backgroundPositionX',
|
|
4441
|
+
'backgroundPositionY',
|
|
4442
|
+
'backgroundPositions',
|
|
4443
|
+
],
|
|
4444
|
+
'aspect-ratio': ['aspectRatio'],
|
|
4445
|
+
'object-fit': ['objectFit'],
|
|
4446
|
+
appearance: ['appearance'],
|
|
4447
|
+
'-webkit-appearance': ['appearance'],
|
|
4448
|
+
'-moz-appearance': ['appearance'],
|
|
4449
|
+
contain: ['contain'],
|
|
4450
|
+
'content-visibility': ['contentVisibility'],
|
|
4451
|
+
'overflow-clip-margin': ['overflowClipBox', 'overflowClipMargin'],
|
|
4452
|
+
'contain-intrinsic-size': ['containIntrinsicWidth', 'containIntrinsicHeight'],
|
|
4453
|
+
'contain-intrinsic-width': ['containIntrinsicWidth'],
|
|
4454
|
+
'contain-intrinsic-height': ['containIntrinsicHeight'],
|
|
4455
|
+
'contain-intrinsic-inline-size': ['containIntrinsicWidth'],
|
|
4456
|
+
'contain-intrinsic-block-size': ['containIntrinsicHeight'],
|
|
4457
|
+
'object-position': ['objectPositionX', 'objectPositionY'],
|
|
4458
|
+
position: ['position'],
|
|
4459
|
+
top: ['top'],
|
|
4460
|
+
right: ['right'],
|
|
4461
|
+
bottom: ['bottom'],
|
|
4462
|
+
left: ['left'],
|
|
4463
|
+
float: ['float'],
|
|
4464
|
+
clear: ['clear'],
|
|
4465
|
+
overflow: ['overflowX', 'overflowY'],
|
|
4466
|
+
clip: ['clip'],
|
|
4467
|
+
'overflow-x': ['overflowX'],
|
|
4468
|
+
'overflow-y': ['overflowY'],
|
|
4469
|
+
opacity: ['opacity'],
|
|
4470
|
+
translate: ['translate'],
|
|
4471
|
+
transform: ['transformTranslate'],
|
|
4472
|
+
'z-index': ['zIndex'],
|
|
4473
|
+
'vertical-align': ['verticalAlign'],
|
|
4474
|
+
'text-decoration': [
|
|
4475
|
+
'textDecorationLine',
|
|
4476
|
+
'textDecorationColor',
|
|
4477
|
+
'textDecorationStyle',
|
|
4478
|
+
],
|
|
4479
|
+
'text-decoration-line': ['textDecorationLine'],
|
|
4480
|
+
'text-decoration-style': ['textDecorationStyle'],
|
|
4481
|
+
'text-decoration-thickness': ['textDecorationThickness'],
|
|
4482
|
+
'text-underline-offset': ['textUnderlineOffset'],
|
|
4483
|
+
'table-layout': ['tableLayout'],
|
|
1110
4484
|
};
|
|
4485
|
+
/**
|
|
4486
|
+
* A decoration's thickness or an underline's offset: a length, a
|
|
4487
|
+
* percentage of the font size, or null for `auto` (and `alsoAuto`, which is
|
|
4488
|
+
* `from-font` for a thickness); undefined for a value that is none.
|
|
4489
|
+
*/
|
|
4490
|
+
function decorationLength(value, ctx, alsoAuto) {
|
|
4491
|
+
const v = value.trim().toLowerCase();
|
|
4492
|
+
if (v === 'auto' || v === alsoAuto)
|
|
4493
|
+
return null;
|
|
4494
|
+
const pct = /^(-?\d*\.?\d+)%$/.exec(v);
|
|
4495
|
+
if (pct)
|
|
4496
|
+
return (Number(pct[1]) / 100) * ctx.em;
|
|
4497
|
+
const len = parseLength(v, ctx);
|
|
4498
|
+
return typeof len === 'number' ? len : undefined;
|
|
4499
|
+
}
|
|
4500
|
+
/**
|
|
4501
|
+
* The decorations a box's text is drawn with (CSS 2.1 16.3.1): those its
|
|
4502
|
+
* ancestors propagate to it, and its own, each in the colour of the box that
|
|
4503
|
+
* set it, so an underlined link's `<strong>` is underlined in the link's
|
|
4504
|
+
* colour. A float, an absolutely positioned box and an atomic inline box —
|
|
4505
|
+
* an inline block, table or flex box — take none from above, and `none`
|
|
4506
|
+
* takes none away. After `blockify`, whose `float` and `position` it reads.
|
|
4507
|
+
*/
|
|
4508
|
+
export function decorate(style) {
|
|
4509
|
+
const display = style.display;
|
|
4510
|
+
if (style.float !== 'none' ||
|
|
4511
|
+
style.position === 'absolute' ||
|
|
4512
|
+
style.position === 'fixed' ||
|
|
4513
|
+
display === 'inline-block' ||
|
|
4514
|
+
display === 'inline-table' ||
|
|
4515
|
+
display === 'inline-flex') {
|
|
4516
|
+
style.underline = null;
|
|
4517
|
+
style.lineThrough = null;
|
|
4518
|
+
}
|
|
4519
|
+
const own = style.textDecorationLine;
|
|
4520
|
+
if (own === 'none')
|
|
4521
|
+
return;
|
|
4522
|
+
const lines = own.split(' ');
|
|
4523
|
+
if (lines.includes('underline')) {
|
|
4524
|
+
style.underline = inkColor(style.textDecorationColor ?? 'currentColor', style.color);
|
|
4525
|
+
style.underlineStyle = style.textDecorationStyle;
|
|
4526
|
+
style.underlineThickness = style.textDecorationThickness;
|
|
4527
|
+
style.underlineOffset = style.textUnderlineOffset;
|
|
4528
|
+
}
|
|
4529
|
+
if (lines.includes('line-through')) {
|
|
4530
|
+
style.lineThrough = inkColor(style.textDecorationColor ?? 'currentColor', style.color);
|
|
4531
|
+
}
|
|
4532
|
+
}
|
|
4533
|
+
/**
|
|
4534
|
+
* The absolutely positioned styles that were inline-level before they were
|
|
4535
|
+
* made blocks: where such a box would have been in flow is on its line,
|
|
4536
|
+
* where a block's is under it (CSS 2.1 10.3.7, `staticPositions`).
|
|
4537
|
+
*/
|
|
4538
|
+
export const INLINE_BEFORE_ABSOLUTE = new WeakSet();
|
|
4539
|
+
/**
|
|
4540
|
+
* `line-clamp`'s longhands and the legacy flexible box they are written
|
|
4541
|
+
* with, settled once the cascade is done, since each reads the others
|
|
4542
|
+
* (CSS Overflow 4, 5.1.1 and 5.3). `continue: collapse` makes a block
|
|
4543
|
+
* container a line-clamp container, and `-webkit-line-clamp`'s
|
|
4544
|
+
* `-webkit-legacy` does only in a `display: -webkit-box` whose
|
|
4545
|
+
* `-webkit-box-orient` is vertical — which is then a block of its own
|
|
4546
|
+
* formatting context, as Tailwind's `line-clamp-2` writes all four. Any
|
|
4547
|
+
* other `-webkit-box` is a flex box, in its orient's direction, packed and
|
|
4548
|
+
* aligned as it says, whose items grow and shrink by `-webkit-box-flex`
|
|
4549
|
+
* alone (as Blink lays one out). Before `blockify`, whose `display` this
|
|
4550
|
+
* decides.
|
|
4551
|
+
*/
|
|
4552
|
+
export function settleClamp(style, parent) {
|
|
4553
|
+
const box = style.webkitBox;
|
|
4554
|
+
const vertical = style.webkitBoxOrient === 'vertical';
|
|
4555
|
+
const collapses = style.clampContinue === 'collapse' ||
|
|
4556
|
+
(style.clampContinue === 'legacy' && box !== null && vertical);
|
|
4557
|
+
if (box !== null) {
|
|
4558
|
+
if (collapses && vertical) {
|
|
4559
|
+
style.display = box === 'inline' ? 'inline-block' : 'block';
|
|
4560
|
+
style.flowRoot = box === 'block';
|
|
4561
|
+
}
|
|
4562
|
+
else {
|
|
4563
|
+
const reverse = style.webkitBoxDirection === 'reverse';
|
|
4564
|
+
style.flexDirection = vertical
|
|
4565
|
+
? reverse
|
|
4566
|
+
? 'column-reverse'
|
|
4567
|
+
: 'column'
|
|
4568
|
+
: reverse
|
|
4569
|
+
? 'row-reverse'
|
|
4570
|
+
: 'row';
|
|
4571
|
+
style.flexWrap = 'nowrap';
|
|
4572
|
+
style.justifyContent = style.webkitBoxPack ?? 'flex-start';
|
|
4573
|
+
style.alignItems = style.webkitBoxAlign ?? 'stretch';
|
|
4574
|
+
}
|
|
4575
|
+
}
|
|
4576
|
+
if (parent?.webkitBox &&
|
|
4577
|
+
(parent.display === 'flex' || parent.display === 'inline-flex')) {
|
|
4578
|
+
style.flexGrow = style.webkitBoxFlex;
|
|
4579
|
+
style.flexShrink = style.webkitBoxFlex;
|
|
4580
|
+
}
|
|
4581
|
+
style.lineClamp =
|
|
4582
|
+
collapses && CLAMPS.has(style.display) && !style.columns
|
|
4583
|
+
? (style.maxLines ?? Infinity)
|
|
4584
|
+
: null;
|
|
4585
|
+
}
|
|
4586
|
+
/** The displays of a block container, which `continue` applies to. */
|
|
4587
|
+
const CLAMPS = new Set([
|
|
4588
|
+
'block',
|
|
4589
|
+
'inline-block',
|
|
4590
|
+
'list-item',
|
|
4591
|
+
'table-cell',
|
|
4592
|
+
'table-caption',
|
|
4593
|
+
]);
|
|
1111
4594
|
/**
|
|
1112
4595
|
* The blockification the box tree depends on: a floated or absolutely
|
|
1113
4596
|
* positioned element is a block whatever `display` said, and a flex item's
|
|
@@ -1123,6 +4606,13 @@ export function blockify(style, inFlexContainer) {
|
|
|
1123
4606
|
style.position === 'fixed';
|
|
1124
4607
|
if (!isOutOfFlow && !inFlexContainer)
|
|
1125
4608
|
return;
|
|
4609
|
+
if ((style.position === 'absolute' || style.position === 'fixed') &&
|
|
4610
|
+
(out === 'inline' ||
|
|
4611
|
+
out === 'inline-block' ||
|
|
4612
|
+
out === 'inline-table' ||
|
|
4613
|
+
out === 'inline-flex')) {
|
|
4614
|
+
INLINE_BEFORE_ABSOLUTE.add(style);
|
|
4615
|
+
}
|
|
1126
4616
|
switch (out) {
|
|
1127
4617
|
case 'inline':
|
|
1128
4618
|
case 'inline-block':
|
|
@@ -1132,6 +4622,8 @@ export function blockify(style, inFlexContainer) {
|
|
|
1132
4622
|
case 'table-header-group':
|
|
1133
4623
|
case 'table-footer-group':
|
|
1134
4624
|
case 'table-caption':
|
|
4625
|
+
case 'table-column':
|
|
4626
|
+
case 'table-column-group':
|
|
1135
4627
|
style.display = 'block';
|
|
1136
4628
|
return;
|
|
1137
4629
|
case 'inline-flex':
|