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.
Files changed (338) hide show
  1. package/CHANGELOG.md +1064 -20
  2. package/LICENSE +3 -1
  3. package/README.md +123 -28
  4. package/dist/Lang.rgr +1188 -290
  5. package/dist/api.d.ts +2343 -879
  6. package/dist/api.js +79923 -56921
  7. package/dist/lib/JSON.rgr +102 -91
  8. package/dist/lib/Shell.rgr +4 -4
  9. package/dist/lib/apple/AppleToolchain.rgr +4 -4
  10. package/dist/lib/apple/README.md +5 -4
  11. package/dist/lib/apple/apple_test.rgr +1 -1
  12. package/dist/lib/core/README.md +1 -1
  13. package/dist/lib/evg/EVG.rgr +12 -0
  14. package/dist/lib/evg/EVGA11yFromTree.rgr +302 -0
  15. package/dist/lib/evg/EVGA11yTree.rgr +894 -0
  16. package/dist/lib/evg/EVGBox.rgr +267 -0
  17. package/dist/lib/evg/EVGBoxShorthandTest.rgr +220 -0
  18. package/dist/lib/evg/EVGCodepoint.rgr +316 -0
  19. package/dist/lib/evg/EVGColor.rgr +700 -0
  20. package/dist/lib/evg/EVGCommands.rgr +177 -0
  21. package/dist/lib/evg/EVGComponent.rgr +331 -0
  22. package/dist/lib/evg/EVGComponentTest.rgr +387 -0
  23. package/dist/lib/evg/EVGConnector.rgr +541 -0
  24. package/dist/lib/evg/EVGConnectorTest.rgr +306 -0
  25. package/dist/lib/evg/EVGDisplayList.rgr +4186 -0
  26. package/dist/lib/evg/EVGEasing.rgr +370 -0
  27. package/dist/lib/evg/EVGEffectTest.rgr +253 -0
  28. package/dist/lib/evg/EVGElement.rgr +4914 -0
  29. package/dist/lib/evg/EVGFixedTest.rgr +289 -0
  30. package/dist/lib/evg/EVGFlexRulesTest.rgr +317 -0
  31. package/dist/lib/evg/EVGFlexWrapTest.rgr +246 -0
  32. package/dist/lib/evg/EVGFling.rgr +244 -0
  33. package/dist/lib/evg/EVGFocus.rgr +400 -0
  34. package/dist/lib/evg/EVGFocusTest.rgr +399 -0
  35. package/dist/lib/evg/EVGGradient.rgr +322 -0
  36. package/dist/lib/evg/EVGGrapheme.rgr +190 -0
  37. package/dist/lib/evg/EVGGrid.rgr +975 -0
  38. package/dist/lib/evg/EVGHitTest.rgr +208 -0
  39. package/dist/lib/evg/EVGHoles.rgr +294 -0
  40. package/dist/lib/evg/EVGHostMeasurerTest.rgr +281 -0
  41. package/dist/lib/evg/EVGHostTextMeasurer.rgr +331 -0
  42. package/dist/lib/evg/EVGHostTree.rgr +834 -0
  43. package/dist/lib/evg/EVGHostTreeTest.rgr +447 -0
  44. package/dist/lib/evg/EVGImageDecode.rgr +117 -0
  45. package/dist/lib/evg/EVGImageMeasurer.rgr +86 -0
  46. package/dist/lib/evg/EVGInspect.rgr +883 -0
  47. package/dist/lib/evg/EVGInvalidateTest.rgr +432 -0
  48. package/dist/lib/evg/EVGJsonTest.rgr +535 -0
  49. package/dist/lib/evg/EVGLayout.rgr +4220 -0
  50. package/dist/lib/evg/EVGMeasure.rgr +1267 -0
  51. package/dist/lib/evg/EVGOverlayTest.rgr +626 -0
  52. package/dist/lib/evg/EVGPatch.rgr +1721 -0
  53. package/dist/lib/evg/EVGPatchTest.rgr +692 -0
  54. package/dist/lib/evg/EVGPopoverTest.rgr +395 -0
  55. package/dist/lib/evg/EVGReconcile.rgr +226 -0
  56. package/dist/lib/evg/EVGReconcileTest.rgr +421 -0
  57. package/dist/lib/evg/EVGReject.rgr +126 -0
  58. package/dist/lib/evg/EVGRelayoutTest.rgr +375 -0
  59. package/dist/lib/evg/EVGRtlLayoutTest.rgr +319 -0
  60. package/dist/lib/evg/EVGRuler.rgr +232 -0
  61. package/dist/lib/evg/EVGRulerTest.rgr +172 -0
  62. package/dist/lib/evg/EVGSelectChrome.rgr +178 -0
  63. package/dist/lib/evg/EVGStyleCacheTest.rgr +419 -0
  64. package/dist/lib/evg/EVGStyleSheet.rgr +2148 -0
  65. package/dist/lib/evg/EVGStyleStateTest.rgr +407 -0
  66. package/dist/lib/evg/EVGStyleVarTest.rgr +471 -0
  67. package/dist/lib/evg/EVGText.rgr +105 -0
  68. package/dist/lib/evg/EVGTextEngine.rgr +536 -0
  69. package/dist/lib/evg/EVGTextMeasurer.rgr +720 -0
  70. package/dist/lib/evg/EVGTimingTest.rgr +1417 -0
  71. package/dist/lib/evg/EVGToolbar.rgr +1369 -0
  72. package/dist/lib/evg/EVGTransition.rgr +728 -0
  73. package/dist/lib/evg/EVGTreeJson.rgr +721 -0
  74. package/dist/lib/evg/EVGUnit.rgr +554 -0
  75. package/dist/lib/evg/EVGViewportUnitTest.rgr +236 -0
  76. package/dist/lib/evg/EvgApp.rgr +189 -0
  77. package/dist/lib/evg/EvgBitmapTracer.rgr +4277 -0
  78. package/dist/lib/evg/EvgBitmapTracerTest.rgr +2119 -0
  79. package/dist/lib/evg/EvgHost.rgr +451 -0
  80. package/dist/lib/evg/EvgTest.rgr +91 -0
  81. package/dist/lib/evg/EvgTraceColor.rgr +236 -0
  82. package/dist/lib/evg/EvgTraceCurve.rgr +648 -0
  83. package/dist/lib/evg/EvgTraceFit.rgr +767 -0
  84. package/dist/lib/evg/EvgTracePath.rgr +435 -0
  85. package/dist/lib/evg/EvgTraceTypes.rgr +559 -0
  86. package/dist/lib/evg/EvgViewport.rgr +313 -0
  87. package/dist/lib/evg/FxDemoDoc.rgr +185 -0
  88. package/dist/lib/evg/HOSTS.md +227 -0
  89. package/dist/lib/evg/ISSUES.md +835 -0
  90. package/dist/lib/evg/PLAN_ACCESSIBILITY.md +650 -0
  91. package/dist/lib/evg/PLAN_CSS_LAYOUT_AND_FONTS.md +1124 -0
  92. package/dist/lib/evg/PLAN_EFFECTS.md +431 -0
  93. package/dist/lib/evg/PLAN_EVG.md +518 -0
  94. package/dist/lib/evg/PLAN_EVG_RENDERER.md +1674 -0
  95. package/dist/lib/evg/PLAN_INSPECTOR.md +788 -0
  96. package/dist/lib/evg/PLAN_LINKS_AND_FORMS.md +157 -0
  97. package/dist/lib/evg/PLAN_NATIVE_HOSTS.md +671 -0
  98. package/dist/lib/evg/PLAN_VECTOR_IR.md +664 -0
  99. package/dist/lib/evg/PLAN_VIEW_TRANSFORM.md +322 -0
  100. package/dist/lib/evg/PathBuilder.rgr +333 -0
  101. package/dist/lib/evg/README.md +1236 -0
  102. package/dist/lib/evg/SPEC.md +1252 -0
  103. package/dist/lib/evg/SVGPathParser.rgr +1199 -0
  104. package/dist/lib/evg/SvgParser.rgr +1736 -0
  105. package/dist/lib/evg/TODO_EVG_RENDERER.md +595 -0
  106. package/dist/lib/evg/VectorShapes.rgr +331 -0
  107. package/dist/lib/evg/VectorStroke.rgr +107 -0
  108. package/dist/lib/evg/VectorViewBox.rgr +380 -0
  109. package/dist/lib/evg/agent/README.md +290 -0
  110. package/dist/lib/evg/agent/evg_agent.rgr +899 -0
  111. package/dist/lib/evg/agent/fixtures/broken.evg.json +7 -0
  112. package/dist/lib/evg/agent/fixtures/card.evg.json +9 -0
  113. package/dist/lib/evg/agent/fixtures/connector.css +63 -0
  114. package/dist/lib/evg/agent/fixtures/connector.evg.json +14 -0
  115. package/dist/lib/evg/agent/fixtures/drawn.evg.json +129 -0
  116. package/dist/lib/evg/agent/fixtures/gradient.evg.json +4 -0
  117. package/dist/lib/evg/agent/fixtures/popover.css +99 -0
  118. package/dist/lib/evg/agent/fixtures/popover.evg.json +30 -0
  119. package/dist/lib/evg/agent/roundtrip.sh +74 -0
  120. package/dist/lib/evg/agent/smoke.sh +230 -0
  121. package/dist/lib/evg/android/README.md +97 -0
  122. package/dist/lib/evg/android/androidstubs/AndroidStubs.kt +175 -0
  123. package/dist/lib/evg/android/androidstubs/Annotation.kt +10 -0
  124. package/dist/lib/evg/android/androidstubs/App.kt +30 -0
  125. package/dist/lib/evg/android/androidstubs/Content.kt +41 -0
  126. package/dist/lib/evg/android/androidstubs/ContentRes.kt +12 -0
  127. package/dist/lib/evg/android/androidstubs/InputMethod.kt +46 -0
  128. package/dist/lib/evg/android/androidstubs/Net.kt +6 -0
  129. package/dist/lib/evg/android/androidstubs/Os.kt +30 -0
  130. package/dist/lib/evg/android/androidstubs/Util.kt +15 -0
  131. package/dist/lib/evg/android/androidstubs/View.kt +113 -0
  132. package/dist/lib/evg/android/androidstubs/Widget.kt +16 -0
  133. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidEvgSurface.kt +323 -0
  134. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidTextMeasurer.kt +56 -0
  135. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/RippleEffect.kt +262 -0
  136. package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtEvgSurface.kt +238 -0
  137. package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtTextMeasurer.kt +85 -0
  138. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgEngineThread.kt +115 -0
  139. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgPainter.kt +231 -0
  140. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgSurface.kt +117 -0
  141. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/RecordingSurface.kt +94 -0
  142. package/dist/lib/evg/apple/README.md +128 -0
  143. package/dist/lib/evg/apple/Sources/CoreGraphicsEvgSurface.swift +329 -0
  144. package/dist/lib/evg/apple/Sources/CoreTextMeasurer.swift +70 -0
  145. package/dist/lib/evg/apple/Sources/EvgEngineQueue.swift +105 -0
  146. package/dist/lib/evg/apple/Sources/EvgPainter.swift +225 -0
  147. package/dist/lib/evg/apple/Sources/EvgSurface.swift +163 -0
  148. package/dist/lib/evg/apple/Sources/RecordingSurface.swift +110 -0
  149. package/dist/lib/evg/bench/EvgLayoutBench.rgr +34 -0
  150. package/dist/lib/evg/bench/README.md +64 -0
  151. package/dist/lib/evg/bench/layout-bench.mjs +316 -0
  152. package/dist/lib/evg/bench/layout-cases.mjs +510 -0
  153. package/dist/lib/evg/bench/layout-conformance.mjs +254 -0
  154. package/dist/lib/evg/bin/.gitignore +17 -0
  155. package/dist/lib/evg/evg_test.rgr +313 -0
  156. package/dist/lib/evg/gl/README.md +198 -0
  157. package/dist/lib/evg/gl/a11y-paint-check.mjs +73 -0
  158. package/dist/lib/evg/gl/blur-check.mjs +399 -0
  159. package/dist/lib/evg/gl/boxmodel.json +1 -0
  160. package/dist/lib/evg/gl/demo.html +46 -0
  161. package/dist/lib/evg/gl/effect-presets.css +285 -0
  162. package/dist/lib/evg/gl/effect-presets.js +63 -0
  163. package/dist/lib/evg/gl/effect-shots.mjs +135 -0
  164. package/dist/lib/evg/gl/evg-a11y.js +566 -0
  165. package/dist/lib/evg/gl/evg-binary.js +160 -0
  166. package/dist/lib/evg/gl/evg-engine.js +296 -0
  167. package/dist/lib/evg/gl/evg-fx.js +237 -0
  168. package/dist/lib/evg/gl/evg-gestures.js +209 -0
  169. package/dist/lib/evg/gl/evg-list.js +167 -0
  170. package/dist/lib/evg/gl/evg-measure.js +186 -0
  171. package/dist/lib/evg/gl/evg-textinput.js +303 -0
  172. package/dist/lib/evg/gl/evg-view.js +144 -0
  173. package/dist/lib/evg/gl/evg-webgl.js +3942 -0
  174. package/dist/lib/evg/gl/fx-check.mjs +998 -0
  175. package/dist/lib/evg/gl/fx-demo.html +166 -0
  176. package/dist/lib/evg/gl/fx-demo.js +2 -0
  177. package/dist/lib/evg/gl/fx-serve.mjs +47 -0
  178. package/dist/lib/evg/gl/gestures-check.mjs +189 -0
  179. package/dist/lib/evg/gl/list-binary-check.mjs +152 -0
  180. package/dist/lib/evg/gl/measure-check.mjs +135 -0
  181. package/dist/lib/evg/gl/rotation-check.mjs +207 -0
  182. package/dist/lib/evg/gl/shift-check.mjs +113 -0
  183. package/dist/lib/evg/gl/stroke-check.mjs +168 -0
  184. package/dist/lib/evg/gl/text-snap-check.mjs +139 -0
  185. package/dist/lib/evg/gl/view-check.mjs +343 -0
  186. package/dist/lib/evg/gl/view-policy-check.mjs +184 -0
  187. package/dist/lib/evg/html/evg-dom.js +318 -0
  188. package/dist/lib/evg/html/evg-html.js +600 -0
  189. package/dist/lib/evg/inspect/README.md +415 -0
  190. package/dist/lib/evg/inspect/browser-smoke.mjs +115 -0
  191. package/dist/lib/evg/inspect/evg-inspect.js +947 -0
  192. package/dist/lib/evg/inspect/shots/css.png +0 -0
  193. package/dist/lib/evg/inspect/shots/dashboard.png +0 -0
  194. package/dist/lib/evg/inspect/shots/pptx-slide.png +0 -0
  195. package/dist/lib/evg/inspect/shots/state.png +0 -0
  196. package/dist/lib/evg/inspect/shots.mjs +226 -0
  197. package/dist/lib/evg/oracle/css-blur.json +576 -0
  198. package/dist/lib/evg/oracle/css-box.json +157 -0
  199. package/dist/lib/evg/oracle/css-timing.json +709 -0
  200. package/dist/lib/evg/oracle/css_blur_oracle.mjs +529 -0
  201. package/dist/lib/evg/oracle/css_box_oracle.mjs +106 -0
  202. package/dist/lib/evg/oracle/css_timing_oracle.mjs +389 -0
  203. package/dist/lib/evg/original/EVGColor.clj +310 -0
  204. package/dist/lib/evg/original/EVGColorContext.rgr +178 -0
  205. package/dist/lib/evg/original/SVGPath.rgr +627 -0
  206. package/dist/lib/evg/original/Vec2.crgr +103 -0
  207. package/dist/lib/evg/ranger.json +9 -0
  208. package/dist/lib/evg/showcase/README.md +246 -0
  209. package/dist/lib/evg/showcase/assets/emblem.svg +30 -0
  210. package/dist/lib/evg/showcase/assets/rosette.svg +22 -0
  211. package/dist/lib/evg/showcase/build.mjs +651 -0
  212. package/dist/lib/evg/showcase/pages/album.tsx +30 -0
  213. package/dist/lib/evg/showcase/pages/boxmodel.tsx +37 -0
  214. package/dist/lib/evg/showcase/pages/cards.tsx +41 -0
  215. package/dist/lib/evg/showcase/pages/chart_api.tsx +170 -0
  216. package/dist/lib/evg/showcase/pages/charts.tsx +219 -0
  217. package/dist/lib/evg/showcase/pages/drawing.tsx +160 -0
  218. package/dist/lib/evg/showcase/pages/emoji.tsx +91 -0
  219. package/dist/lib/evg/showcase/pages/flex.tsx +46 -0
  220. package/dist/lib/evg/showcase/pages/more.tsx +332 -0
  221. package/dist/lib/evg/showcase/pages/plots.tsx +262 -0
  222. package/dist/lib/evg/showcase/pages/svg.tsx +63 -0
  223. package/dist/lib/evg/showcase/pages/tables.tsx +209 -0
  224. package/dist/lib/evg/showcase/pages/typography.tsx +43 -0
  225. package/dist/lib/evg/showcase/pages/units.tsx +35 -0
  226. package/dist/lib/evg/showcase/pages/variants.tsx +233 -0
  227. package/dist/lib/evg/showcase/pages/vector.tsx +75 -0
  228. package/dist/lib/evg/showcase/pages/views.tsx +223 -0
  229. package/dist/lib/evg/showcase/tests/chart_api_smoke.mjs +339 -0
  230. package/dist/lib/evg/showcase/tests/gl_smoke.mjs +125 -0
  231. package/dist/lib/evg/showcase/themes/chart_api-default.css +15 -0
  232. package/dist/lib/evg/showcase/themes/charts-default.css +29 -0
  233. package/dist/lib/evg/showcase/themes/drawing-default.css +35 -0
  234. package/dist/lib/evg/showcase/themes/more-default.css +128 -0
  235. package/dist/lib/evg/showcase/themes/plots-default.css +18 -0
  236. package/dist/lib/evg/showcase/themes/showcase.css +508 -0
  237. package/dist/lib/evg/showcase/themes/tables-default.css +123 -0
  238. package/dist/lib/evg/showcase/themes/variants-default.css +15 -0
  239. package/dist/lib/evg/showcase/themes/views-default.css +15 -0
  240. package/dist/lib/evg/tools/bench_vs_potrace.mjs +290 -0
  241. package/dist/lib/evg/tools/evg_image_tool.rgr +488 -0
  242. package/dist/lib/evg/tools/evg_trace_bench.rgr +114 -0
  243. package/dist/lib/evg/tools/evg_trace_cli.rgr +662 -0
  244. package/dist/lib/evg/tools/evg_trace_cpp_bench.rgr +83 -0
  245. package/dist/lib/evg/tools/run_trace_bench.sh +74 -0
  246. package/dist/lib/evg/tools/run_trace_cli_smoke.sh +128 -0
  247. package/dist/lib/evg/web/responsive/EvgResponsiveCheck.rgr +271 -0
  248. package/dist/lib/evg/web/responsive/EvgResponsiveDemo.rgr +531 -0
  249. package/dist/lib/evg/web/responsive/README.md +97 -0
  250. package/dist/lib/evg/web/responsive/build.mjs +90 -0
  251. package/dist/lib/evg/web/responsive/dom-check.mjs +169 -0
  252. package/dist/lib/evg/web/responsive/index.html +177 -0
  253. package/dist/lib/evg/web/responsive/smoke.mjs +221 -0
  254. package/dist/lib/evg/web/tools/assets-client.mjs +95 -0
  255. package/dist/lib/evg/web/tools/boot-bench.mjs +189 -0
  256. package/dist/lib/evg/web/tools/inline-assets.mjs +119 -0
  257. package/dist/lib/evg/web/tools/minify.mjs +60 -0
  258. package/dist/lib/evg/web/tracer/build.mjs +87 -0
  259. package/dist/lib/evg/web/tracer/index.html +2805 -0
  260. package/dist/lib/evg/web/tracer/sample.png +0 -0
  261. package/dist/lib/evg/web/tracer/smoke.mjs +1162 -0
  262. package/dist/lib/evgr/Cargo.lock +21 -0
  263. package/dist/lib/evgr/Cargo.toml +19 -0
  264. package/dist/lib/evgr/README.md +140 -0
  265. package/dist/lib/evgr/bench/NativeBench.rgr +246 -0
  266. package/dist/lib/evgr/bench/compare.mjs +252 -0
  267. package/dist/lib/evgr/bench/speed.mjs +277 -0
  268. package/dist/lib/evgr/bin/.gitignore +3 -0
  269. package/dist/lib/evgr/src/bin/bench.rs +106 -0
  270. package/dist/lib/evgr/src/bin/smoke.rs +13 -0
  271. package/dist/lib/evgr/src/grid.rs +730 -0
  272. package/dist/lib/evgr/src/lib.rs +1003 -0
  273. package/dist/lib/evgr/src/style.rs +468 -0
  274. package/dist/lib/evgr/src/text.rs +83 -0
  275. package/dist/lib/image/BitReader.rgr +171 -0
  276. package/dist/lib/image/Buffer.rgr +173 -0
  277. package/dist/lib/image/DCT.rgr +283 -0
  278. package/dist/lib/image/Deflate.rgr +339 -0
  279. package/dist/lib/image/HuffmanDecoder.rgr +181 -0
  280. package/dist/lib/image/ImageBuffer.rgr +706 -0
  281. package/dist/lib/image/JPEGDecoder.rgr +808 -0
  282. package/dist/lib/image/PNGDecoder.rgr +544 -0
  283. package/dist/lib/image/PNGEncoder.rgr +388 -0
  284. package/dist/lib/image/PPMImage.rgr +224 -0
  285. package/dist/lib/image/ProgressiveJPEGDecoder.rgr +1154 -0
  286. package/dist/lib/image/README.md +33 -0
  287. package/dist/lib/image/RasterBuffer.rgr +294 -0
  288. package/dist/lib/image/VP8BoolDecoder.rgr +163 -0
  289. package/dist/lib/image/WebPDecoder.rgr +433 -0
  290. package/dist/lib/image/WebPLossless.rgr +1035 -0
  291. package/dist/lib/image/WebPLossy.rgr +1929 -0
  292. package/dist/lib/image/ranger.json +9 -0
  293. package/dist/lib/image/testdata/webp/anim_2f_24x16.webp +0 -0
  294. package/dist/lib/image/testdata/webp/fixtures.txt +33 -0
  295. package/dist/lib/image/testdata/webp/gen_fixtures.py +261 -0
  296. package/dist/lib/image/testdata/webp/ll_1x1.webp +0 -0
  297. package/dist/lib/image/testdata/webp/ll_alpha_29x21.webp +0 -0
  298. package/dist/lib/image/testdata/webp/ll_gradient_64x64.webp +0 -0
  299. package/dist/lib/image/testdata/webp/ll_meta_32x32.webp +0 -0
  300. package/dist/lib/image/testdata/webp/ll_noise_17x9.webp +0 -0
  301. package/dist/lib/image/testdata/webp/ll_pal11_23x11.webp +0 -0
  302. package/dist/lib/image/testdata/webp/ll_pal2_19x7.webp +0 -0
  303. package/dist/lib/image/testdata/webp/ll_pal40_16x16.webp +0 -0
  304. package/dist/lib/image/testdata/webp/ll_pal4_13x5.webp +0 -0
  305. package/dist/lib/image/testdata/webp/ll_photo_40x30.webp +0 -0
  306. package/dist/lib/image/testdata/webp/ll_predictors_64x32.webp +0 -0
  307. package/dist/lib/image/testdata/webp/ly_1x1.webp +0 -0
  308. package/dist/lib/image/testdata/webp/ly_alph_raw_f0_21x13.webp +0 -0
  309. package/dist/lib/image/testdata/webp/ly_alph_raw_f1_21x13.webp +0 -0
  310. package/dist/lib/image/testdata/webp/ly_alph_raw_f2_21x13.webp +0 -0
  311. package/dist/lib/image/testdata/webp/ly_alph_raw_f3_21x13.webp +0 -0
  312. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f0_21x13.webp +0 -0
  313. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f1_21x13.webp +0 -0
  314. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f2_21x13.webp +0 -0
  315. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f3_21x13.webp +0 -0
  316. package/dist/lib/image/testdata/webp/ly_alpha_33x17.webp +0 -0
  317. package/dist/lib/image/testdata/webp/ly_exif_15x11.webp +0 -0
  318. package/dist/lib/image/testdata/webp/ly_i16_96x80.webp +0 -0
  319. package/dist/lib/image/testdata/webp/ly_nofilter_24x20.webp +0 -0
  320. package/dist/lib/image/testdata/webp/ly_odd_17x9.webp +0 -0
  321. package/dist/lib/image/testdata/webp/ly_photo_48x40.webp +0 -0
  322. package/dist/lib/image/testdata/webp/ly_q100_20x18.webp +0 -0
  323. package/dist/lib/image/testdata/webp/ly_sharp_40x36.webp +0 -0
  324. package/dist/lib/image/testdata/webp/ly_simple_filter_33x31.webp +0 -0
  325. package/dist/lib/image/testdata/webp/ly_vpx_parts8_37x45.webp +0 -0
  326. package/dist/lib/image/testdata/webp/ly_vpx_skip_96x80.webp +0 -0
  327. package/dist/lib/image/tests/WebPDecodeTool.rgr +81 -0
  328. package/dist/lib/image/tests/WebPDecoderTest.rgr +1162 -0
  329. package/dist/lib/image/tests/run_webp_tests.sh +39 -0
  330. package/dist/lib/rust/RsJson.rgr +468 -0
  331. package/dist/lib/rust/RsPrelude.rgr +1663 -0
  332. package/dist/lib/shell_test.rgr +7 -7
  333. package/dist/lib/stdlib.rgr +21 -5
  334. package/dist/lib/zip/ranger.json +6 -0
  335. package/dist/rgrc.js +87318 -67610
  336. package/package.json +1010 -1040
  337. package/dist/README.md +0 -117
  338. package/dist/package.json +0 -47
@@ -0,0 +1,975 @@
1
+ ; SPDX-License-Identifier: MIT
2
+
3
+ ; EVGGrid.rgr - Track lists and item placement for EVG's CSS Grid subset
4
+ ;
5
+ ; Targets photo-book pages, not CSS Grid Level 2. What is supported:
6
+ ;
7
+ ; grid-template-columns: 120px 1fr 40% | repeat(3, 1fr) | minmax(40px, 1fr)
8
+ ; grid-template-rows: 1fr 1fr
9
+ ; grid-template-areas: "hero hero" "a b"
10
+ ; grid-auto-flow: row | row dense
11
+ ; grid-template-columns: subgrid | grid-template-rows: subgrid
12
+ ; grid-column: span 2 | 2 | 2 / 4 | 2 / span 3
13
+ ; grid-row: (same forms)
14
+ ;
15
+ ; grid-template-columns: [full-start] 1fr [main] 2fr [main-end]
16
+ ; grid-column: main / main-end (named lines, resolved by EVGLayout)
17
+ ;
18
+ ; A track list this subset cannot honour sets `hadError` with a message, which
19
+ ; EVGLayout collects and the tools print — an unsupported declaration changes
20
+ ; the layout, so it must not be dropped in silence.
21
+ ;
22
+ ; `auto` and `fit-content(limit)` size to the content of the items placed in
23
+ ; the track: EVGLayout measures each item's min- and max-content width and
24
+ ; hands the per-track maxima to resolveWithContent(). `auto` takes the
25
+ ; max-content size; `fit-content(limit)` clamps that to the limit but never
26
+ ; below min-content, so an item that cannot be made narrower still fits.
27
+ ; Leftover space then goes to the `fr` tracks as before.
28
+
29
+ Import "EVGUnit.rgr"
30
+
31
+ ; One column or row track.
32
+ ; kind 0 = fixed pixels, 1 = percentage of the container, 2 = fr,
33
+ ; 3 = intrinsic (`auto`, or `fit-content(limit)` when hasFitLimit)
34
+ class EVGGridTrack {
35
+ def kind:int 0
36
+ def value:double 0.0
37
+ ; fit-content(limit): the ceiling on the max-content side. Without it an
38
+ ; intrinsic track is a plain `auto`.
39
+ def hasFitLimit:boolean false
40
+ def fitLimit:EVGUnit
41
+ ; Resolved extent in pixels, filled in by EVGGridTemplate.resolve.
42
+ def sizePx:double 0.0
43
+ ; minmax(min, max) bounds in pixels. Percentages are resolved against the
44
+ ; container when the template is resolved. `hasMin`/`hasMax` distinguish an
45
+ ; absent bound from a zero one.
46
+ def hasMin:boolean false
47
+ def hasMax:boolean false
48
+ def minUnit:EVGUnit
49
+ def maxUnit:EVGUnit
50
+ ; Set while the redistribution loop has already pinned this track.
51
+ def frozen:boolean false
52
+
53
+ Constructor () {
54
+ kind = 0
55
+ value = 0.0
56
+ sizePx = 0.0
57
+ hasMin = false
58
+ hasMax = false
59
+ frozen = false
60
+ hasFitLimit = false
61
+ minUnit = (EVGUnit.unset())
62
+ maxUnit = (EVGUnit.unset())
63
+ fitLimit = (EVGUnit.unset())
64
+ }
65
+ }
66
+
67
+ class EVGGridTemplate {
68
+ def tracks:[EVGGridTrack]
69
+ ; Named grid lines: "[full-start] 1fr [main] 2fr [main-end]". Parallel
70
+ ; arrays of name and the 1-based line number it labels. One line may carry
71
+ ; several names ("[a b]") and one name may not label two lines — the first
72
+ ; wins, which is what CSS does for a duplicate custom-ident.
73
+ def lineNames:[string]
74
+ def lineNumbers:[int]
75
+ ; Set when the spec named a track type this subset does not implement.
76
+ def hadError:boolean false
77
+ def errorText:string ""
78
+
79
+ Constructor () {
80
+ hadError = false
81
+ errorText = ""
82
+ }
83
+
84
+ ; The 1-based line a name labels, or 0 when the template does not have it.
85
+ fn lineNumberNamed:int (name:string) {
86
+ def i:int 0
87
+ while (i < (array_length lineNames)) {
88
+ if ((itemAt lineNames i) == name) {
89
+ return (itemAt lineNumbers i)
90
+ }
91
+ i = (i + 1)
92
+ }
93
+ return 0
94
+ }
95
+
96
+ ; A "[a b]" token labels the line that comes next — line 1 before the first
97
+ ; track, and one past the last track when it trails the list.
98
+ fn addLineNames:void (tok:string) {
99
+ def inner:string (trim (substring tok 1 ((strlen tok) - 1)))
100
+ def line:int ((array_length tracks) + 1)
101
+ def parts:[string] (EVGGridTemplate.tokenize(inner))
102
+ def i:int 0
103
+ while (i < (array_length parts)) {
104
+ def nm:string (itemAt parts i)
105
+ if ((strlen nm) > 0) {
106
+ if ((this.lineNumberNamed(nm)) == 0) {
107
+ push lineNames nm
108
+ push lineNumbers line
109
+ }
110
+ }
111
+ i = (i + 1)
112
+ }
113
+ }
114
+
115
+ sfn isLineNameToken:boolean (tok:string) {
116
+ def len:int (strlen tok)
117
+ if (len < 2) {
118
+ return false
119
+ }
120
+ if ((charAt tok 0) != 91) {
121
+ return false
122
+ }
123
+ return ((charAt tok (len - 1)) == 93)
124
+ }
125
+
126
+ fn count:int () {
127
+ return (array_length tracks)
128
+ }
129
+
130
+ fn trackAt:EVGGridTrack (i:int) {
131
+ return (itemAt tracks i)
132
+ }
133
+
134
+ ; "1fr 1fr", "repeat(3, 1fr)", "120px 40% 1fr"
135
+ sfn parse:EVGGridTemplate (spec:string) {
136
+ def tpl (new EVGGridTemplate())
137
+ def tokens:[string] (EVGGridTemplate.tokenize(spec))
138
+ def i:int 0
139
+ while (i < (array_length tokens)) {
140
+ def tok:string (itemAt tokens i)
141
+ if (EVGGridTemplate.isLineNameToken(tok)) {
142
+ tpl.addLineNames(tok)
143
+ } {
144
+ if (EVGGridTemplate.isRepeat(tok)) {
145
+ tpl.expandRepeat(tok)
146
+ } {
147
+ if (EVGGridTemplate.isMinmax(tok)) {
148
+ tpl.addMinmax(tok)
149
+ } {
150
+ tpl.addTrack(tok)
151
+ }
152
+ }
153
+ }
154
+ i = (i + 1)
155
+ }
156
+ return tpl
157
+ }
158
+
159
+ ; Split on whitespace, but only at paren depth 0 so "repeat(3, 1fr)"
160
+ ; survives as a single token despite the space after its comma.
161
+ sfn tokenize:[string] (spec:string) {
162
+ def out:[string]
163
+ def len:int (strlen spec)
164
+ def depth:int 0
165
+ def start:int 0
166
+ def inTok:boolean false
167
+ def i:int 0
168
+ while (i < len) {
169
+ def c:int (charAt spec i)
170
+ if ((c == 40) || (c == 91)) {
171
+ depth = (depth + 1)
172
+ }
173
+ if ((c == 41) || (c == 93)) {
174
+ depth = (depth - 1)
175
+ }
176
+ def isSpace:boolean (((c == 32) || (c == 9)) || ((c == 10) || (c == 13)))
177
+ if (isSpace && (depth == 0)) {
178
+ if inTok {
179
+ push out (substring spec start i)
180
+ inTok = false
181
+ }
182
+ } {
183
+ if (inTok == false) {
184
+ start = i
185
+ inTok = true
186
+ }
187
+ }
188
+ i = (i + 1)
189
+ }
190
+ if inTok {
191
+ push out (substring spec start len)
192
+ }
193
+ return out
194
+ }
195
+
196
+ sfn isRepeat:boolean (tok:string) {
197
+ if ((strlen tok) < 8) {
198
+ return false
199
+ }
200
+ return ((substring tok 0 7) == "repeat(")
201
+ }
202
+
203
+ ; repeat(N, <track list>) -> the track list N times.
204
+ fn expandRepeat:void (tok:string) {
205
+ def len:int (strlen tok)
206
+ def close:int (len - 1)
207
+ if ((charAt tok close) != 41) {
208
+ hadError = true
209
+ errorText = ("Malformed repeat(): " + tok)
210
+ return
211
+ }
212
+ def inner:string (substring tok 7 close)
213
+ def comma:int 0 - 1
214
+ def j:int 0
215
+ while (j < (strlen inner)) {
216
+ if ((charAt inner j) == 44) {
217
+ comma = j
218
+ j = (strlen inner)
219
+ } {
220
+ j = (j + 1)
221
+ }
222
+ }
223
+ if (comma < 0) {
224
+ hadError = true
225
+ errorText = ("repeat() needs a count and a track list: " + tok)
226
+ return
227
+ }
228
+ def countStr:string (trim (substring inner 0 comma))
229
+ def listStr:string (trim (substring inner (comma + 1) (strlen inner)))
230
+ def countVal@(optional):double (to_double countStr)
231
+ def n:int 0
232
+ if countVal {
233
+ n = (to_int (unwrap countVal))
234
+ } {
235
+ hadError = true
236
+ errorText = ("repeat() count is not a number: " + tok)
237
+ return
238
+ }
239
+ if (n < 1) {
240
+ hadError = true
241
+ errorText = ("repeat() count must be at least 1: " + tok)
242
+ return
243
+ }
244
+ def inner2:[string] (EVGGridTemplate.tokenize(listStr))
245
+ def r:int 0
246
+ while (r < n) {
247
+ def k:int 0
248
+ while (k < (array_length inner2)) {
249
+ ; Nested repeat() is out of scope, but minmax() inside repeat()
250
+ ; is the ordinary way to write a responsive album grid.
251
+ def innerTok:string (itemAt inner2 k)
252
+ if (EVGGridTemplate.isMinmax(innerTok)) {
253
+ this.addMinmax(innerTok)
254
+ } {
255
+ this.addTrack(innerTok)
256
+ }
257
+ k = (k + 1)
258
+ }
259
+ r = (r + 1)
260
+ }
261
+ }
262
+
263
+ sfn isMinmax:boolean (tok:string) {
264
+ if ((strlen tok) < 8) {
265
+ return false
266
+ }
267
+ return ((substring tok 0 7) == "minmax(")
268
+ }
269
+
270
+ ; minmax(<min>, <max>): the track sizes as its max but is clamped into the
271
+ ; range once the fr share is known. A fixed min with an fr max is the common
272
+ ; photo-grid case — "at least 120px, otherwise share what is left".
273
+ fn addMinmax:void (tok:string) {
274
+ def len:int (strlen tok)
275
+ def close:int (len - 1)
276
+ if ((charAt tok close) != 41) {
277
+ hadError = true
278
+ errorText = ("Malformed minmax(): " + tok)
279
+ return
280
+ }
281
+ def inner:string (substring tok 7 close)
282
+ def comma:int 0 - 1
283
+ def j:int 0
284
+ while (j < (strlen inner)) {
285
+ if ((charAt inner j) == 44) {
286
+ comma = j
287
+ j = (strlen inner)
288
+ } {
289
+ j = (j + 1)
290
+ }
291
+ }
292
+ if (comma < 0) {
293
+ hadError = true
294
+ errorText = ("minmax() needs two values: " + tok)
295
+ return
296
+ }
297
+ def minStr:string (trim (substring inner 0 comma))
298
+ def maxStr:string (trim (substring inner (comma + 1) (strlen inner)))
299
+
300
+ ; The max decides how the track grows; the min is a floor applied after.
301
+ def before:int (array_length tracks)
302
+ this.addTrack(maxStr)
303
+ if ((array_length tracks) == before) {
304
+ return
305
+ }
306
+ def track:EVGGridTrack (itemAt tracks ((array_length tracks) - 1))
307
+ if ((EVGGridTemplate.isFrToken(minStr)) == false) {
308
+ track.minUnit = (EVGUnit.parse(minStr))
309
+ track.hasMin = track.minUnit.isSet
310
+ }
311
+ if ((EVGGridTemplate.isFrToken(maxStr)) == false) {
312
+ track.maxUnit = (EVGUnit.parse(maxStr))
313
+ track.hasMax = track.maxUnit.isSet
314
+ }
315
+ }
316
+
317
+ sfn isFrToken:boolean (tok:string) {
318
+ def len:int (strlen tok)
319
+ if (len < 2) {
320
+ return false
321
+ }
322
+ return ((substring tok (len - 2) len) == "fr")
323
+ }
324
+
325
+ fn addTrack:void (tok:string) {
326
+ def t:string (trim tok)
327
+ def len:int (strlen t)
328
+ if (len == 0) {
329
+ return
330
+ }
331
+ def track (new EVGGridTrack())
332
+
333
+ ; `auto` is an intrinsic track: sized to the content of whatever lands
334
+ ; in it. It used to be 1fr, which is predictable but not what CSS does.
335
+ if (t == "auto") {
336
+ track.kind = 3
337
+ push tracks track
338
+ return
339
+ }
340
+
341
+ ; fit-content(limit) — an intrinsic track with a ceiling.
342
+ if (EVGGridTemplate.isFitContent(t)) {
343
+ def inner:string (trim (substring t 12 (len - 1)))
344
+ def lim:EVGUnit (EVGUnit.parse(inner))
345
+ if (lim.isSet == false) {
346
+ hadError = true
347
+ errorText = ("Unsupported fit-content() limit: " + inner)
348
+ return
349
+ }
350
+ track.kind = 3
351
+ track.hasFitLimit = true
352
+ track.fitLimit = lim
353
+ push tracks track
354
+ return
355
+ }
356
+
357
+ if (len > 2) {
358
+ if ((substring t (len - 2) len) == "fr") {
359
+ def numStr:string (substring t 0 (len - 2))
360
+ def frVal@(optional):double (to_double numStr)
361
+ if frVal {
362
+ track.kind = 2
363
+ track.value = (unwrap frVal)
364
+ push tracks track
365
+ return
366
+ }
367
+ }
368
+ }
369
+ if (t == "fr") {
370
+ track.kind = 2
371
+ track.value = 1.0
372
+ push tracks track
373
+ return
374
+ }
375
+
376
+ def unit:EVGUnit (EVGUnit.parse(t))
377
+ if (unit.isSet == false) {
378
+ hadError = true
379
+ errorText = ("Unsupported track size: " + t)
380
+ return
381
+ }
382
+ if (unit.unitType == 1) {
383
+ track.kind = 1
384
+ track.value = unit.value
385
+ } {
386
+ track.kind = 0
387
+ track.value = unit.value
388
+ }
389
+ push tracks track
390
+ }
391
+
392
+ ; Resolve every track to pixels inside `available`, which must already have
393
+ ; the gaps subtracted. Fixed and percentage tracks take their size first;
394
+ ; whatever is left is split between the fr tracks in proportion.
395
+ ; Does any track need to know what is in it?
396
+ fn hasIntrinsicTrack:boolean () {
397
+ def i:int 0
398
+ while (i < (array_length tracks)) {
399
+ def t:EVGGridTrack (itemAt tracks i)
400
+ if (t.kind == 3) {
401
+ return true
402
+ }
403
+ i = (i + 1)
404
+ }
405
+ return false
406
+ }
407
+
408
+ sfn isFitContent:boolean (tok:string) {
409
+ if ((strlen tok) < 14) {
410
+ return false
411
+ }
412
+ if ((substring tok 0 12) != "fit-content(") {
413
+ return false
414
+ }
415
+ return ((charAt tok ((strlen tok) - 1)) == 41)
416
+ }
417
+
418
+ ; Size the tracks with no content information — every intrinsic track ends
419
+ ; up at zero. Callers that know what is in the tracks use resolveWithContent.
420
+ fn resolve:void (available:double) {
421
+ def empty:[double]
422
+ def empty2:[double]
423
+ this.resolveWithContent(available empty empty2)
424
+ }
425
+
426
+ ; `content` and `minContent` give each track's max-content and min-content
427
+ ; width by index — the widest item placed in it, and the narrowest that
428
+ ; item can be squeezed to. Shorter than the track list (or empty) means the
429
+ ; rest have no content.
430
+ fn resolveWithContent:void (available:double content:[double] minContent:[double]) {
431
+ def n:int (array_length tracks)
432
+ def used:double 0.0
433
+ def totalFr:double 0.0
434
+ def i:int 0
435
+ while (i < n) {
436
+ def t:EVGGridTrack (itemAt tracks i)
437
+ t.frozen = false
438
+ ; Resolve any minmax() bounds against the container first, so the
439
+ ; clamp below is in pixels like everything else.
440
+ if t.hasMin {
441
+ t.minUnit.resolve(available 16.0)
442
+ }
443
+ if t.hasMax {
444
+ t.maxUnit.resolve(available 16.0)
445
+ }
446
+ if (t.kind == 0) {
447
+ t.sizePx = t.value
448
+ used = (used + t.sizePx)
449
+ t.frozen = true
450
+ }
451
+ if (t.kind == 1) {
452
+ t.sizePx = ((available * t.value) / 100.0)
453
+ used = (used + t.sizePx)
454
+ t.frozen = true
455
+ }
456
+ if (t.kind == 2) {
457
+ totalFr = (totalFr + t.value)
458
+ }
459
+ if (t.kind == 3) {
460
+ ; Intrinsic: the widest thing in the track, capped by a
461
+ ; fit-content() limit if there is one. CSS floors that clamp at
462
+ ; min-content, but an item that cannot wrap has min-content ==
463
+ ; max-content, and this engine only measures items that cannot
464
+ ; — so the floor is the measurement itself and clamping below
465
+ ; it would make the item overflow its own track.
466
+ def c:double 0.0
467
+ if (i < (array_length content)) {
468
+ c = (itemAt content i)
469
+ }
470
+ if t.hasFitLimit {
471
+ t.fitLimit.resolve(available 16.0)
472
+ if (c > t.fitLimit.pixels) {
473
+ c = t.fitLimit.pixels
474
+ }
475
+ ; The clamp is floored at min-content: an item that cannot
476
+ ; be made narrower than the limit keeps its size and the
477
+ ; track grows to hold it, rather than the item spilling out
478
+ ; of its own cell. For an item with a definite width that
479
+ ; floor IS its width, which is why fit-content(100px)
480
+ ; around a 200px box measures 200 in a browser.
481
+ if (i < (array_length minContent)) {
482
+ def floorPx:double (itemAt minContent i)
483
+ if (c < floorPx) {
484
+ c = floorPx
485
+ }
486
+ }
487
+ }
488
+ t.sizePx = c
489
+ used = (used + c)
490
+ t.frozen = true
491
+ }
492
+ i = (i + 1)
493
+ }
494
+
495
+ ; Distribute the leftover across the fr tracks, then clamp against any
496
+ ; minmax() bounds. A track that hits a bound is pinned there and the
497
+ ; space it did not take is offered to the rest — the same freeze-and-
498
+ ; redistribute the flex axis uses, and the reason `minmax(120px, 1fr)`
499
+ ; can hold its floor without stealing from its neighbours silently.
500
+ def poolSpace:double (available - used)
501
+ if (poolSpace < 0.0) {
502
+ poolSpace = 0.0
503
+ }
504
+ def poolFr:double totalFr
505
+ def pass:int 0
506
+ def settled:boolean false
507
+ while ((pass <= n) && (settled == false)) {
508
+ settled = true
509
+ def j:int 0
510
+ while (j < n) {
511
+ def t2:EVGGridTrack (itemAt tracks j)
512
+ if ((t2.kind == 2) && (t2.frozen == false)) {
513
+ def size:double 0.0
514
+ if (poolFr > 0.0) {
515
+ size = ((poolSpace * t2.value) / poolFr)
516
+ }
517
+ def clamped:double size
518
+ if t2.hasMin {
519
+ if (clamped < t2.minUnit.pixels) {
520
+ clamped = t2.minUnit.pixels
521
+ }
522
+ }
523
+ if t2.hasMax {
524
+ if (clamped > t2.maxUnit.pixels) {
525
+ clamped = t2.maxUnit.pixels
526
+ }
527
+ }
528
+ t2.sizePx = clamped
529
+ if (clamped != size) {
530
+ t2.frozen = true
531
+ poolSpace = (poolSpace - clamped)
532
+ poolFr = (poolFr - t2.value)
533
+ if (poolSpace < 0.0) {
534
+ poolSpace = 0.0
535
+ }
536
+ settled = false
537
+ }
538
+ }
539
+ j = (j + 1)
540
+ }
541
+ pass = (pass + 1)
542
+ }
543
+
544
+ ; A fixed or percentage track can still carry a minmax() floor.
545
+ def k:int 0
546
+ while (k < n) {
547
+ def t3:EVGGridTrack (itemAt tracks k)
548
+ if (t3.kind != 2) {
549
+ if t3.hasMin {
550
+ if (t3.sizePx < t3.minUnit.pixels) {
551
+ t3.sizePx = t3.minUnit.pixels
552
+ }
553
+ }
554
+ if t3.hasMax {
555
+ if (t3.sizePx > t3.maxUnit.pixels) {
556
+ t3.sizePx = t3.maxUnit.pixels
557
+ }
558
+ }
559
+ }
560
+ k = (k + 1)
561
+ }
562
+ }
563
+
564
+ ; Extent of `span` tracks starting at index `from`, including the gaps
565
+ ; between them (but not the gaps on either outer edge).
566
+ fn extentOf:double (from:int span:int gap:double) {
567
+ def total:double 0.0
568
+ def n:int (array_length tracks)
569
+ def i:int from
570
+ def placed:int 0
571
+ while ((i < n) && (placed < span)) {
572
+ def tk:EVGGridTrack (itemAt tracks i)
573
+ total = (total + tk.sizePx)
574
+ placed = (placed + 1)
575
+ i = (i + 1)
576
+ }
577
+ if (placed > 1) {
578
+ total = (total + ((to_double (placed - 1)) * gap))
579
+ }
580
+ return total
581
+ }
582
+
583
+ ; Distance from the container's content edge to the start of track `index`.
584
+ fn offsetOf:double (index:int gap:double) {
585
+ def total:double 0.0
586
+ def i:int 0
587
+ while (i < index) {
588
+ def tk:EVGGridTrack (itemAt tracks i)
589
+ total = (total + tk.sizePx)
590
+ total = (total + gap)
591
+ i = (i + 1)
592
+ }
593
+ return total
594
+ }
595
+ }
596
+
597
+ ; A `grid-column` / `grid-row` value.
598
+ ; start: 1-based grid line, or 0 for "wherever auto-flow puts it"
599
+ ; span : number of tracks covered, at least 1
600
+ ; grid-template-areas — a picture of the page in names:
601
+ ;
602
+ ; grid-template-areas="'hero hero' 'left right'"
603
+ ;
604
+ ; Each quoted string is a row, each name a column. An item claims a region with
605
+ ; grid-area="hero". A name repeated across adjacent cells forms one rectangle,
606
+ ; which is the whole point: the layout reads as the shape it produces.
607
+ ;
608
+ ; "." is the CSS spelling for a deliberately empty cell.
609
+ class EVGGridAreas {
610
+ def names:[string]
611
+ def rowStart:[int]
612
+ def colStart:[int]
613
+ def rowSpan:[int]
614
+ def colSpan:[int]
615
+ def columns:int 0
616
+ def rows:int 0
617
+ def hadError:boolean false
618
+ def errorText:string ""
619
+
620
+ Constructor () {
621
+ columns = 0
622
+ rows = 0
623
+ hadError = false
624
+ errorText = ""
625
+ }
626
+
627
+ fn count:int () {
628
+ return (array_length names)
629
+ }
630
+
631
+ fn indexOfName:int (name:string) {
632
+ def i:int 0
633
+ while (i < (array_length names)) {
634
+ if ((itemAt names i) == name) {
635
+ return i
636
+ }
637
+ i = (i + 1)
638
+ }
639
+ return 0 - 1
640
+ }
641
+
642
+ ; Split the spec into rows. Both quote styles are accepted, and a row may
643
+ ; also be separated by a newline so a stylesheet can lay it out readably.
644
+ sfn parse:EVGGridAreas (spec:string) {
645
+ def areas (new EVGGridAreas())
646
+ def rowsOut:[string]
647
+ def cur:string ""
648
+ def inQuote:boolean false
649
+ def quoteCh:int 0
650
+ def i:int 0
651
+ def len:int (strlen spec)
652
+ while (i < len) {
653
+ def c:int (charAt spec i)
654
+ if inQuote {
655
+ if (c == quoteCh) {
656
+ push rowsOut cur
657
+ cur = ""
658
+ inQuote = false
659
+ } {
660
+ cur = cur + (strfromcode c)
661
+ }
662
+ } {
663
+ if ((c == 34) || (c == 39)) {
664
+ inQuote = true
665
+ quoteCh = c
666
+ }
667
+ }
668
+ i = (i + 1)
669
+ }
670
+ if inQuote {
671
+ areas.hadError = true
672
+ areas.errorText = ("Unterminated row in grid-template-areas: " + spec)
673
+ return areas
674
+ }
675
+ if ((array_length rowsOut) == 0) {
676
+ areas.hadError = true
677
+ areas.errorText = ("grid-template-areas needs quoted rows: " + spec)
678
+ return areas
679
+ }
680
+ areas.build(rowsOut)
681
+ return areas
682
+ }
683
+
684
+ ; Turn the grid of names into one rectangle per name. A name that appears in
685
+ ; a non-rectangular pattern is reported rather than guessed at, because the
686
+ ; result would silently overlap another area.
687
+ fn build:void (rowsOut:[string]) {
688
+ rows = (array_length rowsOut)
689
+ def cells:[string]
690
+ def r:int 0
691
+ while (r < rows) {
692
+ def toks:[string] (EVGGridTemplate.tokenize((itemAt rowsOut r)))
693
+ if (r == 0) {
694
+ columns = (array_length toks)
695
+ } {
696
+ if ((array_length toks) != columns) {
697
+ hadError = true
698
+ errorText = "grid-template-areas rows must all have the same number of columns"
699
+ return
700
+ }
701
+ }
702
+ def c:int 0
703
+ while (c < (array_length toks)) {
704
+ push cells (itemAt toks c)
705
+ c = (c + 1)
706
+ }
707
+ r = (r + 1)
708
+ }
709
+ if (columns == 0) {
710
+ hadError = true
711
+ errorText = "grid-template-areas has no columns"
712
+ return
713
+ }
714
+
715
+ def rr:int 0
716
+ while (rr < rows) {
717
+ def cc:int 0
718
+ while (cc < columns) {
719
+ def name:string (itemAt cells ((rr * columns) + cc))
720
+ if (name != ".") {
721
+ def at:int (this.indexOfName(name))
722
+ if (at < 0) {
723
+ push names name
724
+ push rowStart rr
725
+ push colStart cc
726
+ push rowSpan 1
727
+ push colSpan 1
728
+ } {
729
+ ; Extend the existing rectangle to cover this cell.
730
+ def r0:int (itemAt rowStart at)
731
+ def c0:int (itemAt colStart at)
732
+ def rs:int (itemAt rowSpan at)
733
+ def cs:int (itemAt colSpan at)
734
+ if ((rr + 1) > (r0 + rs)) {
735
+ set rowSpan at ((rr + 1) - r0)
736
+ }
737
+ if ((cc + 1) > (c0 + cs)) {
738
+ set colSpan at ((cc + 1) - c0)
739
+ }
740
+ }
741
+ }
742
+ cc = (cc + 1)
743
+ }
744
+ rr = (rr + 1)
745
+ }
746
+
747
+ ; Every cell inside a name's rectangle must actually carry that name.
748
+ def n:int (array_length names)
749
+ def k:int 0
750
+ while (k < n) {
751
+ def nm:string (itemAt names k)
752
+ def r0:int (itemAt rowStart k)
753
+ def c0:int (itemAt colStart k)
754
+ def rs:int (itemAt rowSpan k)
755
+ def cs:int (itemAt colSpan k)
756
+ def a:int 0
757
+ while (a < rs) {
758
+ def b:int 0
759
+ while (b < cs) {
760
+ def idx:int (((r0 + a) * columns) + (c0 + b))
761
+ if ((itemAt cells idx) != nm) {
762
+ hadError = true
763
+ errorText = ("Area \"" + nm + "\" is not a rectangle in grid-template-areas")
764
+ return
765
+ }
766
+ b = (b + 1)
767
+ }
768
+ a = (a + 1)
769
+ }
770
+ k = (k + 1)
771
+ }
772
+ }
773
+ }
774
+
775
+ class EVGGridPlacement {
776
+ def start:int 0
777
+ def span:int 1
778
+ ; A placement this parser cannot honour. Named lines ("grid-column:
779
+ ; sidebar") are the case that matters: they used to fall through
780
+ ; lineNumber's `to_double` and come back as 0, which is indistinguishable
781
+ ; from auto-placement — the item quietly landed wherever the flow put it
782
+ ; and nothing said the name had been ignored.
783
+ def hadError:boolean false
784
+ def errorText:string ""
785
+ ; Line names, kept unresolved: a placement is parsed from the item, but the
786
+ ; names live on the container's template, which only the layout can see.
787
+ ; EVGLayout calls resolveNames() once it has both.
788
+ def startName:string ""
789
+ def endName:string ""
790
+ ; A numeric end line, so span can be recomputed if the start moves when a
791
+ ; name resolves. 0 = none.
792
+ def endLine:int 0
793
+ ; True when the span came from an explicit "span N", which outranks any
794
+ ; end line.
795
+ def spanExplicit:boolean false
796
+
797
+ Constructor () {
798
+ start = 0
799
+ span = 1
800
+ hadError = false
801
+ errorText = ""
802
+ startName = ""
803
+ endName = ""
804
+ endLine = 0
805
+ spanExplicit = false
806
+ }
807
+
808
+ ; "span 2" | "2" | "2 / 4" | "2 / span 3" | "" (auto)
809
+ sfn parse:EVGGridPlacement (spec:string) {
810
+ def p (new EVGGridPlacement())
811
+ def s:string (trim spec)
812
+ if ((strlen s) == 0) {
813
+ return p
814
+ }
815
+
816
+ ; Split on '/' into at most two sides.
817
+ def slash:int 0 - 1
818
+ def i:int 0
819
+ while (i < (strlen s)) {
820
+ if ((charAt s i) == 47) {
821
+ slash = i
822
+ i = (strlen s)
823
+ } {
824
+ i = (i + 1)
825
+ }
826
+ }
827
+
828
+ if (slash < 0) {
829
+ ; One side: "span N", a line number, or a line name.
830
+ if (EVGGridPlacement.isSpan(s)) {
831
+ p.span = (EVGGridPlacement.spanCount(s))
832
+ p.spanExplicit = true
833
+ } {
834
+ p.takeStart(s)
835
+ }
836
+ return p
837
+ }
838
+
839
+ def lhs:string (trim (substring s 0 slash))
840
+ def rhs:string (trim (substring s (slash + 1) (strlen s)))
841
+ p.takeStart(lhs)
842
+ if (EVGGridPlacement.isSpan(rhs)) {
843
+ p.span = (EVGGridPlacement.spanCount(rhs))
844
+ p.spanExplicit = true
845
+ } {
846
+ p.takeEnd(rhs)
847
+ }
848
+ p.applyEndLine()
849
+ return p
850
+ }
851
+
852
+ ; A side is a line number, a line name, or nothing this subset can use.
853
+ ; Something that looks like a number but is not a usable one — 0, or a
854
+ ; negative line, CSS's count-from-the-end form — is rejected outright
855
+ ; rather than treated as a name it plainly is not.
856
+ fn takeStart:void (token:string) {
857
+ if (EVGGridPlacement.isNumericToken(token)) {
858
+ start = (EVGGridPlacement.lineNumber(token))
859
+ if (start == 0) {
860
+ this.reject(token)
861
+ }
862
+ } {
863
+ startName = token
864
+ }
865
+ }
866
+
867
+ fn takeEnd:void (token:string) {
868
+ if (EVGGridPlacement.isNumericToken(token)) {
869
+ endLine = (EVGGridPlacement.lineNumber(token))
870
+ if (endLine == 0) {
871
+ this.reject(token)
872
+ }
873
+ } {
874
+ endName = token
875
+ }
876
+ }
877
+
878
+ ; "2 / 4" covers the tracks between lines 2 and 4, i.e. two tracks.
879
+ fn applyEndLine:void () {
880
+ if spanExplicit {
881
+ return
882
+ }
883
+ if (start < 1) {
884
+ return
885
+ }
886
+ if (endLine > start) {
887
+ span = (endLine - start)
888
+ }
889
+ }
890
+
891
+ ; Point the named sides at real lines, now that the container's template is
892
+ ; known. A name the template does not define leaves the item auto-placed
893
+ ; and is reported, the same as any other declaration the engine cannot
894
+ ; honour.
895
+ fn resolveNames:void (tpl:EVGGridTemplate) {
896
+ if ((strlen startName) > 0) {
897
+ def n:int (tpl.lineNumberNamed(startName))
898
+ if (n > 0) {
899
+ start = n
900
+ startName = ""
901
+ } {
902
+ this.rejectName(startName)
903
+ }
904
+ }
905
+ if ((strlen endName) > 0) {
906
+ def e:int (tpl.lineNumberNamed(endName))
907
+ if (e > 0) {
908
+ endLine = e
909
+ endName = ""
910
+ } {
911
+ this.rejectName(endName)
912
+ }
913
+ }
914
+ this.applyEndLine()
915
+ }
916
+
917
+ sfn isNumericToken:boolean (s:string) {
918
+ if ((strlen s) == 0) {
919
+ return false
920
+ }
921
+ def c:int (charAt s 0)
922
+ if ((c >= 48) && (c <= 57)) {
923
+ return true
924
+ }
925
+ return ((c == 45) || (c == 43))
926
+ }
927
+
928
+ fn rejectName:void (name:string) {
929
+ if hadError {
930
+ return
931
+ }
932
+ hadError = true
933
+ errorText = ("no line named \"" + name + "\"; the item was auto-placed")
934
+ }
935
+
936
+ ; A token that looks numeric but is not a line this engine can use: 0, or
937
+ ; a negative line, which CSS counts from the end of the grid.
938
+ fn reject:void (token:string) {
939
+ if hadError {
940
+ return
941
+ }
942
+ hadError = true
943
+ errorText = ("unsupported line \"" + token + "\" (negative line numbers are not supported); the item was auto-placed")
944
+ }
945
+
946
+ sfn isSpan:boolean (s:string) {
947
+ if ((strlen s) < 4) {
948
+ return false
949
+ }
950
+ return ((substring s 0 4) == "span")
951
+ }
952
+
953
+ sfn spanCount:int (s:string) {
954
+ def rest:string (trim (substring s 4 (strlen s)))
955
+ def v@(optional):double (to_double rest)
956
+ if v {
957
+ def n:int (to_int (unwrap v))
958
+ if (n >= 1) {
959
+ return n
960
+ }
961
+ }
962
+ return 1
963
+ }
964
+
965
+ sfn lineNumber:int (s:string) {
966
+ def v@(optional):double (to_double s)
967
+ if v {
968
+ def n:int (to_int (unwrap v))
969
+ if (n >= 1) {
970
+ return n
971
+ }
972
+ }
973
+ return 0
974
+ }
975
+ }