ranger-compiler 3.5.1 → 3.5.2
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/CHANGELOG.md +1064 -20
- package/LICENSE +3 -1
- package/README.md +123 -28
- package/dist/Lang.rgr +1188 -290
- package/dist/api.d.ts +2343 -879
- package/dist/api.js +79923 -56921
- package/dist/lib/JSON.rgr +102 -91
- package/dist/lib/Shell.rgr +4 -4
- package/dist/lib/apple/AppleToolchain.rgr +4 -4
- package/dist/lib/apple/README.md +5 -4
- package/dist/lib/apple/apple_test.rgr +1 -1
- package/dist/lib/core/README.md +1 -1
- package/dist/lib/evg/EVG.rgr +12 -0
- package/dist/lib/evg/EVGA11yFromTree.rgr +302 -0
- package/dist/lib/evg/EVGA11yTree.rgr +894 -0
- package/dist/lib/evg/EVGBox.rgr +267 -0
- package/dist/lib/evg/EVGBoxShorthandTest.rgr +220 -0
- package/dist/lib/evg/EVGCodepoint.rgr +316 -0
- package/dist/lib/evg/EVGColor.rgr +700 -0
- package/dist/lib/evg/EVGCommands.rgr +177 -0
- package/dist/lib/evg/EVGComponent.rgr +331 -0
- package/dist/lib/evg/EVGComponentTest.rgr +387 -0
- package/dist/lib/evg/EVGConnector.rgr +541 -0
- package/dist/lib/evg/EVGConnectorTest.rgr +306 -0
- package/dist/lib/evg/EVGDisplayList.rgr +4186 -0
- package/dist/lib/evg/EVGEasing.rgr +370 -0
- package/dist/lib/evg/EVGEffectTest.rgr +253 -0
- package/dist/lib/evg/EVGElement.rgr +4914 -0
- package/dist/lib/evg/EVGFixedTest.rgr +289 -0
- package/dist/lib/evg/EVGFlexRulesTest.rgr +317 -0
- package/dist/lib/evg/EVGFlexWrapTest.rgr +246 -0
- package/dist/lib/evg/EVGFling.rgr +244 -0
- package/dist/lib/evg/EVGFocus.rgr +400 -0
- package/dist/lib/evg/EVGFocusTest.rgr +399 -0
- package/dist/lib/evg/EVGGradient.rgr +322 -0
- package/dist/lib/evg/EVGGrapheme.rgr +190 -0
- package/dist/lib/evg/EVGGrid.rgr +975 -0
- package/dist/lib/evg/EVGHitTest.rgr +208 -0
- package/dist/lib/evg/EVGHoles.rgr +294 -0
- package/dist/lib/evg/EVGHostMeasurerTest.rgr +281 -0
- package/dist/lib/evg/EVGHostTextMeasurer.rgr +331 -0
- package/dist/lib/evg/EVGHostTree.rgr +834 -0
- package/dist/lib/evg/EVGHostTreeTest.rgr +447 -0
- package/dist/lib/evg/EVGImageDecode.rgr +117 -0
- package/dist/lib/evg/EVGImageMeasurer.rgr +86 -0
- package/dist/lib/evg/EVGInspect.rgr +883 -0
- package/dist/lib/evg/EVGInvalidateTest.rgr +432 -0
- package/dist/lib/evg/EVGJsonTest.rgr +535 -0
- package/dist/lib/evg/EVGLayout.rgr +4220 -0
- package/dist/lib/evg/EVGMeasure.rgr +1267 -0
- package/dist/lib/evg/EVGOverlayTest.rgr +626 -0
- package/dist/lib/evg/EVGPatch.rgr +1721 -0
- package/dist/lib/evg/EVGPatchTest.rgr +692 -0
- package/dist/lib/evg/EVGPopoverTest.rgr +395 -0
- package/dist/lib/evg/EVGReconcile.rgr +226 -0
- package/dist/lib/evg/EVGReconcileTest.rgr +421 -0
- package/dist/lib/evg/EVGReject.rgr +126 -0
- package/dist/lib/evg/EVGRelayoutTest.rgr +375 -0
- package/dist/lib/evg/EVGRtlLayoutTest.rgr +319 -0
- package/dist/lib/evg/EVGRuler.rgr +232 -0
- package/dist/lib/evg/EVGRulerTest.rgr +172 -0
- package/dist/lib/evg/EVGSelectChrome.rgr +178 -0
- package/dist/lib/evg/EVGStyleCacheTest.rgr +419 -0
- package/dist/lib/evg/EVGStyleSheet.rgr +2148 -0
- package/dist/lib/evg/EVGStyleStateTest.rgr +407 -0
- package/dist/lib/evg/EVGStyleVarTest.rgr +471 -0
- package/dist/lib/evg/EVGText.rgr +105 -0
- package/dist/lib/evg/EVGTextEngine.rgr +536 -0
- package/dist/lib/evg/EVGTextMeasurer.rgr +720 -0
- package/dist/lib/evg/EVGTimingTest.rgr +1417 -0
- package/dist/lib/evg/EVGToolbar.rgr +1369 -0
- package/dist/lib/evg/EVGTransition.rgr +728 -0
- package/dist/lib/evg/EVGTreeJson.rgr +721 -0
- package/dist/lib/evg/EVGUnit.rgr +554 -0
- package/dist/lib/evg/EVGViewportUnitTest.rgr +236 -0
- package/dist/lib/evg/EvgApp.rgr +189 -0
- package/dist/lib/evg/EvgBitmapTracer.rgr +4277 -0
- package/dist/lib/evg/EvgBitmapTracerTest.rgr +2119 -0
- package/dist/lib/evg/EvgHost.rgr +451 -0
- package/dist/lib/evg/EvgTest.rgr +91 -0
- package/dist/lib/evg/EvgTraceColor.rgr +236 -0
- package/dist/lib/evg/EvgTraceCurve.rgr +648 -0
- package/dist/lib/evg/EvgTraceFit.rgr +767 -0
- package/dist/lib/evg/EvgTracePath.rgr +435 -0
- package/dist/lib/evg/EvgTraceTypes.rgr +559 -0
- package/dist/lib/evg/EvgViewport.rgr +313 -0
- package/dist/lib/evg/FxDemoDoc.rgr +185 -0
- package/dist/lib/evg/HOSTS.md +227 -0
- package/dist/lib/evg/ISSUES.md +835 -0
- package/dist/lib/evg/PLAN_ACCESSIBILITY.md +650 -0
- package/dist/lib/evg/PLAN_CSS_LAYOUT_AND_FONTS.md +1124 -0
- package/dist/lib/evg/PLAN_EFFECTS.md +431 -0
- package/dist/lib/evg/PLAN_EVG.md +518 -0
- package/dist/lib/evg/PLAN_EVG_RENDERER.md +1674 -0
- package/dist/lib/evg/PLAN_INSPECTOR.md +788 -0
- package/dist/lib/evg/PLAN_LINKS_AND_FORMS.md +157 -0
- package/dist/lib/evg/PLAN_NATIVE_HOSTS.md +671 -0
- package/dist/lib/evg/PLAN_VECTOR_IR.md +664 -0
- package/dist/lib/evg/PLAN_VIEW_TRANSFORM.md +322 -0
- package/dist/lib/evg/PathBuilder.rgr +333 -0
- package/dist/lib/evg/README.md +1236 -0
- package/dist/lib/evg/SPEC.md +1252 -0
- package/dist/lib/evg/SVGPathParser.rgr +1199 -0
- package/dist/lib/evg/SvgParser.rgr +1736 -0
- package/dist/lib/evg/TODO_EVG_RENDERER.md +595 -0
- package/dist/lib/evg/VectorShapes.rgr +331 -0
- package/dist/lib/evg/VectorStroke.rgr +107 -0
- package/dist/lib/evg/VectorViewBox.rgr +380 -0
- package/dist/lib/evg/agent/README.md +290 -0
- package/dist/lib/evg/agent/evg_agent.rgr +899 -0
- package/dist/lib/evg/agent/fixtures/broken.evg.json +7 -0
- package/dist/lib/evg/agent/fixtures/card.evg.json +9 -0
- package/dist/lib/evg/agent/fixtures/connector.css +63 -0
- package/dist/lib/evg/agent/fixtures/connector.evg.json +14 -0
- package/dist/lib/evg/agent/fixtures/drawn.evg.json +129 -0
- package/dist/lib/evg/agent/fixtures/gradient.evg.json +4 -0
- package/dist/lib/evg/agent/fixtures/popover.css +99 -0
- package/dist/lib/evg/agent/fixtures/popover.evg.json +30 -0
- package/dist/lib/evg/agent/roundtrip.sh +74 -0
- package/dist/lib/evg/agent/smoke.sh +230 -0
- package/dist/lib/evg/android/README.md +97 -0
- package/dist/lib/evg/android/androidstubs/AndroidStubs.kt +175 -0
- package/dist/lib/evg/android/androidstubs/Annotation.kt +10 -0
- package/dist/lib/evg/android/androidstubs/App.kt +30 -0
- package/dist/lib/evg/android/androidstubs/Content.kt +41 -0
- package/dist/lib/evg/android/androidstubs/ContentRes.kt +12 -0
- package/dist/lib/evg/android/androidstubs/InputMethod.kt +46 -0
- package/dist/lib/evg/android/androidstubs/Net.kt +6 -0
- package/dist/lib/evg/android/androidstubs/Os.kt +30 -0
- package/dist/lib/evg/android/androidstubs/Util.kt +15 -0
- package/dist/lib/evg/android/androidstubs/View.kt +113 -0
- package/dist/lib/evg/android/androidstubs/Widget.kt +16 -0
- package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidEvgSurface.kt +323 -0
- package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidTextMeasurer.kt +56 -0
- package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/RippleEffect.kt +262 -0
- package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtEvgSurface.kt +238 -0
- package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtTextMeasurer.kt +85 -0
- package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgEngineThread.kt +115 -0
- package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgPainter.kt +231 -0
- package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgSurface.kt +117 -0
- package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/RecordingSurface.kt +94 -0
- package/dist/lib/evg/apple/README.md +128 -0
- package/dist/lib/evg/apple/Sources/CoreGraphicsEvgSurface.swift +329 -0
- package/dist/lib/evg/apple/Sources/CoreTextMeasurer.swift +70 -0
- package/dist/lib/evg/apple/Sources/EvgEngineQueue.swift +105 -0
- package/dist/lib/evg/apple/Sources/EvgPainter.swift +225 -0
- package/dist/lib/evg/apple/Sources/EvgSurface.swift +163 -0
- package/dist/lib/evg/apple/Sources/RecordingSurface.swift +110 -0
- package/dist/lib/evg/bench/EvgLayoutBench.rgr +34 -0
- package/dist/lib/evg/bench/README.md +64 -0
- package/dist/lib/evg/bench/layout-bench.mjs +316 -0
- package/dist/lib/evg/bench/layout-cases.mjs +510 -0
- package/dist/lib/evg/bench/layout-conformance.mjs +254 -0
- package/dist/lib/evg/bin/.gitignore +17 -0
- package/dist/lib/evg/evg_test.rgr +313 -0
- package/dist/lib/evg/gl/README.md +198 -0
- package/dist/lib/evg/gl/a11y-paint-check.mjs +73 -0
- package/dist/lib/evg/gl/blur-check.mjs +399 -0
- package/dist/lib/evg/gl/boxmodel.json +1 -0
- package/dist/lib/evg/gl/demo.html +46 -0
- package/dist/lib/evg/gl/effect-presets.css +285 -0
- package/dist/lib/evg/gl/effect-presets.js +63 -0
- package/dist/lib/evg/gl/effect-shots.mjs +135 -0
- package/dist/lib/evg/gl/evg-a11y.js +566 -0
- package/dist/lib/evg/gl/evg-binary.js +160 -0
- package/dist/lib/evg/gl/evg-engine.js +296 -0
- package/dist/lib/evg/gl/evg-fx.js +237 -0
- package/dist/lib/evg/gl/evg-gestures.js +209 -0
- package/dist/lib/evg/gl/evg-list.js +167 -0
- package/dist/lib/evg/gl/evg-measure.js +186 -0
- package/dist/lib/evg/gl/evg-textinput.js +303 -0
- package/dist/lib/evg/gl/evg-view.js +144 -0
- package/dist/lib/evg/gl/evg-webgl.js +3942 -0
- package/dist/lib/evg/gl/fx-check.mjs +998 -0
- package/dist/lib/evg/gl/fx-demo.html +166 -0
- package/dist/lib/evg/gl/fx-demo.js +2 -0
- package/dist/lib/evg/gl/fx-serve.mjs +47 -0
- package/dist/lib/evg/gl/gestures-check.mjs +189 -0
- package/dist/lib/evg/gl/list-binary-check.mjs +152 -0
- package/dist/lib/evg/gl/measure-check.mjs +135 -0
- package/dist/lib/evg/gl/rotation-check.mjs +207 -0
- package/dist/lib/evg/gl/shift-check.mjs +113 -0
- package/dist/lib/evg/gl/stroke-check.mjs +168 -0
- package/dist/lib/evg/gl/text-snap-check.mjs +139 -0
- package/dist/lib/evg/gl/view-check.mjs +343 -0
- package/dist/lib/evg/gl/view-policy-check.mjs +184 -0
- package/dist/lib/evg/html/evg-dom.js +318 -0
- package/dist/lib/evg/html/evg-html.js +600 -0
- package/dist/lib/evg/inspect/README.md +415 -0
- package/dist/lib/evg/inspect/browser-smoke.mjs +115 -0
- package/dist/lib/evg/inspect/evg-inspect.js +947 -0
- package/dist/lib/evg/inspect/shots/css.png +0 -0
- package/dist/lib/evg/inspect/shots/dashboard.png +0 -0
- package/dist/lib/evg/inspect/shots/pptx-slide.png +0 -0
- package/dist/lib/evg/inspect/shots/state.png +0 -0
- package/dist/lib/evg/inspect/shots.mjs +226 -0
- package/dist/lib/evg/oracle/css-blur.json +576 -0
- package/dist/lib/evg/oracle/css-box.json +157 -0
- package/dist/lib/evg/oracle/css-timing.json +709 -0
- package/dist/lib/evg/oracle/css_blur_oracle.mjs +529 -0
- package/dist/lib/evg/oracle/css_box_oracle.mjs +106 -0
- package/dist/lib/evg/oracle/css_timing_oracle.mjs +389 -0
- package/dist/lib/evg/original/EVGColor.clj +310 -0
- package/dist/lib/evg/original/EVGColorContext.rgr +178 -0
- package/dist/lib/evg/original/SVGPath.rgr +627 -0
- package/dist/lib/evg/original/Vec2.crgr +103 -0
- package/dist/lib/evg/ranger.json +9 -0
- package/dist/lib/evg/showcase/README.md +246 -0
- package/dist/lib/evg/showcase/assets/emblem.svg +30 -0
- package/dist/lib/evg/showcase/assets/rosette.svg +22 -0
- package/dist/lib/evg/showcase/build.mjs +651 -0
- package/dist/lib/evg/showcase/pages/album.tsx +30 -0
- package/dist/lib/evg/showcase/pages/boxmodel.tsx +37 -0
- package/dist/lib/evg/showcase/pages/cards.tsx +41 -0
- package/dist/lib/evg/showcase/pages/chart_api.tsx +170 -0
- package/dist/lib/evg/showcase/pages/charts.tsx +219 -0
- package/dist/lib/evg/showcase/pages/drawing.tsx +160 -0
- package/dist/lib/evg/showcase/pages/emoji.tsx +91 -0
- package/dist/lib/evg/showcase/pages/flex.tsx +46 -0
- package/dist/lib/evg/showcase/pages/more.tsx +332 -0
- package/dist/lib/evg/showcase/pages/plots.tsx +262 -0
- package/dist/lib/evg/showcase/pages/svg.tsx +63 -0
- package/dist/lib/evg/showcase/pages/tables.tsx +209 -0
- package/dist/lib/evg/showcase/pages/typography.tsx +43 -0
- package/dist/lib/evg/showcase/pages/units.tsx +35 -0
- package/dist/lib/evg/showcase/pages/variants.tsx +233 -0
- package/dist/lib/evg/showcase/pages/vector.tsx +75 -0
- package/dist/lib/evg/showcase/pages/views.tsx +223 -0
- package/dist/lib/evg/showcase/tests/chart_api_smoke.mjs +339 -0
- package/dist/lib/evg/showcase/tests/gl_smoke.mjs +125 -0
- package/dist/lib/evg/showcase/themes/chart_api-default.css +15 -0
- package/dist/lib/evg/showcase/themes/charts-default.css +29 -0
- package/dist/lib/evg/showcase/themes/drawing-default.css +35 -0
- package/dist/lib/evg/showcase/themes/more-default.css +128 -0
- package/dist/lib/evg/showcase/themes/plots-default.css +18 -0
- package/dist/lib/evg/showcase/themes/showcase.css +508 -0
- package/dist/lib/evg/showcase/themes/tables-default.css +123 -0
- package/dist/lib/evg/showcase/themes/variants-default.css +15 -0
- package/dist/lib/evg/showcase/themes/views-default.css +15 -0
- package/dist/lib/evg/tools/bench_vs_potrace.mjs +290 -0
- package/dist/lib/evg/tools/evg_image_tool.rgr +488 -0
- package/dist/lib/evg/tools/evg_trace_bench.rgr +114 -0
- package/dist/lib/evg/tools/evg_trace_cli.rgr +662 -0
- package/dist/lib/evg/tools/evg_trace_cpp_bench.rgr +83 -0
- package/dist/lib/evg/tools/run_trace_bench.sh +74 -0
- package/dist/lib/evg/tools/run_trace_cli_smoke.sh +128 -0
- package/dist/lib/evg/web/responsive/EvgResponsiveCheck.rgr +271 -0
- package/dist/lib/evg/web/responsive/EvgResponsiveDemo.rgr +531 -0
- package/dist/lib/evg/web/responsive/README.md +97 -0
- package/dist/lib/evg/web/responsive/build.mjs +90 -0
- package/dist/lib/evg/web/responsive/dom-check.mjs +169 -0
- package/dist/lib/evg/web/responsive/index.html +177 -0
- package/dist/lib/evg/web/responsive/smoke.mjs +221 -0
- package/dist/lib/evg/web/tools/assets-client.mjs +95 -0
- package/dist/lib/evg/web/tools/boot-bench.mjs +189 -0
- package/dist/lib/evg/web/tools/inline-assets.mjs +119 -0
- package/dist/lib/evg/web/tools/minify.mjs +60 -0
- package/dist/lib/evg/web/tracer/build.mjs +87 -0
- package/dist/lib/evg/web/tracer/index.html +2805 -0
- package/dist/lib/evg/web/tracer/sample.png +0 -0
- package/dist/lib/evg/web/tracer/smoke.mjs +1162 -0
- package/dist/lib/evgr/Cargo.lock +21 -0
- package/dist/lib/evgr/Cargo.toml +19 -0
- package/dist/lib/evgr/README.md +140 -0
- package/dist/lib/evgr/bench/NativeBench.rgr +246 -0
- package/dist/lib/evgr/bench/compare.mjs +252 -0
- package/dist/lib/evgr/bench/speed.mjs +277 -0
- package/dist/lib/evgr/bin/.gitignore +3 -0
- package/dist/lib/evgr/src/bin/bench.rs +106 -0
- package/dist/lib/evgr/src/bin/smoke.rs +13 -0
- package/dist/lib/evgr/src/grid.rs +730 -0
- package/dist/lib/evgr/src/lib.rs +1003 -0
- package/dist/lib/evgr/src/style.rs +468 -0
- package/dist/lib/evgr/src/text.rs +83 -0
- package/dist/lib/image/BitReader.rgr +171 -0
- package/dist/lib/image/Buffer.rgr +173 -0
- package/dist/lib/image/DCT.rgr +283 -0
- package/dist/lib/image/Deflate.rgr +339 -0
- package/dist/lib/image/HuffmanDecoder.rgr +181 -0
- package/dist/lib/image/ImageBuffer.rgr +706 -0
- package/dist/lib/image/JPEGDecoder.rgr +808 -0
- package/dist/lib/image/PNGDecoder.rgr +544 -0
- package/dist/lib/image/PNGEncoder.rgr +388 -0
- package/dist/lib/image/PPMImage.rgr +224 -0
- package/dist/lib/image/ProgressiveJPEGDecoder.rgr +1154 -0
- package/dist/lib/image/README.md +33 -0
- package/dist/lib/image/RasterBuffer.rgr +294 -0
- package/dist/lib/image/VP8BoolDecoder.rgr +163 -0
- package/dist/lib/image/WebPDecoder.rgr +433 -0
- package/dist/lib/image/WebPLossless.rgr +1035 -0
- package/dist/lib/image/WebPLossy.rgr +1929 -0
- package/dist/lib/image/ranger.json +9 -0
- package/dist/lib/image/testdata/webp/anim_2f_24x16.webp +0 -0
- package/dist/lib/image/testdata/webp/fixtures.txt +33 -0
- package/dist/lib/image/testdata/webp/gen_fixtures.py +261 -0
- package/dist/lib/image/testdata/webp/ll_1x1.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_alpha_29x21.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_gradient_64x64.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_meta_32x32.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_noise_17x9.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_pal11_23x11.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_pal2_19x7.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_pal40_16x16.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_pal4_13x5.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_photo_40x30.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_predictors_64x32.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_1x1.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_raw_f0_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_raw_f1_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_raw_f2_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_raw_f3_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_vp8l_f0_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_vp8l_f1_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_vp8l_f2_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_vp8l_f3_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alpha_33x17.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_exif_15x11.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_i16_96x80.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_nofilter_24x20.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_odd_17x9.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_photo_48x40.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_q100_20x18.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_sharp_40x36.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_simple_filter_33x31.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_vpx_parts8_37x45.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_vpx_skip_96x80.webp +0 -0
- package/dist/lib/image/tests/WebPDecodeTool.rgr +81 -0
- package/dist/lib/image/tests/WebPDecoderTest.rgr +1162 -0
- package/dist/lib/image/tests/run_webp_tests.sh +39 -0
- package/dist/lib/rust/RsJson.rgr +468 -0
- package/dist/lib/rust/RsPrelude.rgr +1663 -0
- package/dist/lib/shell_test.rgr +7 -7
- package/dist/lib/stdlib.rgr +21 -5
- package/dist/lib/zip/ranger.json +6 -0
- package/dist/rgrc.js +87318 -67610
- package/package.json +1010 -1040
- package/dist/README.md +0 -117
- package/dist/package.json +0 -47
|
@@ -0,0 +1,1721 @@
|
|
|
1
|
+
; SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
; ==============================================================================
|
|
4
|
+
; EVGPatch — the only way an agent is allowed to change a tree
|
|
5
|
+
; ==============================================================================
|
|
6
|
+
;
|
|
7
|
+
; An LLM that edits a document needs three things this engine already has:
|
|
8
|
+
; a stable address for a node, a change that is validated before it lands, and
|
|
9
|
+
; a way to put it back. This is the second and third of those.
|
|
10
|
+
;
|
|
11
|
+
; The rule the whole file exists to enforce: THE MODEL NEVER WRITES CODE INTO
|
|
12
|
+
; THE DOCUMENT. It writes ops — `set-prop`, `set-text`, `insert`, `remove`,
|
|
13
|
+
; `move` — each addressed by an `EVGInspect` path, each validated against the
|
|
14
|
+
; engine's real property set, and each carrying its own inverse. The worst a
|
|
15
|
+
; bad generation can do is be rejected.
|
|
16
|
+
;
|
|
17
|
+
; --- VALIDATION IS NOT A LIST HERE --------------------------------------------
|
|
18
|
+
;
|
|
19
|
+
; There is no whitelist of property names in this file, and that is deliberate:
|
|
20
|
+
; a second list would drift from the first. `EVGElement.setAttribute` already
|
|
21
|
+
; ends with `EVGReject.note("unknown property" …)` for anything it does not
|
|
22
|
+
; implement, so the engine itself is the validator. A `set-prop` clears nothing
|
|
23
|
+
; and counts nothing global: it reads `EVGReject.noteTotal()` before and after,
|
|
24
|
+
; and a note that appeared during the write is the rejection.
|
|
25
|
+
;
|
|
26
|
+
; What this file DOES enumerate is the readable set — `readProp` — because an
|
|
27
|
+
; op has to be invertible before it is allowed to apply. That is the stronger
|
|
28
|
+
; rule and it is the one worth stating: **an op that cannot be undone is not
|
|
29
|
+
; applied**. So the patchable set is exactly the set this file can read back,
|
|
30
|
+
; and a property outside it is rejected with that as the reason rather than
|
|
31
|
+
; applied and lost.
|
|
32
|
+
;
|
|
33
|
+
; --- ALL OR NOTHING -----------------------------------------------------------
|
|
34
|
+
;
|
|
35
|
+
; A rejected op rolls the whole patch back. "3 of 5 applied" hands the caller a
|
|
36
|
+
; document in a state nobody designed and a diff nobody asked for; the agent
|
|
37
|
+
; then has to work out which 3. Failing the batch keeps the document exactly as
|
|
38
|
+
; it was, and the reason says which op and why.
|
|
39
|
+
;
|
|
40
|
+
; --- ADDRESSES ARE REBUILT, NOT CACHED ----------------------------------------
|
|
41
|
+
;
|
|
42
|
+
; `insert`, `remove` and `move` change what every later path means, so the
|
|
43
|
+
; `EVGInspect` index is rebuilt before each op rather than once per patch. That
|
|
44
|
+
; is O(n) per op and it is the right trade at this size: the alternative is a
|
|
45
|
+
; path-rewriting scheme that is wrong in exactly the cases — an unkeyed sibling
|
|
46
|
+
; shifting under an insert — that the inspector's own doc comment warns about.
|
|
47
|
+
;
|
|
48
|
+
; --- PARENTS ------------------------------------------------------------------
|
|
49
|
+
;
|
|
50
|
+
; `EVGElement.addChild` deliberately keeps no `parent` back-reference (it broke
|
|
51
|
+
; the C++ backend — see its comment). So nothing here reads `el.parent`. The
|
|
52
|
+
; parent of a node is its path with the last segment removed, which is what
|
|
53
|
+
; `EVGInspect` already tracks, and structural ops go through the parent element
|
|
54
|
+
; found that way.
|
|
55
|
+
; ==============================================================================
|
|
56
|
+
|
|
57
|
+
Import "EVGElement.rgr"
|
|
58
|
+
Import "EVGInspect.rgr"
|
|
59
|
+
Import "EVGReject.rgr"
|
|
60
|
+
Import "EVGUnit.rgr"
|
|
61
|
+
Import "EVGColor.rgr"
|
|
62
|
+
Import "EVGDisplayList.rgr"
|
|
63
|
+
|
|
64
|
+
; One edit. Built through the static helpers below rather than by hand, so the
|
|
65
|
+
; fields that do not apply to a given op stay at their defaults.
|
|
66
|
+
class EVGPatchOp {
|
|
67
|
+
def op:string ""
|
|
68
|
+
def at:string ""
|
|
69
|
+
def prop:string ""
|
|
70
|
+
def value:string ""
|
|
71
|
+
def toPath:string ""
|
|
72
|
+
def index:int 0
|
|
73
|
+
def tag:string ""
|
|
74
|
+
; `insert` may carry a whole SUBTREE rather than one empty tag. A real
|
|
75
|
+
; control is a tree — a switch is a track with a thumb in it — and an
|
|
76
|
+
; agent that can only insert one empty node at a time has to build one
|
|
77
|
+
; out of a dozen ops, which is how a screen ends up with a drawing of a
|
|
78
|
+
; switch instead of a switch. Reading it is the caller's: `evg_agent`
|
|
79
|
+
; parses `"node": {…}` with the same reader that reads a document.
|
|
80
|
+
def node@(optional):EVGElement
|
|
81
|
+
; `restore` only: which detached subtree in the result's stash to put back.
|
|
82
|
+
def stash:int 0
|
|
83
|
+
|
|
84
|
+
Constructor () {}
|
|
85
|
+
|
|
86
|
+
sfn setProp:EVGPatchOp (at:string prop:string value:string) {
|
|
87
|
+
def o (new EVGPatchOp())
|
|
88
|
+
o.op = "set-prop"
|
|
89
|
+
o.at = at
|
|
90
|
+
o.prop = prop
|
|
91
|
+
o.value = value
|
|
92
|
+
return o
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
; A node's `id` is not a property — nothing paints it, and the patchable
|
|
96
|
+
; set is about what the engine can read back. It is the node's NAME: the
|
|
97
|
+
; hit test answers with it, and an app turns a press on it into an event.
|
|
98
|
+
; Without an op for it an agent can only name a node by rewriting the file
|
|
99
|
+
; by hand, which is how a screen ends up with buttons nothing can press.
|
|
100
|
+
sfn setId:EVGPatchOp (at:string value:string) {
|
|
101
|
+
def o (new EVGPatchOp())
|
|
102
|
+
o.op = "set-id"
|
|
103
|
+
o.at = at
|
|
104
|
+
o.value = value
|
|
105
|
+
return o
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
; The document's stylesheet, whole. It is not a property of a node — it
|
|
109
|
+
; belongs to the document — so it has an op of its own rather than a place
|
|
110
|
+
; in the patchable set. `at` is ignored; there is one sheet.
|
|
111
|
+
sfn setCss:EVGPatchOp (value:string) {
|
|
112
|
+
def o (new EVGPatchOp())
|
|
113
|
+
o.op = "set-css"
|
|
114
|
+
o.at = "0"
|
|
115
|
+
o.value = value
|
|
116
|
+
return o
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
sfn setText:EVGPatchOp (at:string value:string) {
|
|
120
|
+
def o (new EVGPatchOp())
|
|
121
|
+
o.op = "set-text"
|
|
122
|
+
o.at = at
|
|
123
|
+
o.value = value
|
|
124
|
+
return o
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
sfn insertNode:EVGPatchOp (at:string index:int tag:string) {
|
|
128
|
+
def o (new EVGPatchOp())
|
|
129
|
+
o.op = "insert"
|
|
130
|
+
o.at = at
|
|
131
|
+
o.index = index
|
|
132
|
+
o.tag = tag
|
|
133
|
+
return o
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
; A subtree, put in whole. Its inverse is the same `remove` an empty
|
|
137
|
+
; insert gets, so undo needs to know nothing about this.
|
|
138
|
+
sfn insertTree:EVGPatchOp (at:string index:int kid:EVGElement) {
|
|
139
|
+
def o (new EVGPatchOp())
|
|
140
|
+
o.op = "insert"
|
|
141
|
+
o.at = at
|
|
142
|
+
o.index = index
|
|
143
|
+
o.tag = kid.tagName
|
|
144
|
+
o.node = kid
|
|
145
|
+
return o
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
sfn removeNode:EVGPatchOp (at:string) {
|
|
149
|
+
def o (new EVGPatchOp())
|
|
150
|
+
o.op = "remove"
|
|
151
|
+
o.at = at
|
|
152
|
+
return o
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
sfn move:EVGPatchOp (at:string toPath:string index:int) {
|
|
156
|
+
def o (new EVGPatchOp())
|
|
157
|
+
o.op = "move"
|
|
158
|
+
o.at = at
|
|
159
|
+
o.toPath = toPath
|
|
160
|
+
o.index = index
|
|
161
|
+
return o
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
sfn restore:EVGPatchOp (at:string index:int stash:int) {
|
|
165
|
+
def o (new EVGPatchOp())
|
|
166
|
+
o.op = "restore"
|
|
167
|
+
o.at = at
|
|
168
|
+
o.index = index
|
|
169
|
+
o.stash = stash
|
|
170
|
+
return o
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
fn describe:string () {
|
|
174
|
+
def s:string op
|
|
175
|
+
s = ((s + " ") + at)
|
|
176
|
+
if ((strlen prop) > 0) {
|
|
177
|
+
s = ((s + " ") + prop)
|
|
178
|
+
}
|
|
179
|
+
if ((strlen value) > 0) {
|
|
180
|
+
s = ((s + "=") + value)
|
|
181
|
+
}
|
|
182
|
+
return s
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
; What a patch did, and everything needed to undo it.
|
|
187
|
+
;
|
|
188
|
+
; `inverse` is in APPLY order, so undoing means walking it backwards. That is
|
|
189
|
+
; stated here because the alternative — storing it pre-reversed — reads fine
|
|
190
|
+
; until a caller appends two patches' inverses together and the seam is wrong.
|
|
191
|
+
class EVGPatchResult {
|
|
192
|
+
def ok:boolean true
|
|
193
|
+
def applied:int 0
|
|
194
|
+
def inverse:[EVGPatchOp]
|
|
195
|
+
def rejected:[string]
|
|
196
|
+
; Subtrees taken out by `remove`, kept alive so `restore` can put them back.
|
|
197
|
+
def stash:[EVGElement]
|
|
198
|
+
|
|
199
|
+
Constructor () {
|
|
200
|
+
def a:[EVGPatchOp]
|
|
201
|
+
inverse = a
|
|
202
|
+
def b:[string]
|
|
203
|
+
rejected = b
|
|
204
|
+
def c:[EVGElement]
|
|
205
|
+
stash = c
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
fn reason:string () {
|
|
209
|
+
if ((array_length rejected) == 0) {
|
|
210
|
+
return ""
|
|
211
|
+
}
|
|
212
|
+
return (itemAt rejected 0)
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
class EVGPatch {
|
|
217
|
+
def insp:EVGInspect
|
|
218
|
+
def res:EVGPatchResult
|
|
219
|
+
; Off while rolling back: an undo must not record an undo of itself.
|
|
220
|
+
def recording:boolean true
|
|
221
|
+
|
|
222
|
+
Constructor () {
|
|
223
|
+
insp = (new EVGInspect())
|
|
224
|
+
res = (new EVGPatchResult())
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
; --- the entry point ------------------------------------------------------
|
|
228
|
+
|
|
229
|
+
fn apply:EVGPatchResult (root:EVGElement ops:[EVGPatchOp]) {
|
|
230
|
+
def out (new EVGPatchResult())
|
|
231
|
+
res = out
|
|
232
|
+
recording = true
|
|
233
|
+
def i:int 0
|
|
234
|
+
while (i < (array_length ops)) {
|
|
235
|
+
def op:EVGPatchOp (itemAt ops i)
|
|
236
|
+
def why:string (this.applyOne(root op))
|
|
237
|
+
if ((strlen why) > 0) {
|
|
238
|
+
def msg:string ((((("op " + (to_string i)) + " (") + (op.describe())) + "): ") + why)
|
|
239
|
+
push out.rejected msg
|
|
240
|
+
this.rollback(root)
|
|
241
|
+
out.ok = false
|
|
242
|
+
out.applied = 0
|
|
243
|
+
return out
|
|
244
|
+
}
|
|
245
|
+
out.applied = (out.applied + 1)
|
|
246
|
+
i = (i + 1)
|
|
247
|
+
}
|
|
248
|
+
return out
|
|
249
|
+
} doc {
|
|
250
|
+
description "Applies ops to a tree, all or nothing, recording an inverse for each."
|
|
251
|
+
param root "The tree to edit."
|
|
252
|
+
param ops "The edits, applied in order."
|
|
253
|
+
returns "What was applied, why it was not, and how to undo it."
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
; Walk the recorded inverse backwards. Used on rejection, and callable by a
|
|
257
|
+
; host that wants an undo button.
|
|
258
|
+
fn rollback:void (root:EVGElement) {
|
|
259
|
+
recording = false
|
|
260
|
+
def i:int ((array_length res.inverse) - 1)
|
|
261
|
+
while (i >= 0) {
|
|
262
|
+
def op:EVGPatchOp (itemAt res.inverse i)
|
|
263
|
+
def ignored:string (this.applyOne(root op))
|
|
264
|
+
i = (i - 1)
|
|
265
|
+
}
|
|
266
|
+
recording = true
|
|
267
|
+
} doc {
|
|
268
|
+
description "Undoes an applied patch, newest edit first."
|
|
269
|
+
param root "The tree the patch was applied to."
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
; --- one op ---------------------------------------------------------------
|
|
273
|
+
;
|
|
274
|
+
; Returns "" when it applied, and the reason when it did not. Nothing here
|
|
275
|
+
; throws: a rejection is data the caller shows the model, not an exception.
|
|
276
|
+
|
|
277
|
+
fn applyOne:string (root:EVGElement op:EVGPatchOp) {
|
|
278
|
+
insp.build(root)
|
|
279
|
+
if (op.op == "set-prop") {
|
|
280
|
+
return (this.doSetProp(op))
|
|
281
|
+
}
|
|
282
|
+
if (op.op == "set-text") {
|
|
283
|
+
return (this.doSetText(op))
|
|
284
|
+
}
|
|
285
|
+
if (op.op == "set-id") {
|
|
286
|
+
return (this.doSetId(op))
|
|
287
|
+
}
|
|
288
|
+
if (op.op == "set-css") {
|
|
289
|
+
return (this.doSetCss(root op))
|
|
290
|
+
}
|
|
291
|
+
if (op.op == "insert") {
|
|
292
|
+
return (this.doInsert(op))
|
|
293
|
+
}
|
|
294
|
+
if (op.op == "remove") {
|
|
295
|
+
return (this.doRemove(op))
|
|
296
|
+
}
|
|
297
|
+
if (op.op == "move") {
|
|
298
|
+
return (this.doMove(op))
|
|
299
|
+
}
|
|
300
|
+
if (op.op == "restore") {
|
|
301
|
+
return (this.doRestore(op))
|
|
302
|
+
}
|
|
303
|
+
return ("unknown op \"" + (op.op + "\""))
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
fn record:void (op:EVGPatchOp) {
|
|
307
|
+
if recording {
|
|
308
|
+
push res.inverse op
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
; --- set-prop -------------------------------------------------------------
|
|
313
|
+
|
|
314
|
+
fn doSetProp:string (op:EVGPatchOp) {
|
|
315
|
+
def hit@(optional):EVGElement (insp.elementAt(op.at))
|
|
316
|
+
if (null? hit) {
|
|
317
|
+
return (this.noSuchNode(op.at))
|
|
318
|
+
}
|
|
319
|
+
def el:EVGElement (unwrap hit)
|
|
320
|
+
def name:string (EVGElement.toKebab(op.prop))
|
|
321
|
+
if ((EVGPatch.readable(name)) == false) {
|
|
322
|
+
return ((("property \"" + name) + "\" is not patchable — nothing here can read it back, so the edit could not be undone"))
|
|
323
|
+
}
|
|
324
|
+
def before:string (EVGPatch.readProp(el name))
|
|
325
|
+
def wasInline:boolean (el.hasInline(name))
|
|
326
|
+
def notesBefore:int (EVGReject.noteTotal())
|
|
327
|
+
if (EVGReject.isAbsent(op.value)) {
|
|
328
|
+
def cleared:boolean (EVGPatch.clearProp(el name))
|
|
329
|
+
if (cleared == false) {
|
|
330
|
+
return ((("property \"" + name) + "\" cannot be cleared"))
|
|
331
|
+
}
|
|
332
|
+
el.unmarkInline(name)
|
|
333
|
+
} {
|
|
334
|
+
el.setAttribute(name op.value)
|
|
335
|
+
el.markInline(name)
|
|
336
|
+
}
|
|
337
|
+
def notesAfter:int (EVGReject.noteTotal())
|
|
338
|
+
if (notesAfter > notesBefore) {
|
|
339
|
+
def last:int ((EVGReject.noteCount()) - 1)
|
|
340
|
+
def why:string "rejected by the engine"
|
|
341
|
+
if (last >= 0) {
|
|
342
|
+
why = (EVGReject.noteAt(last))
|
|
343
|
+
}
|
|
344
|
+
; Put it back exactly as it was before complaining.
|
|
345
|
+
def undo:boolean (EVGPatch.clearProp(el name))
|
|
346
|
+
if ((strlen before) > 0) {
|
|
347
|
+
el.setAttribute(name before)
|
|
348
|
+
}
|
|
349
|
+
if (wasInline == false) {
|
|
350
|
+
el.unmarkInline(name)
|
|
351
|
+
}
|
|
352
|
+
return why
|
|
353
|
+
}
|
|
354
|
+
def inv:EVGPatchOp (EVGPatchOp.setProp(op.at name before))
|
|
355
|
+
this.record(inv)
|
|
356
|
+
return ""
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
; --- set-css --------------------------------------------------------------
|
|
360
|
+
|
|
361
|
+
fn doSetCss:string (root:EVGElement op:EVGPatchOp) {
|
|
362
|
+
def want:string op.value
|
|
363
|
+
if (EVGReject.isAbsent(want)) {
|
|
364
|
+
want = ""
|
|
365
|
+
}
|
|
366
|
+
def before:string root.documentCss
|
|
367
|
+
root.documentCss = want
|
|
368
|
+
def inv:EVGPatchOp (EVGPatchOp.setCss(before))
|
|
369
|
+
this.record(inv)
|
|
370
|
+
return ""
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
; --- set-id ---------------------------------------------------------------
|
|
374
|
+
|
|
375
|
+
fn doSetId:string (op:EVGPatchOp) {
|
|
376
|
+
def hit@(optional):EVGElement (insp.elementAt(op.at))
|
|
377
|
+
if (null? hit) {
|
|
378
|
+
return (this.noSuchNode(op.at))
|
|
379
|
+
}
|
|
380
|
+
def el:EVGElement (unwrap hit)
|
|
381
|
+
def want:string op.value
|
|
382
|
+
if (EVGReject.isAbsent(want)) {
|
|
383
|
+
want = ""
|
|
384
|
+
}
|
|
385
|
+
; Two nodes with one id is a screen where the same press means two
|
|
386
|
+
; things, and the hit test can only answer with one of them. Say which
|
|
387
|
+
; node already has it rather than let the second one look live.
|
|
388
|
+
if ((strlen want) > 0) {
|
|
389
|
+
def other:string (this.pathOfId(want op.at))
|
|
390
|
+
if ((strlen other) > 0) {
|
|
391
|
+
return ((("id \"" + want) + ("\" is already on " + (other + " — an id names one node, and a press can only reach one of them"))))
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
def before:string el.id
|
|
395
|
+
el.id = want
|
|
396
|
+
def inv:EVGPatchOp (EVGPatchOp.setId(op.at before))
|
|
397
|
+
this.record(inv)
|
|
398
|
+
return ""
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
; The path of the node carrying `id`, ignoring `skip` (the node being named,
|
|
402
|
+
; which may already carry it). "" when nothing else has it.
|
|
403
|
+
fn pathOfId:string (id:string skip:string) {
|
|
404
|
+
def n:int (insp.count())
|
|
405
|
+
def i:int 0
|
|
406
|
+
while (i < n) {
|
|
407
|
+
def p:string (insp.pathAt(i))
|
|
408
|
+
if (p != skip) {
|
|
409
|
+
def hit@(optional):EVGElement (insp.elementAt(p))
|
|
410
|
+
if (null? hit) {
|
|
411
|
+
} {
|
|
412
|
+
if ((unwrap hit).id == id) {
|
|
413
|
+
return p
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
i = (i + 1)
|
|
418
|
+
}
|
|
419
|
+
return ""
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
; --- set-text -------------------------------------------------------------
|
|
423
|
+
|
|
424
|
+
fn doSetText:string (op:EVGPatchOp) {
|
|
425
|
+
def hit@(optional):EVGElement (insp.elementAt(op.at))
|
|
426
|
+
if (null? hit) {
|
|
427
|
+
return (this.noSuchNode(op.at))
|
|
428
|
+
}
|
|
429
|
+
def el:EVGElement (unwrap hit)
|
|
430
|
+
if ((array_length el.children) > 0) {
|
|
431
|
+
return "set-text on a node with children — remove them first, or write to the child that holds the text"
|
|
432
|
+
}
|
|
433
|
+
def before:string el.textContent
|
|
434
|
+
el.textContent = op.value
|
|
435
|
+
def inv:EVGPatchOp (EVGPatchOp.setText(op.at before))
|
|
436
|
+
this.record(inv)
|
|
437
|
+
return ""
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
; --- insert ---------------------------------------------------------------
|
|
441
|
+
|
|
442
|
+
fn doInsert:string (op:EVGPatchOp) {
|
|
443
|
+
def hit@(optional):EVGElement (insp.elementAt(op.at))
|
|
444
|
+
if (null? hit) {
|
|
445
|
+
return (this.noSuchNode(op.at))
|
|
446
|
+
}
|
|
447
|
+
def parent:EVGElement (unwrap hit)
|
|
448
|
+
def made@(optional):EVGElement (EVGPatch.create(op.tag))
|
|
449
|
+
if (op.node) {
|
|
450
|
+
made = op.node
|
|
451
|
+
}
|
|
452
|
+
if (null? made) {
|
|
453
|
+
return ((("no element type for tag \"" + op.tag) + "\" — div, span, img or path"))
|
|
454
|
+
}
|
|
455
|
+
def kid:EVGElement (unwrap made)
|
|
456
|
+
def at:int (EVGPatch.clampIndex(op.index (array_length parent.children)))
|
|
457
|
+
EVGPatch.insertAt(parent kid at)
|
|
458
|
+
def inv:EVGPatchOp (EVGPatchOp.removeNode((EVGPatch.childPath(op.at parent at))))
|
|
459
|
+
this.record(inv)
|
|
460
|
+
return ""
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
; --- remove ---------------------------------------------------------------
|
|
464
|
+
|
|
465
|
+
fn doRemove:string (op:EVGPatchOp) {
|
|
466
|
+
if (op.at == "0") {
|
|
467
|
+
return "cannot remove the root"
|
|
468
|
+
}
|
|
469
|
+
def hit@(optional):EVGElement (insp.elementAt(op.at))
|
|
470
|
+
if (null? hit) {
|
|
471
|
+
return (this.noSuchNode(op.at))
|
|
472
|
+
}
|
|
473
|
+
def el:EVGElement (unwrap hit)
|
|
474
|
+
def parentPath:string (insp.parentOf(op.at))
|
|
475
|
+
def ph@(optional):EVGElement (insp.elementAt(parentPath))
|
|
476
|
+
if (null? ph) {
|
|
477
|
+
return (this.noSuchNode(parentPath))
|
|
478
|
+
}
|
|
479
|
+
def parent:EVGElement (unwrap ph)
|
|
480
|
+
def at:int (EVGPatch.indexOfChild(parent el))
|
|
481
|
+
if (at < 0) {
|
|
482
|
+
return "the node is not among its parent's children — the index is stale"
|
|
483
|
+
}
|
|
484
|
+
EVGPatch.removeAt(parent at)
|
|
485
|
+
push res.stash el
|
|
486
|
+
def slot:int ((array_length res.stash) - 1)
|
|
487
|
+
def inv:EVGPatchOp (EVGPatchOp.restore(parentPath at slot))
|
|
488
|
+
this.record(inv)
|
|
489
|
+
return ""
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
; `restore` is the inverse of `remove` and exists only for that. It puts a
|
|
493
|
+
; stashed subtree back where it was, which is why it names a slot and not a
|
|
494
|
+
; tag: the node is the same object, with its children and state intact.
|
|
495
|
+
fn doRestore:string (op:EVGPatchOp) {
|
|
496
|
+
def hit@(optional):EVGElement (insp.elementAt(op.at))
|
|
497
|
+
if (null? hit) {
|
|
498
|
+
return (this.noSuchNode(op.at))
|
|
499
|
+
}
|
|
500
|
+
def parent:EVGElement (unwrap hit)
|
|
501
|
+
if (op.stash >= (array_length res.stash)) {
|
|
502
|
+
return "nothing stashed at that slot"
|
|
503
|
+
}
|
|
504
|
+
def el:EVGElement (itemAt res.stash op.stash)
|
|
505
|
+
def at:int (EVGPatch.clampIndex(op.index (array_length parent.children)))
|
|
506
|
+
EVGPatch.insertAt(parent el at)
|
|
507
|
+
def inv:EVGPatchOp (EVGPatchOp.removeNode((EVGPatch.childPath(op.at parent at))))
|
|
508
|
+
this.record(inv)
|
|
509
|
+
return ""
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
; --- move -----------------------------------------------------------------
|
|
513
|
+
|
|
514
|
+
fn doMove:string (op:EVGPatchOp) {
|
|
515
|
+
if (op.at == "0") {
|
|
516
|
+
return "cannot move the root"
|
|
517
|
+
}
|
|
518
|
+
def hit@(optional):EVGElement (insp.elementAt(op.at))
|
|
519
|
+
if (null? hit) {
|
|
520
|
+
return (this.noSuchNode(op.at))
|
|
521
|
+
}
|
|
522
|
+
def el:EVGElement (unwrap hit)
|
|
523
|
+
def destHit@(optional):EVGElement (insp.elementAt(op.toPath))
|
|
524
|
+
if (null? destHit) {
|
|
525
|
+
return (this.noSuchNode(op.toPath))
|
|
526
|
+
}
|
|
527
|
+
def dest:EVGElement (unwrap destHit)
|
|
528
|
+
if (EVGPatch.subtreeHas(el dest)) {
|
|
529
|
+
return "cannot move a node into itself or its own subtree"
|
|
530
|
+
}
|
|
531
|
+
def fromPath:string (insp.parentOf(op.at))
|
|
532
|
+
def fh@(optional):EVGElement (insp.elementAt(fromPath))
|
|
533
|
+
if (null? fh) {
|
|
534
|
+
return (this.noSuchNode(fromPath))
|
|
535
|
+
}
|
|
536
|
+
def from:EVGElement (unwrap fh)
|
|
537
|
+
def at:int (EVGPatch.indexOfChild(from el))
|
|
538
|
+
if (at < 0) {
|
|
539
|
+
return "the node is not among its parent's children — the index is stale"
|
|
540
|
+
}
|
|
541
|
+
EVGPatch.removeAt(from at)
|
|
542
|
+
def landing:int (EVGPatch.clampIndex(op.index (array_length dest.children)))
|
|
543
|
+
EVGPatch.insertAt(dest el landing)
|
|
544
|
+
; The inverse names the node by where it now IS, and puts it back by
|
|
545
|
+
; the parent path and index it came from.
|
|
546
|
+
def nowPath:string (EVGPatch.childPath(op.toPath dest landing))
|
|
547
|
+
def inv:EVGPatchOp (EVGPatchOp.move(nowPath fromPath at))
|
|
548
|
+
this.record(inv)
|
|
549
|
+
return ""
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
fn noSuchNode:string (path:string) {
|
|
553
|
+
def n:int (insp.count())
|
|
554
|
+
return ((((("no node at \"" + path) + "\" (") + (to_string n)) + " nodes in this tree)"))
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
; --- children, as arrays --------------------------------------------------
|
|
558
|
+
;
|
|
559
|
+
; There is no array insert in the standard ops, so both of these rebuild the
|
|
560
|
+
; list. At the size of a child list that is cheaper than being clever.
|
|
561
|
+
|
|
562
|
+
sfn insertAt:void (parent:EVGElement kid:EVGElement at:int) {
|
|
563
|
+
def rebuilt:[EVGElement]
|
|
564
|
+
def i:int 0
|
|
565
|
+
def n:int (array_length parent.children)
|
|
566
|
+
while (i < n) {
|
|
567
|
+
if (i == at) {
|
|
568
|
+
push rebuilt kid
|
|
569
|
+
}
|
|
570
|
+
push rebuilt (itemAt parent.children i)
|
|
571
|
+
i = (i + 1)
|
|
572
|
+
}
|
|
573
|
+
if (at >= n) {
|
|
574
|
+
push rebuilt kid
|
|
575
|
+
}
|
|
576
|
+
parent.children = rebuilt
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
sfn removeAt:void (parent:EVGElement at:int) {
|
|
580
|
+
def rebuilt:[EVGElement]
|
|
581
|
+
def i:int 0
|
|
582
|
+
def n:int (array_length parent.children)
|
|
583
|
+
while (i < n) {
|
|
584
|
+
if (i != at) {
|
|
585
|
+
push rebuilt (itemAt parent.children i)
|
|
586
|
+
}
|
|
587
|
+
i = (i + 1)
|
|
588
|
+
}
|
|
589
|
+
parent.children = rebuilt
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
; Identity is `inspectSlot`, which `EVGInspect.build` has just stamped on
|
|
593
|
+
; every node in this tree — the same handle the display list uses to say
|
|
594
|
+
; which element emitted a command. A node that carries no slot was never in
|
|
595
|
+
; the index, and answering for it would be a guess.
|
|
596
|
+
sfn indexOfChild:int (parent:EVGElement kid:EVGElement) {
|
|
597
|
+
if (kid.inspectSlot < 0) {
|
|
598
|
+
return (0 - 1)
|
|
599
|
+
}
|
|
600
|
+
def i:int 0
|
|
601
|
+
while (i < (array_length parent.children)) {
|
|
602
|
+
def c:EVGElement (itemAt parent.children i)
|
|
603
|
+
if (c.inspectSlot == kid.inspectSlot) {
|
|
604
|
+
return i
|
|
605
|
+
}
|
|
606
|
+
i = (i + 1)
|
|
607
|
+
}
|
|
608
|
+
return (0 - 1)
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
sfn subtreeHas:boolean (el:EVGElement maybe:EVGElement) {
|
|
612
|
+
if (el.inspectSlot == maybe.inspectSlot) {
|
|
613
|
+
|
|
614
|
+
return true
|
|
615
|
+
}
|
|
616
|
+
def i:int 0
|
|
617
|
+
while (i < (array_length el.children)) {
|
|
618
|
+
def c:EVGElement (itemAt el.children i)
|
|
619
|
+
if (EVGPatch.subtreeHas(c maybe)) {
|
|
620
|
+
|
|
621
|
+
return true
|
|
622
|
+
}
|
|
623
|
+
i = (i + 1)
|
|
624
|
+
}
|
|
625
|
+
return false
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
sfn clampIndex:int (want:int n:int) {
|
|
629
|
+
if (want < 0) {
|
|
630
|
+
return 0
|
|
631
|
+
}
|
|
632
|
+
if (want > n) {
|
|
633
|
+
return n
|
|
634
|
+
}
|
|
635
|
+
return want
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
; The path a child at `at` has under `parentPath`, by the same rule
|
|
639
|
+
; `EVGInspect.visit` uses: the key when there is one, the index otherwise.
|
|
640
|
+
sfn childPath:string (parentPath:string parent:EVGElement at:int) {
|
|
641
|
+
def seg:string (to_string at)
|
|
642
|
+
if (at < (array_length parent.children)) {
|
|
643
|
+
def kid:EVGElement (itemAt parent.children at)
|
|
644
|
+
if ((strlen kid.key) > 0) {
|
|
645
|
+
seg = ("k:" + kid.key)
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
return ((parentPath + "/") + seg)
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
; --- the patchable set ----------------------------------------------------
|
|
653
|
+
;
|
|
654
|
+
; `kindOf` is the ONE list. `readable` is defined from it, and `readProp`
|
|
655
|
+
; and `clearProp` answer by name for the names it admits — so a name this
|
|
656
|
+
; admits and they do not is a bug, and `EVGPatchTest` walks the list on a
|
|
657
|
+
; fresh element to catch exactly that.
|
|
658
|
+
;
|
|
659
|
+
; Membership is narrower than what `setAttribute` accepts, and the missing
|
|
660
|
+
; ones are missing for a reason: a property this file cannot read back
|
|
661
|
+
; cannot be inverted, and an op that cannot be inverted does not apply.
|
|
662
|
+
|
|
663
|
+
sfn KIND_NONE:int () { return 0 }
|
|
664
|
+
sfn KIND_STRING:int () { return 1 }
|
|
665
|
+
sfn KIND_UNIT:int () { return 2 }
|
|
666
|
+
sfn KIND_COLOR:int () { return 3 }
|
|
667
|
+
sfn KIND_NUMBER:int () { return 4 }
|
|
668
|
+
; There is no separate "box" kind. A box side is a length like any other —
|
|
669
|
+
; the difference is only WHICH field to read, and reading the wrong one is
|
|
670
|
+
; the mistake this note exists to prevent: `EVGBox` carries the authored
|
|
671
|
+
; `paddingLeft:EVGUnit` AND the `paddingLeftPx` that layout resolves from
|
|
672
|
+
; it. `EVGInspect` prints the px, correctly, because an inspector reports
|
|
673
|
+
; what layout did. A patch must read the unit, because an inverse has to
|
|
674
|
+
; restore what the author wrote and the px is 0 until the first pass.
|
|
675
|
+
|
|
676
|
+
; `class` is what an author writes and `class-name` is what `setAttribute`
|
|
677
|
+
; answers to, so the alias is resolved once, here, rather than at each use.
|
|
678
|
+
; `evg-fx-<something>`. A family rather than a list, because the names
|
|
679
|
+
; belong to whichever plugin reads them and this file will never know
|
|
680
|
+
; them all.
|
|
681
|
+
sfn isFx:boolean (name:string) {
|
|
682
|
+
if ((strlen name) <= 7) {
|
|
683
|
+
return false
|
|
684
|
+
}
|
|
685
|
+
return ((substring name 0 7) == "evg-fx-")
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
; The `evg-fx-*` properties THIS element carries, full names. `patchableNames`
|
|
689
|
+
; cannot list them — they are per element, not per format — so anything that
|
|
690
|
+
; walks the static list and wants the whole truth walks this too.
|
|
691
|
+
sfn fxNamesOf:[string] (el:EVGElement) {
|
|
692
|
+
def out:[string]
|
|
693
|
+
def i:int 0
|
|
694
|
+
while (i < (array_length el.fxNames)) {
|
|
695
|
+
push out ("evg-fx-" + (itemAt el.fxNames i))
|
|
696
|
+
i = (i + 1)
|
|
697
|
+
}
|
|
698
|
+
return out
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
sfn canonical:string (name:string) {
|
|
702
|
+
def n:string (EVGElement.toKebab(name))
|
|
703
|
+
if (n == "class") {
|
|
704
|
+
return "class-name"
|
|
705
|
+
}
|
|
706
|
+
return n
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
sfn kindOf:int (rawName:string) {
|
|
710
|
+
def name:string (EVGPatch.canonical(rawName))
|
|
711
|
+
if (((name == "display") || (name == "position")) || (name == "flex-direction")) { return 1 }
|
|
712
|
+
if (((name == "justify-content") || (name == "align-items")) || (name == "align-content")) { return 1 }
|
|
713
|
+
if (((name == "flex-wrap") || (name == "grid-area")) || (name == "grid-template-columns")) { return 1 }
|
|
714
|
+
; The rest of the grid vocabulary. It was incomplete, and the hole was
|
|
715
|
+
; invisible in a property list and obvious in a picture: the showcase's
|
|
716
|
+
; card deck places every card with `grid-row: 1 / 3` against the deck's
|
|
717
|
+
; `subgrid` rows, so a conversion that dropped `grid-row` laid the
|
|
718
|
+
; captions on top of the paragraph below them.
|
|
719
|
+
if (((name == "grid-row") || (name == "grid-column")) || (name == "grid-template-areas")) { return 1 }
|
|
720
|
+
if (name == "grid-auto-flow") { return 1 }
|
|
721
|
+
if (((name == "grid-template-rows") || (name == "font-family")) || (name == "font-weight")) { return 1 }
|
|
722
|
+
if (((name == "text-align") || (name == "overflow")) || (name == "cursor")) { return 1 }
|
|
723
|
+
if (((name == "class-name") || (name == "theme")) || (name == "src")) { return 1 }
|
|
724
|
+
if (((name == "object-fit") || (name == "transform")) || (name == "transition")) { return 1 }
|
|
725
|
+
; A turn is two properties, not one. `transform` says by how much;
|
|
726
|
+
; `transform-origin` says about WHAT, and its default is the box's own
|
|
727
|
+
; centre — which is the wrong pivot for anything anchored at a point.
|
|
728
|
+
; A chart's turned axis label reads as centred on its tick until this
|
|
729
|
+
; is carried, and then slides half its own length along the axis.
|
|
730
|
+
; `white-space` belongs with them for a related reason: a label placed
|
|
731
|
+
; in a box measured by an ESTIMATE wraps the moment the estimate is a
|
|
732
|
+
; pixel short, and "markdown" comes back as "markdo".
|
|
733
|
+
if ((name == "transform-origin") || (name == "white-space")) { return 1 }
|
|
734
|
+
if (name == "background-gradient") { return 1 }
|
|
735
|
+
; The vector vocabulary. Left out of the first draft, and running the
|
|
736
|
+
; converter over the showcase said what that cost: every chart, every
|
|
737
|
+
; diagram and every piece of imported SVG lost its geometry — 72 paths
|
|
738
|
+
; on one page alone — and came out as correctly-sized empty boxes.
|
|
739
|
+
; A format for editing documents that cannot hold a chart is not one.
|
|
740
|
+
if (((name == "d") || (name == "view-box")) || (name == "fill-rule")) { return 1 }
|
|
741
|
+
if (((name == "stroke-linecap") || (name == "stroke-linejoin")) || (name == "stroke-dasharray")) { return 1 }
|
|
742
|
+
if ((name == "alt") || (name == "href")) { return 1 }
|
|
743
|
+
; Anchors and connectors. The geometry ones are names and keywords;
|
|
744
|
+
; the three lengths are plain numbers, the way `stroke-width` is.
|
|
745
|
+
; Surfaces: the overlay vocabulary and CSS Anchor Positioning's.
|
|
746
|
+
; Without these the editable format could not express a menu at all —
|
|
747
|
+
; a document could hold the rows and not the fact that they are a
|
|
748
|
+
; popover.
|
|
749
|
+
if (((name == "overlay") || (name == "overlay-anchor-role")) || (name == "overlay-side")) { return 1 }
|
|
750
|
+
if (((name == "overlay-align") || (name == "position-anchor")) || (name == "position-area")) { return 1 }
|
|
751
|
+
if (((name == "position-try-fallbacks") || (name == "position-try-order")) || (name == "presentation")) { return 1 }
|
|
752
|
+
if (name == "fit-viewport") { return 1 }
|
|
753
|
+
if ((name == "overlay-gap") || (name == "sheet-below")) { return 4 }
|
|
754
|
+
if (((name == "anchor-name") || (name == "from")) || (name == "to")) { return 1 }
|
|
755
|
+
if (((name == "from-side") || (name == "to-side")) || (name == "routing")) { return 1 }
|
|
756
|
+
if ((name == "arrow-start") || (name == "arrow-end")) { return 1 }
|
|
757
|
+
if (((name == "arrow-size") || (name == "from-offset")) || (name == "to-offset")) { return 4 }
|
|
758
|
+
; An imported SVG document is its TEXT, held on the element and parsed
|
|
759
|
+
; by whoever draws it. Leaving it out cost the showcase's `svg` page
|
|
760
|
+
; every graphic on it while both audits reported the conversion clean —
|
|
761
|
+
; the boxes were right, because the boxes are sized by CSS, and only
|
|
762
|
+
; the painting was empty. It is carried at full length: a document that
|
|
763
|
+
; renders correctly is worth more than a short file.
|
|
764
|
+
if (name == "svg") { return 1 }
|
|
765
|
+
; The two-stop gradient. Found by converting a Figma page that had one:
|
|
766
|
+
; the scene carried it, `SceneToEVG` emitted `gradient-from` and
|
|
767
|
+
; `gradient-to`, and the editable format — which is this set — dropped
|
|
768
|
+
; both, so a gradient fill arrived in the document as nothing at all.
|
|
769
|
+
if (name == "gradient-dir") { return 4 }
|
|
770
|
+
; Surface effects. The engine reads none of this: `starfield` is a
|
|
771
|
+
; name, `evg-fx-density` is a number under a name, and the plugin that
|
|
772
|
+
; draws it is the first thing that attaches meaning to either. Left
|
|
773
|
+
; out of this set they could not be patched and could not be written
|
|
774
|
+
; down — so a document could hold a page with a starfield behind it
|
|
775
|
+
; and lose the starfield the next time anything rewrote the file.
|
|
776
|
+
if ((name == "evg-surface-effect") || (name == "evg-effect-on")) { return 1 }
|
|
777
|
+
if (EVGPatch.isFx(name)) { return 4 }
|
|
778
|
+
|
|
779
|
+
if (((name == "width") || (name == "height")) || (name == "min-width")) { return 2 }
|
|
780
|
+
if (((name == "min-height") || (name == "max-width")) || (name == "max-height")) { return 2 }
|
|
781
|
+
if (((name == "left") || (name == "top")) || (name == "right")) { return 2 }
|
|
782
|
+
if (((name == "bottom") || (name == "gap")) || (name == "font-size")) { return 2 }
|
|
783
|
+
if (name == "flex-basis") { return 2 }
|
|
784
|
+
|
|
785
|
+
if (((name == "color") || (name == "background-color")) || (name == "border-color")) { return 3 }
|
|
786
|
+
if ((name == "fill") || (name == "stroke")) { return 3 }
|
|
787
|
+
if ((name == "gradient-from") || (name == "gradient-to")) { return 3 }
|
|
788
|
+
; `emoji-color` is a TINT, and that is the whole reason it exists: the
|
|
789
|
+
; face is monochrome outlines with no colour of its own, so something
|
|
790
|
+
; has to say what to fill them with, and unset it inherits the text
|
|
791
|
+
; colour. The showcase's emoji page tints three rows through it
|
|
792
|
+
; (`.tintRose`, `.tintBlue`, `.tintGreen`); dropping the property did
|
|
793
|
+
; not lose colours the glyphs had, it lost the tint and let all three
|
|
794
|
+
; fall back to the text colour. Every audit was silent, because the
|
|
795
|
+
; draw commands matched in count and kind and differed only in fill.
|
|
796
|
+
if (name == "emoji-color") { return 3 }
|
|
797
|
+
|
|
798
|
+
if (((name == "opacity") || (name == "line-height")) || (name == "flex-grow")) { return 4 }
|
|
799
|
+
if (((name == "flex-shrink") || (name == "rotate")) || (name == "scale")) { return 4 }
|
|
800
|
+
if ((name == "stroke-width") || (name == "stroke-dashoffset")) { return 4 }
|
|
801
|
+
|
|
802
|
+
if (((name == "margin-top") || (name == "margin-right")) || (name == "margin-bottom")) { return 2 }
|
|
803
|
+
if (((name == "margin-left") || (name == "padding-top")) || (name == "padding-right")) { return 2 }
|
|
804
|
+
if (((name == "padding-bottom") || (name == "padding-left")) || (name == "border-width")) { return 2 }
|
|
805
|
+
if (name == "border-radius") { return 2 }
|
|
806
|
+
|
|
807
|
+
return 0
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
; The same set `kindOf` admits, in a form something can iterate: the
|
|
811
|
+
; serializer walks it to decide what a node has to say, and the test walks
|
|
812
|
+
; it to prove `readProp` and `clearProp` answer for all of it. Kept here
|
|
813
|
+
; rather than in either caller so there is still one list.
|
|
814
|
+
sfn patchableNames:[string] () {
|
|
815
|
+
def a:[string]
|
|
816
|
+
push a "display"
|
|
817
|
+
push a "position"
|
|
818
|
+
push a "flex-direction"
|
|
819
|
+
push a "justify-content"
|
|
820
|
+
push a "align-items"
|
|
821
|
+
push a "align-content"
|
|
822
|
+
push a "flex-wrap"
|
|
823
|
+
push a "grid-area"
|
|
824
|
+
push a "grid-row"
|
|
825
|
+
push a "grid-column"
|
|
826
|
+
push a "grid-template-areas"
|
|
827
|
+
push a "grid-auto-flow"
|
|
828
|
+
push a "grid-template-columns"
|
|
829
|
+
push a "grid-template-rows"
|
|
830
|
+
push a "font-family"
|
|
831
|
+
push a "font-weight"
|
|
832
|
+
push a "text-align"
|
|
833
|
+
push a "overflow"
|
|
834
|
+
push a "cursor"
|
|
835
|
+
push a "class-name"
|
|
836
|
+
push a "theme"
|
|
837
|
+
push a "src"
|
|
838
|
+
push a "object-fit"
|
|
839
|
+
push a "transform"
|
|
840
|
+
push a "transform-origin"
|
|
841
|
+
push a "white-space"
|
|
842
|
+
push a "transition"
|
|
843
|
+
push a "background-gradient"
|
|
844
|
+
push a "svg"
|
|
845
|
+
push a "d"
|
|
846
|
+
push a "view-box"
|
|
847
|
+
push a "fill-rule"
|
|
848
|
+
push a "stroke-linecap"
|
|
849
|
+
push a "stroke-linejoin"
|
|
850
|
+
push a "stroke-dasharray"
|
|
851
|
+
push a "overlay"
|
|
852
|
+
push a "overlay-anchor-role"
|
|
853
|
+
push a "overlay-side"
|
|
854
|
+
push a "overlay-align"
|
|
855
|
+
push a "overlay-gap"
|
|
856
|
+
push a "position-anchor"
|
|
857
|
+
push a "position-try-fallbacks"
|
|
858
|
+
push a "position-try-order"
|
|
859
|
+
push a "presentation"
|
|
860
|
+
push a "sheet-below"
|
|
861
|
+
push a "fit-viewport"
|
|
862
|
+
push a "anchor-name"
|
|
863
|
+
push a "from"
|
|
864
|
+
push a "to"
|
|
865
|
+
push a "from-side"
|
|
866
|
+
push a "to-side"
|
|
867
|
+
push a "routing"
|
|
868
|
+
push a "arrow-start"
|
|
869
|
+
push a "arrow-end"
|
|
870
|
+
push a "arrow-size"
|
|
871
|
+
push a "from-offset"
|
|
872
|
+
push a "to-offset"
|
|
873
|
+
push a "alt"
|
|
874
|
+
push a "href"
|
|
875
|
+
push a "fill"
|
|
876
|
+
push a "stroke"
|
|
877
|
+
push a "emoji-color"
|
|
878
|
+
push a "gradient-from"
|
|
879
|
+
push a "gradient-to"
|
|
880
|
+
push a "gradient-dir"
|
|
881
|
+
push a "stroke-width"
|
|
882
|
+
push a "stroke-dashoffset"
|
|
883
|
+
push a "width"
|
|
884
|
+
push a "height"
|
|
885
|
+
push a "min-width"
|
|
886
|
+
push a "min-height"
|
|
887
|
+
push a "max-width"
|
|
888
|
+
push a "max-height"
|
|
889
|
+
push a "left"
|
|
890
|
+
push a "top"
|
|
891
|
+
push a "right"
|
|
892
|
+
push a "bottom"
|
|
893
|
+
push a "gap"
|
|
894
|
+
push a "font-size"
|
|
895
|
+
push a "flex-basis"
|
|
896
|
+
push a "color"
|
|
897
|
+
push a "background-color"
|
|
898
|
+
push a "border-color"
|
|
899
|
+
push a "evg-surface-effect"
|
|
900
|
+
push a "evg-effect-on"
|
|
901
|
+
push a "opacity"
|
|
902
|
+
push a "line-height"
|
|
903
|
+
push a "flex-grow"
|
|
904
|
+
push a "flex-shrink"
|
|
905
|
+
push a "rotate"
|
|
906
|
+
push a "scale"
|
|
907
|
+
push a "margin-top"
|
|
908
|
+
push a "margin-right"
|
|
909
|
+
push a "margin-bottom"
|
|
910
|
+
push a "margin-left"
|
|
911
|
+
push a "padding-top"
|
|
912
|
+
push a "padding-right"
|
|
913
|
+
push a "padding-bottom"
|
|
914
|
+
push a "padding-left"
|
|
915
|
+
push a "border-width"
|
|
916
|
+
push a "border-radius"
|
|
917
|
+
return a
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
; An inset written as an anchor reference, in the form `setAttribute`
|
|
921
|
+
; takes back: `anchor(right)`, or `calc(anchor(right) + 8px)`.
|
|
922
|
+
sfn anchorInsetText:string (side:string offset:double) {
|
|
923
|
+
if ((strlen side) == 0) {
|
|
924
|
+
return ""
|
|
925
|
+
}
|
|
926
|
+
def inner:string (("anchor(" + side) + ")")
|
|
927
|
+
if (offset == 0.0) {
|
|
928
|
+
return inner
|
|
929
|
+
}
|
|
930
|
+
def sign:string " + "
|
|
931
|
+
def mag:double offset
|
|
932
|
+
if (offset < 0.0) {
|
|
933
|
+
sign = " - "
|
|
934
|
+
mag = (0.0 - offset)
|
|
935
|
+
}
|
|
936
|
+
def out:string (("calc(" + inner) + sign)
|
|
937
|
+
out = ((out + (EVGDisplayList.num(mag))) + "px)")
|
|
938
|
+
return out
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
sfn readable:boolean (name:string) {
|
|
942
|
+
return ((EVGPatch.kindOf(name)) != 0)
|
|
943
|
+
} doc {
|
|
944
|
+
description "Whether a property can be patched — which is to say, read back and undone."
|
|
945
|
+
param name "A property name, kebab or camel."
|
|
946
|
+
returns "True when an op naming it will be considered."
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
; What the element says about this property NOW, in the form `setAttribute`
|
|
950
|
+
; would take back. "" means it was never set, which `clearProp` restores.
|
|
951
|
+
sfn readProp:string (el:EVGElement name:string) {
|
|
952
|
+
def n:string (EVGPatch.canonical(name))
|
|
953
|
+
def kind:int (EVGPatch.kindOf(n))
|
|
954
|
+
if (kind == 0) {
|
|
955
|
+
return ""
|
|
956
|
+
}
|
|
957
|
+
def bx:EVGBox el.box
|
|
958
|
+
|
|
959
|
+
if (n == "display") { return el.display }
|
|
960
|
+
if (n == "position") { return el.position }
|
|
961
|
+
if (n == "flex-direction") { return el.flexDirection }
|
|
962
|
+
if (n == "justify-content") { return el.justifyContent }
|
|
963
|
+
if (n == "align-items") { return el.alignItems }
|
|
964
|
+
if (n == "align-content") { return el.alignContent }
|
|
965
|
+
if (n == "flex-wrap") { return el.flexWrap }
|
|
966
|
+
if (n == "grid-area") { return el.gridArea }
|
|
967
|
+
if (n == "grid-row") { return el.gridRow }
|
|
968
|
+
if (n == "grid-column") { return el.gridColumn }
|
|
969
|
+
if (n == "grid-template-areas") { return el.gridTemplateAreas }
|
|
970
|
+
if (n == "grid-auto-flow") { return el.gridAutoFlow }
|
|
971
|
+
if (n == "grid-template-columns") { return el.gridTemplateColumns }
|
|
972
|
+
if (n == "grid-template-rows") { return el.gridTemplateRows }
|
|
973
|
+
if (n == "font-family") { return el.fontFamily }
|
|
974
|
+
if (n == "font-weight") { return el.fontWeight }
|
|
975
|
+
if (n == "text-align") { return el.textAlign }
|
|
976
|
+
if (n == "overflow") { return el.overflow }
|
|
977
|
+
if (n == "cursor") { return el.cursor }
|
|
978
|
+
if (n == "class-name") { return el.className }
|
|
979
|
+
if (n == "theme") { return el.theme }
|
|
980
|
+
if (n == "src") { return el.src }
|
|
981
|
+
if (n == "object-fit") { return el.objectFit }
|
|
982
|
+
if (n == "transform") { return el.transformSpec }
|
|
983
|
+
if (n == "transform-origin") { return el.transformOriginSpec }
|
|
984
|
+
if (n == "white-space") { return el.whiteSpace }
|
|
985
|
+
if (n == "transition") { return el.transitionSpec }
|
|
986
|
+
if (n == "background-gradient") { return el.backgroundGradient }
|
|
987
|
+
if (n == "svg") { return el.svgSource }
|
|
988
|
+
; A CONNECTOR'S `d` AND `view-box` ARE THE LAYOUT'S, not the
|
|
989
|
+
; document's. Writing them back would freeze the geometry the tag
|
|
990
|
+
; exists to keep fluid — the file would carry the arrow measured at
|
|
991
|
+
; one width, which is the hand-placed arrow all over again.
|
|
992
|
+
if (n == "d") {
|
|
993
|
+
if (el.tagName == "connector") {
|
|
994
|
+
return ""
|
|
995
|
+
}
|
|
996
|
+
return el.svgPath
|
|
997
|
+
}
|
|
998
|
+
if (n == "view-box") {
|
|
999
|
+
if (el.tagName == "connector") {
|
|
1000
|
+
return ""
|
|
1001
|
+
}
|
|
1002
|
+
return el.viewBox
|
|
1003
|
+
}
|
|
1004
|
+
if (n == "overlay") {
|
|
1005
|
+
if el.isOverlay {
|
|
1006
|
+
return "true"
|
|
1007
|
+
}
|
|
1008
|
+
return ""
|
|
1009
|
+
}
|
|
1010
|
+
if (n == "overlay-anchor-role") {
|
|
1011
|
+
if el.isOverlayAnchor {
|
|
1012
|
+
return "true"
|
|
1013
|
+
}
|
|
1014
|
+
return ""
|
|
1015
|
+
}
|
|
1016
|
+
if (n == "fit-viewport") {
|
|
1017
|
+
if el.fitViewport {
|
|
1018
|
+
return "true"
|
|
1019
|
+
}
|
|
1020
|
+
return ""
|
|
1021
|
+
}
|
|
1022
|
+
if (n == "overlay-side") { return el.overlaySide }
|
|
1023
|
+
if (n == "overlay-align") { return el.overlayAlign }
|
|
1024
|
+
if (n == "overlay-gap") { return (EVGDisplayList.num(el.overlayGap)) }
|
|
1025
|
+
if (n == "position-anchor") { return el.positionAnchor }
|
|
1026
|
+
if (n == "position-try-fallbacks") { return el.positionTryFallbacks }
|
|
1027
|
+
if (n == "position-try-order") { return el.positionTryOrder }
|
|
1028
|
+
if (n == "presentation") { return el.presentation }
|
|
1029
|
+
if (n == "sheet-below") { return (EVGDisplayList.num(el.sheetBelow)) }
|
|
1030
|
+
if (n == "anchor-name") { return el.anchorName }
|
|
1031
|
+
if (n == "from") { return el.connectorFrom }
|
|
1032
|
+
if (n == "to") { return el.connectorTo }
|
|
1033
|
+
if (n == "from-side") { return el.connectorFromSide }
|
|
1034
|
+
if (n == "to-side") { return el.connectorToSide }
|
|
1035
|
+
if (n == "routing") { return el.connectorRouting }
|
|
1036
|
+
if (n == "arrow-start") { return el.arrowStart }
|
|
1037
|
+
if (n == "arrow-end") { return el.arrowEnd }
|
|
1038
|
+
if (n == "arrow-size") { return (EVGDisplayList.num(el.arrowSize)) }
|
|
1039
|
+
if (n == "from-offset") { return (EVGDisplayList.num(el.connectorFromOffset)) }
|
|
1040
|
+
if (n == "to-offset") { return (EVGDisplayList.num(el.connectorToOffset)) }
|
|
1041
|
+
if (n == "fill-rule") { return el.fillRule }
|
|
1042
|
+
if (n == "stroke-linecap") { return el.strokeLineCap }
|
|
1043
|
+
if (n == "stroke-linejoin") { return el.strokeLineJoin }
|
|
1044
|
+
if (n == "stroke-dasharray") { return el.strokeDashArray }
|
|
1045
|
+
if (n == "alt") { return el.alt }
|
|
1046
|
+
if (n == "href") { return el.href }
|
|
1047
|
+
|
|
1048
|
+
if (n == "width") { return (EVGPatch.unitText(el.width)) }
|
|
1049
|
+
if (n == "height") { return (EVGPatch.unitText(el.height)) }
|
|
1050
|
+
if (n == "min-width") { return (EVGPatch.unitText(el.minWidth)) }
|
|
1051
|
+
if (n == "min-height") { return (EVGPatch.unitText(el.minHeight)) }
|
|
1052
|
+
if (n == "max-width") { return (EVGPatch.unitText(el.maxWidth)) }
|
|
1053
|
+
if (n == "max-height") { return (EVGPatch.unitText(el.maxHeight)) }
|
|
1054
|
+
; AN INSET MAY BE AN `anchor()` RATHER THAN A LENGTH, and then the
|
|
1055
|
+
; length is empty — which would have written the document back with
|
|
1056
|
+
; the badge unpositioned and nothing to say it had been dropped.
|
|
1057
|
+
if (n == "left") {
|
|
1058
|
+
def la:string (EVGPatch.anchorInsetText(el.anchorLeftSide el.anchorLeftOffset))
|
|
1059
|
+
if ((strlen la) > 0) {
|
|
1060
|
+
return la
|
|
1061
|
+
}
|
|
1062
|
+
return (EVGPatch.unitText(el.left))
|
|
1063
|
+
}
|
|
1064
|
+
if (n == "top") {
|
|
1065
|
+
def ta:string (EVGPatch.anchorInsetText(el.anchorTopSide el.anchorTopOffset))
|
|
1066
|
+
if ((strlen ta) > 0) {
|
|
1067
|
+
return ta
|
|
1068
|
+
}
|
|
1069
|
+
return (EVGPatch.unitText(el.top))
|
|
1070
|
+
}
|
|
1071
|
+
if (n == "right") {
|
|
1072
|
+
def ra:string (EVGPatch.anchorInsetText(el.anchorRightSide el.anchorRightOffset))
|
|
1073
|
+
if ((strlen ra) > 0) {
|
|
1074
|
+
return ra
|
|
1075
|
+
}
|
|
1076
|
+
return (EVGPatch.unitText(el.right))
|
|
1077
|
+
}
|
|
1078
|
+
if (n == "bottom") {
|
|
1079
|
+
def ba:string (EVGPatch.anchorInsetText(el.anchorBottomSide el.anchorBottomOffset))
|
|
1080
|
+
if ((strlen ba) > 0) {
|
|
1081
|
+
return ba
|
|
1082
|
+
}
|
|
1083
|
+
return (EVGPatch.unitText(el.bottom))
|
|
1084
|
+
}
|
|
1085
|
+
if (n == "gap") { return (EVGPatch.unitText(el.gap)) }
|
|
1086
|
+
if (n == "font-size") { return (EVGPatch.unitText(el.fontSize)) }
|
|
1087
|
+
if (n == "flex-basis") { return (EVGPatch.unitText(el.flexBasis)) }
|
|
1088
|
+
|
|
1089
|
+
if (n == "color") { return (EVGPatch.colorText(el.color)) }
|
|
1090
|
+
if (n == "background-color") { return (EVGPatch.colorText(el.backgroundColor)) }
|
|
1091
|
+
if (n == "border-color") { return (EVGPatch.colorText(bx.borderColor)) }
|
|
1092
|
+
if (n == "fill") { return (EVGPatch.colorText(el.fillColor)) }
|
|
1093
|
+
if (n == "stroke") { return (EVGPatch.colorText(el.strokeColor)) }
|
|
1094
|
+
if (n == "emoji-color") { return (EVGPatch.colorText(el.emojiColor)) }
|
|
1095
|
+
if (n == "gradient-from") { return (EVGPatch.gradientText(el.gradientFrom el.gradientSet)) }
|
|
1096
|
+
if (n == "gradient-to") { return (EVGPatch.gradientText(el.gradientTo el.gradientSet)) }
|
|
1097
|
+
|
|
1098
|
+
if (n == "evg-surface-effect") { return el.surfaceEffect }
|
|
1099
|
+
if (n == "evg-effect-on") { return el.effectTrigger }
|
|
1100
|
+
if (EVGPatch.isFx(n)) {
|
|
1101
|
+
def key:string (substring n 7 (strlen n))
|
|
1102
|
+
def i:int 0
|
|
1103
|
+
while (i < (array_length el.fxNames)) {
|
|
1104
|
+
if ((itemAt el.fxNames i) == key) {
|
|
1105
|
+
return (EVGDisplayList.num((itemAt el.fxValues i)))
|
|
1106
|
+
}
|
|
1107
|
+
i = (i + 1)
|
|
1108
|
+
}
|
|
1109
|
+
return ""
|
|
1110
|
+
}
|
|
1111
|
+
if (n == "opacity") { return (EVGDisplayList.num(el.opacity)) }
|
|
1112
|
+
if (n == "line-height") { return (EVGDisplayList.num(el.lineHeight)) }
|
|
1113
|
+
if (n == "flex-grow") { return (EVGDisplayList.num(el.flex)) }
|
|
1114
|
+
if (n == "flex-shrink") { return (EVGDisplayList.num(el.flexShrink)) }
|
|
1115
|
+
if (n == "rotate") { return (EVGDisplayList.num(el.rotate)) }
|
|
1116
|
+
if (n == "scale") { return (EVGDisplayList.num(el.scale)) }
|
|
1117
|
+
if (n == "gradient-dir") { return (EVGDisplayList.num((to_double el.gradientDir))) }
|
|
1118
|
+
if (n == "stroke-width") { return (EVGDisplayList.num(el.strokeWidth)) }
|
|
1119
|
+
if (n == "stroke-dashoffset") { return (EVGDisplayList.num(el.strokeDashOffset)) }
|
|
1120
|
+
|
|
1121
|
+
if (n == "margin-top") { return (EVGPatch.unitText(bx.marginTop)) }
|
|
1122
|
+
if (n == "margin-right") { return (EVGPatch.unitText(bx.marginRight)) }
|
|
1123
|
+
if (n == "margin-bottom") { return (EVGPatch.unitText(bx.marginBottom)) }
|
|
1124
|
+
if (n == "margin-left") { return (EVGPatch.unitText(bx.marginLeft)) }
|
|
1125
|
+
if (n == "padding-top") { return (EVGPatch.unitText(bx.paddingTop)) }
|
|
1126
|
+
if (n == "padding-right") { return (EVGPatch.unitText(bx.paddingRight)) }
|
|
1127
|
+
if (n == "padding-bottom") { return (EVGPatch.unitText(bx.paddingBottom)) }
|
|
1128
|
+
if (n == "padding-left") { return (EVGPatch.unitText(bx.paddingLeft)) }
|
|
1129
|
+
if (n == "border-width") { return (EVGPatch.unitText(bx.borderWidth)) }
|
|
1130
|
+
if (n == "border-radius") { return (EVGPatch.unitText(bx.borderRadius)) }
|
|
1131
|
+
|
|
1132
|
+
return ""
|
|
1133
|
+
} doc {
|
|
1134
|
+
description "Reads a property back in the form setAttribute would accept."
|
|
1135
|
+
param el "The element to read."
|
|
1136
|
+
param name "A patchable property name."
|
|
1137
|
+
returns "The current value, or \"\" when the property was never set."
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
; Put a property back to the value a fresh element has. This is what an
|
|
1141
|
+
; inverse op does when the property had never been set — writing "" through
|
|
1142
|
+
; `setAttribute` would be a parse failure, not an erasure.
|
|
1143
|
+
sfn clearProp:boolean (el:EVGElement name:string) {
|
|
1144
|
+
def n:string (EVGPatch.canonical(name))
|
|
1145
|
+
def kind:int (EVGPatch.kindOf(n))
|
|
1146
|
+
if (kind == 0) {
|
|
1147
|
+
return false
|
|
1148
|
+
}
|
|
1149
|
+
def bx:EVGBox el.box
|
|
1150
|
+
|
|
1151
|
+
if (n == "display") {
|
|
1152
|
+
el.display = "block"
|
|
1153
|
+
return true
|
|
1154
|
+
}
|
|
1155
|
+
if (n == "position") {
|
|
1156
|
+
el.position = "relative"
|
|
1157
|
+
return true
|
|
1158
|
+
}
|
|
1159
|
+
if (n == "flex-direction") {
|
|
1160
|
+
el.flexDirection = "column"
|
|
1161
|
+
return true
|
|
1162
|
+
}
|
|
1163
|
+
if (n == "justify-content") {
|
|
1164
|
+
el.justifyContent = "flex-start"
|
|
1165
|
+
return true
|
|
1166
|
+
}
|
|
1167
|
+
if (n == "align-items") {
|
|
1168
|
+
el.alignItems = "flex-start"
|
|
1169
|
+
return true
|
|
1170
|
+
}
|
|
1171
|
+
if (n == "align-content") {
|
|
1172
|
+
el.alignContent = "flex-start"
|
|
1173
|
+
return true
|
|
1174
|
+
}
|
|
1175
|
+
if (n == "flex-wrap") {
|
|
1176
|
+
el.flexWrap = "wrap"
|
|
1177
|
+
return true
|
|
1178
|
+
}
|
|
1179
|
+
if (n == "grid-area") {
|
|
1180
|
+
el.gridArea = ""
|
|
1181
|
+
return true
|
|
1182
|
+
}
|
|
1183
|
+
if (n == "grid-row") {
|
|
1184
|
+
el.gridRow = ""
|
|
1185
|
+
return true
|
|
1186
|
+
}
|
|
1187
|
+
if (n == "grid-column") {
|
|
1188
|
+
el.gridColumn = ""
|
|
1189
|
+
return true
|
|
1190
|
+
}
|
|
1191
|
+
if (n == "grid-template-areas") {
|
|
1192
|
+
el.gridTemplateAreas = ""
|
|
1193
|
+
return true
|
|
1194
|
+
}
|
|
1195
|
+
if (n == "grid-auto-flow") {
|
|
1196
|
+
el.gridAutoFlow = "row"
|
|
1197
|
+
return true
|
|
1198
|
+
}
|
|
1199
|
+
if (n == "grid-template-columns") {
|
|
1200
|
+
el.gridTemplateColumns = ""
|
|
1201
|
+
return true
|
|
1202
|
+
}
|
|
1203
|
+
if (n == "grid-template-rows") {
|
|
1204
|
+
el.gridTemplateRows = ""
|
|
1205
|
+
return true
|
|
1206
|
+
}
|
|
1207
|
+
if (n == "font-family") {
|
|
1208
|
+
el.fontFamily = "Noto Sans"
|
|
1209
|
+
return true
|
|
1210
|
+
}
|
|
1211
|
+
if (n == "font-weight") {
|
|
1212
|
+
el.fontWeight = "normal"
|
|
1213
|
+
return true
|
|
1214
|
+
}
|
|
1215
|
+
if (n == "text-align") {
|
|
1216
|
+
el.textAlign = "left"
|
|
1217
|
+
return true
|
|
1218
|
+
}
|
|
1219
|
+
if (n == "overflow") {
|
|
1220
|
+
el.overflow = "visible"
|
|
1221
|
+
return true
|
|
1222
|
+
}
|
|
1223
|
+
if (n == "cursor") {
|
|
1224
|
+
el.cursor = ""
|
|
1225
|
+
return true
|
|
1226
|
+
}
|
|
1227
|
+
if (n == "class-name") {
|
|
1228
|
+
el.className = ""
|
|
1229
|
+
return true
|
|
1230
|
+
}
|
|
1231
|
+
if (n == "theme") {
|
|
1232
|
+
el.theme = ""
|
|
1233
|
+
return true
|
|
1234
|
+
}
|
|
1235
|
+
if (n == "src") {
|
|
1236
|
+
el.src = ""
|
|
1237
|
+
return true
|
|
1238
|
+
}
|
|
1239
|
+
if (n == "object-fit") {
|
|
1240
|
+
el.objectFit = "cover"
|
|
1241
|
+
return true
|
|
1242
|
+
}
|
|
1243
|
+
if (n == "transform") {
|
|
1244
|
+
el.transformSpec = ""
|
|
1245
|
+
return true
|
|
1246
|
+
}
|
|
1247
|
+
if (n == "transform-origin") {
|
|
1248
|
+
el.transformOriginSpec = ""
|
|
1249
|
+
el.transformOriginX = (EVGUnit.unset())
|
|
1250
|
+
el.transformOriginY = (EVGUnit.unset())
|
|
1251
|
+
return true
|
|
1252
|
+
}
|
|
1253
|
+
if (n == "white-space") {
|
|
1254
|
+
el.whiteSpace = "normal"
|
|
1255
|
+
return true
|
|
1256
|
+
}
|
|
1257
|
+
if (n == "transition") {
|
|
1258
|
+
el.transitionSpec = ""
|
|
1259
|
+
return true
|
|
1260
|
+
}
|
|
1261
|
+
if (n == "background-gradient") {
|
|
1262
|
+
el.backgroundGradient = ""
|
|
1263
|
+
return true
|
|
1264
|
+
}
|
|
1265
|
+
if (n == "svg") {
|
|
1266
|
+
el.svgSource = ""
|
|
1267
|
+
return true
|
|
1268
|
+
}
|
|
1269
|
+
if (n == "d") {
|
|
1270
|
+
el.svgPath = ""
|
|
1271
|
+
return true
|
|
1272
|
+
}
|
|
1273
|
+
if (n == "view-box") {
|
|
1274
|
+
el.viewBox = ""
|
|
1275
|
+
return true
|
|
1276
|
+
}
|
|
1277
|
+
if (n == "fill-rule") {
|
|
1278
|
+
el.fillRule = "nonzero"
|
|
1279
|
+
return true
|
|
1280
|
+
}
|
|
1281
|
+
if (n == "stroke-linecap") {
|
|
1282
|
+
el.strokeLineCap = "butt"
|
|
1283
|
+
return true
|
|
1284
|
+
}
|
|
1285
|
+
if (n == "stroke-linejoin") {
|
|
1286
|
+
el.strokeLineJoin = "miter"
|
|
1287
|
+
return true
|
|
1288
|
+
}
|
|
1289
|
+
if (n == "stroke-dasharray") {
|
|
1290
|
+
el.strokeDashArray = ""
|
|
1291
|
+
return true
|
|
1292
|
+
}
|
|
1293
|
+
if (n == "overlay") {
|
|
1294
|
+
el.isOverlay = false
|
|
1295
|
+
return true
|
|
1296
|
+
}
|
|
1297
|
+
if (n == "overlay-anchor-role") {
|
|
1298
|
+
el.isOverlayAnchor = false
|
|
1299
|
+
return true
|
|
1300
|
+
}
|
|
1301
|
+
if (n == "overlay-side") {
|
|
1302
|
+
el.overlaySide = "bottom"
|
|
1303
|
+
return true
|
|
1304
|
+
}
|
|
1305
|
+
if (n == "overlay-align") {
|
|
1306
|
+
el.overlayAlign = "start"
|
|
1307
|
+
return true
|
|
1308
|
+
}
|
|
1309
|
+
if (n == "overlay-gap") {
|
|
1310
|
+
el.overlayGap = 4.0
|
|
1311
|
+
return true
|
|
1312
|
+
}
|
|
1313
|
+
if (n == "position-anchor") {
|
|
1314
|
+
el.positionAnchor = ""
|
|
1315
|
+
return true
|
|
1316
|
+
}
|
|
1317
|
+
if (n == "position-try-fallbacks") {
|
|
1318
|
+
el.positionTryFallbacks = ""
|
|
1319
|
+
return true
|
|
1320
|
+
}
|
|
1321
|
+
if (n == "position-try-order") {
|
|
1322
|
+
el.positionTryOrder = ""
|
|
1323
|
+
return true
|
|
1324
|
+
}
|
|
1325
|
+
if (n == "presentation") {
|
|
1326
|
+
el.presentation = ""
|
|
1327
|
+
return true
|
|
1328
|
+
}
|
|
1329
|
+
if (n == "sheet-below") {
|
|
1330
|
+
el.sheetBelow = 0.0
|
|
1331
|
+
return true
|
|
1332
|
+
}
|
|
1333
|
+
if (n == "fit-viewport") {
|
|
1334
|
+
el.fitViewport = false
|
|
1335
|
+
return true
|
|
1336
|
+
}
|
|
1337
|
+
if (n == "anchor-name") {
|
|
1338
|
+
el.anchorName = ""
|
|
1339
|
+
return true
|
|
1340
|
+
}
|
|
1341
|
+
if (n == "from") {
|
|
1342
|
+
el.connectorFrom = ""
|
|
1343
|
+
return true
|
|
1344
|
+
}
|
|
1345
|
+
if (n == "to") {
|
|
1346
|
+
el.connectorTo = ""
|
|
1347
|
+
return true
|
|
1348
|
+
}
|
|
1349
|
+
if (n == "from-side") {
|
|
1350
|
+
el.connectorFromSide = "auto"
|
|
1351
|
+
return true
|
|
1352
|
+
}
|
|
1353
|
+
if (n == "to-side") {
|
|
1354
|
+
el.connectorToSide = "auto"
|
|
1355
|
+
return true
|
|
1356
|
+
}
|
|
1357
|
+
if (n == "routing") {
|
|
1358
|
+
el.connectorRouting = "straight"
|
|
1359
|
+
return true
|
|
1360
|
+
}
|
|
1361
|
+
if (n == "arrow-start") {
|
|
1362
|
+
el.arrowStart = "none"
|
|
1363
|
+
return true
|
|
1364
|
+
}
|
|
1365
|
+
if (n == "arrow-end") {
|
|
1366
|
+
el.arrowEnd = "none"
|
|
1367
|
+
return true
|
|
1368
|
+
}
|
|
1369
|
+
if (n == "arrow-size") {
|
|
1370
|
+
el.arrowSize = 10.0
|
|
1371
|
+
return true
|
|
1372
|
+
}
|
|
1373
|
+
if (n == "from-offset") {
|
|
1374
|
+
el.connectorFromOffset = 0.0
|
|
1375
|
+
return true
|
|
1376
|
+
}
|
|
1377
|
+
if (n == "to-offset") {
|
|
1378
|
+
el.connectorToOffset = 0.0
|
|
1379
|
+
return true
|
|
1380
|
+
}
|
|
1381
|
+
if (n == "alt") {
|
|
1382
|
+
el.alt = ""
|
|
1383
|
+
return true
|
|
1384
|
+
}
|
|
1385
|
+
if (n == "href") {
|
|
1386
|
+
el.href = ""
|
|
1387
|
+
return true
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
def u:EVGUnit (EVGUnit.unset())
|
|
1391
|
+
if (n == "width") {
|
|
1392
|
+
el.width = u
|
|
1393
|
+
return true
|
|
1394
|
+
}
|
|
1395
|
+
if (n == "height") {
|
|
1396
|
+
el.height = u
|
|
1397
|
+
return true
|
|
1398
|
+
}
|
|
1399
|
+
if (n == "min-width") {
|
|
1400
|
+
el.minWidth = u
|
|
1401
|
+
return true
|
|
1402
|
+
}
|
|
1403
|
+
if (n == "min-height") {
|
|
1404
|
+
el.minHeight = u
|
|
1405
|
+
return true
|
|
1406
|
+
}
|
|
1407
|
+
if (n == "max-width") {
|
|
1408
|
+
el.maxWidth = u
|
|
1409
|
+
return true
|
|
1410
|
+
}
|
|
1411
|
+
if (n == "max-height") {
|
|
1412
|
+
el.maxHeight = u
|
|
1413
|
+
return true
|
|
1414
|
+
}
|
|
1415
|
+
if (n == "left") {
|
|
1416
|
+
el.left = u
|
|
1417
|
+
el.anchorLeftSide = ""
|
|
1418
|
+
el.anchorLeftOffset = 0.0
|
|
1419
|
+
return true
|
|
1420
|
+
}
|
|
1421
|
+
if (n == "top") {
|
|
1422
|
+
el.top = u
|
|
1423
|
+
el.anchorTopSide = ""
|
|
1424
|
+
el.anchorTopOffset = 0.0
|
|
1425
|
+
return true
|
|
1426
|
+
}
|
|
1427
|
+
if (n == "right") {
|
|
1428
|
+
el.right = u
|
|
1429
|
+
el.anchorRightSide = ""
|
|
1430
|
+
el.anchorRightOffset = 0.0
|
|
1431
|
+
return true
|
|
1432
|
+
}
|
|
1433
|
+
if (n == "bottom") {
|
|
1434
|
+
el.bottom = u
|
|
1435
|
+
el.anchorBottomSide = ""
|
|
1436
|
+
el.anchorBottomOffset = 0.0
|
|
1437
|
+
return true
|
|
1438
|
+
}
|
|
1439
|
+
if (n == "gap") {
|
|
1440
|
+
el.gap = u
|
|
1441
|
+
return true
|
|
1442
|
+
}
|
|
1443
|
+
if (n == "font-size") {
|
|
1444
|
+
el.fontSize = u
|
|
1445
|
+
return true
|
|
1446
|
+
}
|
|
1447
|
+
if (n == "flex-basis") {
|
|
1448
|
+
el.flexBasis = u
|
|
1449
|
+
return true
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
; `color` is not like the other two. A fresh element's background and
|
|
1453
|
+
; border are UNSET — nothing is painted until something asks — but its
|
|
1454
|
+
; text colour is a real black, because text has to be drawn in
|
|
1455
|
+
; something. Clearing it to `noColor` made a reverted element read ""
|
|
1456
|
+
; where a fresh one reads rgb(0,0,0), which is the shape of bug this
|
|
1457
|
+
; file's last test exists to catch.
|
|
1458
|
+
def c:EVGColor (EVGColor.noColor())
|
|
1459
|
+
if (n == "color") {
|
|
1460
|
+
el.color = (new EVGColor())
|
|
1461
|
+
return true
|
|
1462
|
+
}
|
|
1463
|
+
if (n == "background-color") {
|
|
1464
|
+
el.backgroundColor = c
|
|
1465
|
+
return true
|
|
1466
|
+
}
|
|
1467
|
+
if (n == "border-color") {
|
|
1468
|
+
bx.borderColor = c
|
|
1469
|
+
return true
|
|
1470
|
+
}
|
|
1471
|
+
if (n == "fill") {
|
|
1472
|
+
el.fillColor = c
|
|
1473
|
+
return true
|
|
1474
|
+
}
|
|
1475
|
+
if (n == "stroke") {
|
|
1476
|
+
el.strokeColor = c
|
|
1477
|
+
return true
|
|
1478
|
+
}
|
|
1479
|
+
if (n == "emoji-color") {
|
|
1480
|
+
el.emojiColor = c
|
|
1481
|
+
return true
|
|
1482
|
+
}
|
|
1483
|
+
; Clearing either end clears the gradient: half a gradient is not a
|
|
1484
|
+
; state the renderer has, and leaving `gradientSet` on with one end
|
|
1485
|
+
; unset paints from black.
|
|
1486
|
+
if (n == "gradient-from") {
|
|
1487
|
+
el.gradientFrom = c
|
|
1488
|
+
el.gradientSet = false
|
|
1489
|
+
return true
|
|
1490
|
+
}
|
|
1491
|
+
if (n == "gradient-to") {
|
|
1492
|
+
el.gradientTo = c
|
|
1493
|
+
el.gradientSet = false
|
|
1494
|
+
return true
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
if (n == "evg-surface-effect") {
|
|
1498
|
+
el.surfaceEffect = ""
|
|
1499
|
+
return true
|
|
1500
|
+
}
|
|
1501
|
+
if (n == "evg-effect-on") {
|
|
1502
|
+
el.effectTrigger = ""
|
|
1503
|
+
return true
|
|
1504
|
+
}
|
|
1505
|
+
; Clearing an fx parameter takes it OUT rather than zeroing it: zero is
|
|
1506
|
+
; a value a plugin reads, and `readProp` answering "0" for a property
|
|
1507
|
+
; that was never set would make every undo write one.
|
|
1508
|
+
if (EVGPatch.isFx(n)) {
|
|
1509
|
+
def key:string (substring n 7 (strlen n))
|
|
1510
|
+
def names:[string]
|
|
1511
|
+
def values:[double]
|
|
1512
|
+
def i:int 0
|
|
1513
|
+
while (i < (array_length el.fxNames)) {
|
|
1514
|
+
if ((itemAt el.fxNames i) == key) {
|
|
1515
|
+
} {
|
|
1516
|
+
push names (itemAt el.fxNames i)
|
|
1517
|
+
push values (itemAt el.fxValues i)
|
|
1518
|
+
}
|
|
1519
|
+
i = (i + 1)
|
|
1520
|
+
}
|
|
1521
|
+
el.fxNames = names
|
|
1522
|
+
el.fxValues = values
|
|
1523
|
+
return true
|
|
1524
|
+
}
|
|
1525
|
+
if (n == "opacity") {
|
|
1526
|
+
el.opacity = 1.0
|
|
1527
|
+
return true
|
|
1528
|
+
}
|
|
1529
|
+
if (n == "line-height") {
|
|
1530
|
+
el.lineHeight = 0.0
|
|
1531
|
+
return true
|
|
1532
|
+
}
|
|
1533
|
+
if (n == "flex-grow") {
|
|
1534
|
+
el.flex = 0.0
|
|
1535
|
+
return true
|
|
1536
|
+
}
|
|
1537
|
+
if (n == "flex-shrink") {
|
|
1538
|
+
el.flexShrink = 1.0
|
|
1539
|
+
return true
|
|
1540
|
+
}
|
|
1541
|
+
if (n == "rotate") {
|
|
1542
|
+
el.rotate = 0.0
|
|
1543
|
+
return true
|
|
1544
|
+
}
|
|
1545
|
+
if (n == "scale") {
|
|
1546
|
+
el.scale = 1.0
|
|
1547
|
+
return true
|
|
1548
|
+
}
|
|
1549
|
+
if (n == "stroke-width") {
|
|
1550
|
+
el.strokeWidth = 0.0
|
|
1551
|
+
return true
|
|
1552
|
+
}
|
|
1553
|
+
if (n == "stroke-dashoffset") {
|
|
1554
|
+
el.strokeDashOffset = 0.0
|
|
1555
|
+
return true
|
|
1556
|
+
}
|
|
1557
|
+
if (n == "gradient-dir") {
|
|
1558
|
+
el.gradientDir = 0
|
|
1559
|
+
return true
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
if (n == "margin-top") {
|
|
1563
|
+
bx.marginTop = u
|
|
1564
|
+
return true
|
|
1565
|
+
}
|
|
1566
|
+
if (n == "margin-right") {
|
|
1567
|
+
bx.marginRight = u
|
|
1568
|
+
return true
|
|
1569
|
+
}
|
|
1570
|
+
if (n == "margin-bottom") {
|
|
1571
|
+
bx.marginBottom = u
|
|
1572
|
+
return true
|
|
1573
|
+
}
|
|
1574
|
+
if (n == "margin-left") {
|
|
1575
|
+
bx.marginLeft = u
|
|
1576
|
+
return true
|
|
1577
|
+
}
|
|
1578
|
+
if (n == "padding-top") {
|
|
1579
|
+
bx.paddingTop = u
|
|
1580
|
+
return true
|
|
1581
|
+
}
|
|
1582
|
+
if (n == "padding-right") {
|
|
1583
|
+
bx.paddingRight = u
|
|
1584
|
+
return true
|
|
1585
|
+
}
|
|
1586
|
+
if (n == "padding-bottom") {
|
|
1587
|
+
bx.paddingBottom = u
|
|
1588
|
+
return true
|
|
1589
|
+
}
|
|
1590
|
+
if (n == "padding-left") {
|
|
1591
|
+
bx.paddingLeft = u
|
|
1592
|
+
return true
|
|
1593
|
+
}
|
|
1594
|
+
if (n == "border-width") {
|
|
1595
|
+
bx.borderWidth = u
|
|
1596
|
+
return true
|
|
1597
|
+
}
|
|
1598
|
+
if (n == "border-radius") {
|
|
1599
|
+
bx.borderRadius = u
|
|
1600
|
+
return true
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
return false
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
; The unit fields are optional on the element, so "never said anything
|
|
1607
|
+
; about width" is a real answer here and not a defensive check — the same
|
|
1608
|
+
; reading `EVGInspect.unitProp` takes.
|
|
1609
|
+
sfn unitText:string (u@(optional):EVGUnit) {
|
|
1610
|
+
if (null? u) {
|
|
1611
|
+
return ""
|
|
1612
|
+
}
|
|
1613
|
+
def uu:EVGUnit (unwrap u)
|
|
1614
|
+
if (uu.isSet == false) {
|
|
1615
|
+
return ""
|
|
1616
|
+
}
|
|
1617
|
+
return (uu.toString())
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
; A gradient end reads as nothing until the gradient is on. Without the
|
|
1621
|
+
; guard a fresh element reports two black ends it does not have, and every
|
|
1622
|
+
; document would carry them.
|
|
1623
|
+
sfn gradientText:string (c@(optional):EVGColor on:boolean) {
|
|
1624
|
+
if (on == false) {
|
|
1625
|
+
return ""
|
|
1626
|
+
}
|
|
1627
|
+
return (EVGPatch.colorText(c))
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
sfn colorText:string (c@(optional):EVGColor) {
|
|
1631
|
+
if (null? c) {
|
|
1632
|
+
return ""
|
|
1633
|
+
}
|
|
1634
|
+
def cc:EVGColor (unwrap c)
|
|
1635
|
+
if (cc.isSet == false) {
|
|
1636
|
+
return ""
|
|
1637
|
+
}
|
|
1638
|
+
return (cc.toCSSString())
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
|
|
1642
|
+
; An element for a tag name — and the tag name is KEPT, not normalised.
|
|
1643
|
+
;
|
|
1644
|
+
; The engine has two tag vocabularies and this has to know both. The
|
|
1645
|
+
; factories above spell things the HTML way (`span`, `img`), while
|
|
1646
|
+
; `JSXToEVG.mapTagName` emits the engine's own (`text`, `image`, `page`,
|
|
1647
|
+
; `layer`). A document written by one and read by the other has to come
|
|
1648
|
+
; back as what it was, so what the tag decides here is the ELEMENT TYPE —
|
|
1649
|
+
; container, text, image or path, which is what layout and every renderer
|
|
1650
|
+
; actually switch on — and the name itself is preserved.
|
|
1651
|
+
;
|
|
1652
|
+
; A tag in neither vocabulary is refused rather than defaulted to a
|
|
1653
|
+
; container. An `insert` op naming a tag nobody has heard of is a typo, and
|
|
1654
|
+
; a document carrying one would otherwise read back as a silently different
|
|
1655
|
+
; element. `evg_json_tool` reports it as a loss for that reason.
|
|
1656
|
+
sfn create@(optional):EVGElement (tag:string) {
|
|
1657
|
+
def family:int (EVGPatch.familyOf(tag))
|
|
1658
|
+
if (family < 0) {
|
|
1659
|
+
def none@(optional):EVGElement
|
|
1660
|
+
return none
|
|
1661
|
+
}
|
|
1662
|
+
def el:EVGElement (EVGElement.createDiv())
|
|
1663
|
+
if (family == 1) {
|
|
1664
|
+
el = (EVGElement.createSpan())
|
|
1665
|
+
}
|
|
1666
|
+
if (family == 2) {
|
|
1667
|
+
el = (EVGElement.createImg())
|
|
1668
|
+
}
|
|
1669
|
+
if (family == 3) {
|
|
1670
|
+
el = (EVGElement.createPath())
|
|
1671
|
+
}
|
|
1672
|
+
el.tagName = tag
|
|
1673
|
+
def out@(optional):EVGElement el
|
|
1674
|
+
return out
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
; 0 container, 1 text, 2 image, 3 path — `EVGElement.elementType` — or -1
|
|
1678
|
+
; for a tag neither vocabulary claims.
|
|
1679
|
+
sfn familyOf:int (tag:string) {
|
|
1680
|
+
if (((tag == "span") || (tag == "text")) || (tag == "Label")) {
|
|
1681
|
+
return 1
|
|
1682
|
+
}
|
|
1683
|
+
if (((tag == "img") || (tag == "image")) || (tag == "Image")) {
|
|
1684
|
+
return 2
|
|
1685
|
+
}
|
|
1686
|
+
if (((tag == "path") || (tag == "Path")) || (tag == "svg")) {
|
|
1687
|
+
return 3
|
|
1688
|
+
}
|
|
1689
|
+
; A connector IS a path — one whose `d` the layout writes rather than
|
|
1690
|
+
; the author. Everything downstream of here treats it as one.
|
|
1691
|
+
if (tag == "connector") {
|
|
1692
|
+
return 3
|
|
1693
|
+
}
|
|
1694
|
+
if (tag == "Svg") {
|
|
1695
|
+
return 3
|
|
1696
|
+
}
|
|
1697
|
+
if (((tag == "div") || (tag == "View")) || (tag == "box")) {
|
|
1698
|
+
return 0
|
|
1699
|
+
}
|
|
1700
|
+
if (((tag == "row") || (tag == "column")) || (tag == "page")) {
|
|
1701
|
+
return 0
|
|
1702
|
+
}
|
|
1703
|
+
if (((tag == "Page") || (tag == "print")) || (tag == "Print")) {
|
|
1704
|
+
return 0
|
|
1705
|
+
}
|
|
1706
|
+
if (((tag == "section") || (tag == "Section")) || (tag == "layer")) {
|
|
1707
|
+
return 0
|
|
1708
|
+
}
|
|
1709
|
+
; A popover is a container that happens to be a surface — see
|
|
1710
|
+
; `EVGElement.isSurface`. The tag is the web's name for it and saves
|
|
1711
|
+
; writing `overlay: true`.
|
|
1712
|
+
if ((tag == "popover") || (tag == "Popover")) {
|
|
1713
|
+
return 0
|
|
1714
|
+
}
|
|
1715
|
+
if (((tag == "Layer") || (tag == "divider")) || (tag == "spacer")) {
|
|
1716
|
+
return 0
|
|
1717
|
+
}
|
|
1718
|
+
return (0 - 1)
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
}
|