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,894 @@
1
+ ; SPDX-License-Identifier: MIT
2
+
3
+ ; ==============================================================================
4
+ ; EVGA11yTree.rgr — the second list: what the frame MEANS.
5
+ ; ==============================================================================
6
+ ;
7
+ ; EVGDisplayList carries geometry — absolute pixels, resolved colours, no tree.
8
+ ; That is exactly what a GPU wants and exactly what a screen reader cannot use.
9
+ ; NVDA, VoiceOver, JAWS and Orca do not look at pixels: they ask the platform
10
+ ; for a TREE of nodes with a role, a name, a value, a state and a rectangle,
11
+ ; and they walk it with the arrow keys, drive it with Tab, mirror it onto a
12
+ ; braille display and speak what changed.
13
+ ;
14
+ ; So a program drawn with EVG on WebGL or SDL2+OpenGL is, to an assistive
15
+ ; technology, one empty graphic. The fix is not a smarter renderer and it is
16
+ ; certainly not guessing meaning back out of the display list — that is OCR
17
+ ; against your own program, and the list is lossy on purpose. The fix is to
18
+ ; emit a SECOND list from the SAME walk:
19
+ ;
20
+ ; +--> EVGDisplayList (geometry) --> WebGL / SDL2 / PDF
21
+ ; app state --walk---|
22
+ ; +--> EVGA11yTree (meaning) --> DOM mirror / UIA /
23
+ ; AT-SPI / NSAccessibility
24
+ ;
25
+ ; One walk, because two walks drift: five painters each walking the laid-out
26
+ ; tree is how border-radius came to work in PDF and silently not in PNG. The
27
+ ; same bug in an accessibility tree would be invisible to everyone who can see
28
+ ; the screen, and only a blind user would ever hit it.
29
+ ;
30
+ ; This module is PURE — no device resources, no host calls, no file system — so
31
+ ; it compiles to ES6, C++, Rust and Go like the display list does. That is the
32
+ ; point: the accessibility model is written once and every host consumes it,
33
+ ; instead of being reimplemented per platform the way it normally is.
34
+ ;
35
+ ; The field set is deliberately the intersection of ARIA, UI Automation,
36
+ ; AT-SPI2, NSAccessibility and AccessKit. Everything here exists in all five,
37
+ ; which is what keeps the browser mirror and a future native adapter reading
38
+ ; the same tree.
39
+ ;
40
+ ; Bounds are ints. Every EVG surface that has semantics — EVGControl, the grid
41
+ ; geometry, the toolbar — already works in whole pixels, and an assistive
42
+ ; technology uses a rectangle to route the mouse, place a magnifier and answer
43
+ ; a touch, none of which care about a quarter of a pixel.
44
+ ; ==============================================================================
45
+
46
+ ; ------------------------------------------------------------------------------
47
+ ; Roles. Ints so a backend can switch cheaply — the same reason the draw
48
+ ; commands are ints — with the ARIA spelling kept here so every host agrees on
49
+ ; the vocabulary instead of each inventing its own mapping.
50
+ ; ------------------------------------------------------------------------------
51
+ class EVGA11yRole {
52
+ ; Decoration. Never surfaced: a node with this role and everything under it
53
+ ; is skipped, which is how a reader is kept from announcing 300 rectangles.
54
+ sfn none:int () { return 0 }
55
+ sfn group:int () { return 1 }
56
+ sfn text:int () { return 2 }
57
+ sfn heading:int () { return 3 }
58
+ sfn button:int () { return 4 }
59
+ sfn checkbox:int () { return 5 }
60
+ sfn radio:int () { return 6 }
61
+ sfn textField:int () { return 7 }
62
+ sfn image:int () { return 8 }
63
+ sfn link:int () { return 9 }
64
+ sfn grid:int () { return 10 }
65
+ sfn row:int () { return 11 }
66
+ sfn columnHeader:int () { return 12 }
67
+ sfn rowHeader:int () { return 13 }
68
+ sfn cell:int () { return 14 }
69
+ sfn tabList:int () { return 15 }
70
+ sfn tab:int () { return 16 }
71
+ sfn toolbar:int () { return 17 }
72
+ sfn menuItem:int () { return 18 }
73
+ sfn dialog:int () { return 19 }
74
+ sfn status:int () { return 20 }
75
+ sfn separator:int () { return 21 }
76
+ sfn list:int () { return 22 }
77
+ sfn listItem:int () { return 23 }
78
+ ; Added for the gallery/ui parity work: roles Radix exposes that this
79
+ ; catalogue had no word for. Appended, so every existing number stands.
80
+ sfn switchControl:int () { return 24 }
81
+ sfn radioGroup:int () { return 25 }
82
+ sfn tabPanel:int () { return 26 }
83
+ sfn region:int () { return 27 }
84
+ sfn progressBar:int () { return 28 }
85
+ ; The overlay family: a menu and its surface, a tooltip, the dialog
86
+ ; variant a reader must interrupt for, and a value a user drags.
87
+ sfn menu:int () { return 29 }
88
+ sfn tooltip:int () { return 30 }
89
+ sfn alertDialog:int () { return 31 }
90
+ sfn slider:int () { return 32 }
91
+ ; A menu bar and the two menu rows that carry a state. ARIA spells these as
92
+ ; separate roles rather than a menuitem with aria-checked, and a reader
93
+ ; announces them differently, so they are separate here too.
94
+ sfn menuBar:int () { return 33 }
95
+ sfn menuItemCheckbox:int () { return 34 }
96
+ sfn menuItemRadio:int () { return 35 }
97
+ ; A select: a button that opens a list of options, and the list itself.
98
+ ; `combobox` even when nothing can be typed — it is what the reference
99
+ ; reports, and what every platform maps a picker onto.
100
+ sfn comboBox:int () { return 36 }
101
+ sfn listBox:int () { return 37 }
102
+ sfn option:int () { return 38 }
103
+ ; `table` and not `grid`. They are different promises: a grid is a widget
104
+ ; you arrow around cell by cell, a table is tabular DATA with ordinary
105
+ ; controls inside it. A sortable, selectable table of the ReUI kind is the
106
+ ; second — you tab to the sort button and the checkbox, you do not steer a
107
+ ; cell cursor — and a native `<table>`, which is what the reference
108
+ ; renders, reports exactly this.
109
+ sfn table:int () { return 39 }
110
+ ; `cell` and `gridcell` are different roles, not two spellings. A cell
111
+ ; belongs to a table, a gridcell to a grid — and a reader announces the
112
+ ; second as something you can steer around with the arrow keys. `cell()`
113
+ ; keeps its old meaning so the datagrid is untouched.
114
+ sfn tableCell:int () { return 40 }
115
+
116
+ ; A tree, and its rows. Nesting is carried by `aria-level` and not by a
117
+ ; `group` per folder — which is what headless-tree does, and therefore what
118
+ ; ReUI's tree does: `getItems()` hands back the visible rows already
119
+ ; flattened, all siblings, each saying how deep it is.
120
+ sfn tree:int () { return 41 }
121
+ sfn treeItem:int () { return 42 }
122
+
123
+ ; A landmark. `navigation` is the one a breadcrumb needs, and it needs a
124
+ ; NAME with it: a page has several navigations and an unnamed one leaves a
125
+ ; reader to guess which of them it has landed in.
126
+ sfn navigation:int () { return 43 }
127
+
128
+ ; A form is a landmark ONLY when it is named, which is the HTML-ARIA
129
+ ; mapping and not a simplification: an unnamed <form> is not a landmark,
130
+ ; because a reader offered "form" with nothing to distinguish it from the
131
+ ; other three on the page has been given a list and no way to choose from
132
+ ; it. The demo names its own; a controller that does not should not use
133
+ ; this role.
134
+ sfn form:int () { return 44 }
135
+
136
+ ; The page's own landmarks: the header strip and the content. A reader
137
+ ; jumps between them, and a screen with a bottom bar and a top bar has to
138
+ ; say which of its three regions is which.
139
+ sfn banner:int () { return 47 }
140
+ sfn mainContent:int () { return 48 }
141
+ ; A paragraph of running text. Rendered markdown is paragraphs, and a
142
+ ; reader steps through them; without the role the text was dropped from
143
+ ; the tree with everything under it.
144
+ sfn paragraph:int () { return 49 }
145
+ ; A number field that steps: the value moves with the arrow keys and is
146
+ ; published as valuenow/min/max, the way a slider's is.
147
+ sfn spinButton:int () { return 50 }
148
+
149
+ ; `rowgroup` — a thead or a tbody. Missing until a table was built out of
150
+ ; roles rather than out of `<table>` tags, and an unrecognised role drops
151
+ ; the element AND EVERYTHING UNDER IT, so the whole body of the dashboard's
152
+ ; table was absent from the tree while the picture looked perfect. The
153
+ ; lint is the only reason that was noticed.
154
+ sfn rowGroup:int () { return 45 }
155
+
156
+ ; A LOG: a live region whose new content is appended, and which a reader
157
+ ; announces as it arrives. `status` is the other polite one and they are
158
+ ; not interchangeable — a status is a single changing value, a log is a
159
+ ; growing list, and a transcript is the second.
160
+ ;
161
+ ; Added because a tree literal saying role="log" produced no error and no
162
+ ; node: `EVGA11yFromTree` drops a role it does not know AND everything
163
+ ; under it, so a whole conversation went missing from the accessible tree
164
+ ; while the picture looked perfect. The lint said so, which is the only
165
+ ; reason it was noticed.
166
+ sfn log:int () { return 46 }
167
+
168
+ ; The ARIA role string. A browser host writes this straight into role="…";
169
+ ; a native adapter maps it to the platform's own constant.
170
+ sfn ariaName:string (role:int) {
171
+ if (role == 43) { return "navigation" }
172
+ if (role == 44) { return "form" }
173
+ if (role == 45) { return "rowgroup" }
174
+ if (role == 46) { return "log" }
175
+ if (role == 47) { return "banner" }
176
+ if (role == 48) { return "main" }
177
+ if (role == 49) { return "paragraph" }
178
+ if (role == 50) { return "spinbutton" }
179
+ if (role == 41) { return "tree" }
180
+ if (role == 42) { return "treeitem" }
181
+ if (role == 1) { return "group" }
182
+ if (role == 2) { return "text" }
183
+ if (role == 3) { return "heading" }
184
+ if (role == 4) { return "button" }
185
+ if (role == 5) { return "checkbox" }
186
+ if (role == 6) { return "radio" }
187
+ if (role == 7) { return "textbox" }
188
+ if (role == 8) { return "img" }
189
+ if (role == 9) { return "link" }
190
+ if (role == 39) { return "table" }
191
+ if (role == 40) { return "cell" }
192
+ if (role == 10) { return "grid" }
193
+ if (role == 11) { return "row" }
194
+ if (role == 12) { return "columnheader" }
195
+ if (role == 13) { return "rowheader" }
196
+ if (role == 14) { return "gridcell" }
197
+ if (role == 15) { return "tablist" }
198
+ if (role == 16) { return "tab" }
199
+ if (role == 17) { return "toolbar" }
200
+ if (role == 18) { return "menuitem" }
201
+ if (role == 19) { return "dialog" }
202
+ if (role == 20) { return "status" }
203
+ if (role == 21) { return "separator" }
204
+ if (role == 22) { return "list" }
205
+ if (role == 23) { return "listitem" }
206
+ if (role == 24) { return "switch" }
207
+ if (role == 25) { return "radiogroup" }
208
+ if (role == 26) { return "tabpanel" }
209
+ if (role == 27) { return "region" }
210
+ if (role == 28) { return "progressbar" }
211
+ if (role == 29) { return "menu" }
212
+ if (role == 30) { return "tooltip" }
213
+ if (role == 31) { return "alertdialog" }
214
+ if (role == 32) { return "slider" }
215
+ if (role == 33) { return "menubar" }
216
+ if (role == 34) { return "menuitemcheckbox" }
217
+ if (role == 35) { return "menuitemradio" }
218
+ if (role == 36) { return "combobox" }
219
+ if (role == 37) { return "listbox" }
220
+ if (role == 38) { return "option" }
221
+ return "none"
222
+ }
223
+ }
224
+
225
+ ; Tri-states. The default is "not applicable" rather than "false", because
226
+ ; `aria-checked="false"` on something that is not checkable makes a reader
227
+ ; announce a checkbox that does not exist.
228
+ class EVGA11yTri {
229
+ sfn notApplicable:int () { return 0 }
230
+ sfn no:int () { return 1 }
231
+ sfn yes:int () { return 2 }
232
+ sfn mixed:int () { return 3 }
233
+ }
234
+
235
+ class EVGA11yLive {
236
+ sfn off:int () { return 0 }
237
+ sfn polite:int () { return 1 }
238
+ sfn assertive:int () { return 2 }
239
+ }
240
+
241
+ class EVGA11yNode {
242
+ ; STABLE across frames, and derived from IDENTITY rather than paint order:
243
+ ; "win:7/ctrl:3", "sheet:Q3/cell:B7", "el:#total". An id that changes every
244
+ ; frame makes the reader lose its cursor, resets focus and flickers the
245
+ ; braille display — and everything still looks correct on screen, which is
246
+ ; why this is the most common way a first implementation fails.
247
+ def id:string ""
248
+ def parentId:string ""
249
+ def role:int 0
250
+ ; What gets spoken. A focusable node without one is a lint failure.
251
+ def name:string ""
252
+ ; Spoken after a pause, or on request: the formula behind a cell, the note
253
+ ; attached to it, the shortcut on a button.
254
+ def description:string ""
255
+ ; The id of the node whose text IS the description, when the sentence is
256
+ ; on screen as its own node (a form field's error message). Serialised as
257
+ ; `describedby`; the DOM mirror turns it into `aria-describedby`.
258
+ def describedBy:string ""
259
+ ; `aria-controls` / `aria-labelledby`: ids of other nodes. A tab controls
260
+ ; its panel and the panel is labelled by its tab.
261
+ def controls:string ""
262
+ def labelledBy:string ""
263
+ ; What a reader says INSTEAD of the role: "sortable" on a draggable row.
264
+ ; Every platform has it — aria-roledescription, UIA LocalizedControlType,
265
+ ; AT-SPI's localized role name — because some controls are a known role
266
+ ; doing an unfamiliar job, and the role name alone then says nothing useful.
267
+ def roleDescription:string ""
268
+ ; `aria-haspopup`. What a reader is told about a trigger that opens
269
+ ; something — a select's closed box is a word in a rectangle without it.
270
+ def hasPopup:string ""
271
+ ; A field's text, a cell's value — separate from the name, which is the
272
+ ; label that does not change when the content does.
273
+ def value:string ""
274
+
275
+ def x:int 0
276
+ def y:int 0
277
+ def w:int 0
278
+ def h:int 0
279
+
280
+ def focusable:boolean false
281
+ def focused:boolean false
282
+ def disabled:boolean false
283
+ def selected:boolean false
284
+ ; `aria-required`, `aria-invalid` and `aria-readonly`, as the DOM spells
285
+ ; them: "true", "false", or "" for a node that says nothing.
286
+ ;
287
+ ; STRINGS, and `readOnly` used to be a boolean here. That was the weaker
288
+ ; spelling and it could not express the distinction the `input_required`
289
+ ; spec exists to record: absent and "false" are different claims. The
290
+ ; controller path has always used three states; this one had one and a
291
+ ; half, and no `required` or `invalid` at all — so a demo-built form drew
292
+ ; a red ring and an error sentence for a person and told a reader nothing.
293
+ def required:string ""
294
+ def invalid:string ""
295
+ def readOnly:string ""
296
+ ; A dialog that owns the screen while it is up. Every platform has this —
297
+ ; aria-modal, UIA IsModal, AT-SPI MODAL, AccessKit Modal — and a host uses
298
+ ; it to hide everything the user cannot currently reach, which is the
299
+ ; difference between "a dialog is open" and "there are two applications".
300
+ def modal:boolean false
301
+ def checked:int 0
302
+ ; `aria-pressed`, tri-state like `checked`. A toggle button that is ON —
303
+ ; a carried sortable row, a show/hide switch beside a password — and NOT
304
+ ; the same claim as `checked`: a pressed button is a button held down, a
305
+ ; checked one is a box with a tick in it, and a reader says different
306
+ ; words for each.
307
+ def pressed:int 0
308
+ def expanded:int 0
309
+ ; `aria-sort` on a column header: 0 none, 1 ascending, 2 descending.
310
+ ;
311
+ ; It needs its own field for the same reason `valuenow` did. Sorting a
312
+ ; table changes which rows are where and nothing else about any node — the
313
+ ; header keeps its name, its role and its position — so without this the
314
+ ; one thing the control does is the one thing the trace cannot see.
315
+ def sorted:int 0
316
+ ; `aria-orientation`, verbatim: "horizontal", "vertical", or "" for none.
317
+ ; A string rather than a code because there are exactly two values and a
318
+ ; reader says them; the tri-state codes exist where a THIRD state (mixed,
319
+ ; not-applicable) has to be told apart from false.
320
+ def orientation:string ""
321
+ ; `aria-current`, verbatim: "page" on a breadcrumb's last crumb.
322
+ def current:string ""
323
+
324
+ ; A number the platform announces: where a slider sits, how far a progress
325
+ ; bar has got. `hasValueNow` is not decoration — a progress bar with a range
326
+ ; and NO position is how every platform spells "busy, length unknown", and
327
+ ; a reader that is handed 0 instead says the work has not started.
328
+ def hasValueNow:boolean false
329
+ def valueNow:int 0
330
+ def hasValueRange:boolean false
331
+ def valueMin:int 0
332
+ def valueMax:int 0
333
+
334
+ ; Position in a set the tree does not fully contain — the whole point of
335
+ ; virtualization. A 10,000 row sheet emits the ~40 rows on screen; "row
336
+ ; 4,120 of 10,000" comes from these and nothing else. Lying here makes the
337
+ ; reader announce positions that do not exist.
338
+ def rowIndex:int 0
339
+ def colIndex:int 0
340
+ def rowCount:int 0
341
+ def colCount:int 0
342
+ def posInSet:int 0
343
+ def setSize:int 0
344
+ ; How deep a `treeitem` sits, counting from 1. A flat list of rows whose
345
+ ; nesting lives only in this number is what headless-tree publishes and
346
+ ; what a reader announces as "level 3" — WITHOUT it a tree is announced as
347
+ ; a flat list of siblings, which is a different structure entirely and not
348
+ ; the one on screen. 0 means the node is not in a levelled structure.
349
+ def level:int 0
350
+
351
+ ; Text fields: caret and selection as offsets into `value`.
352
+ def caret:int 0
353
+ def selStart:int 0
354
+ def selEnd:int 0
355
+
356
+ ; What the node can be asked to DO. A platform adapter reports these as
357
+ ; available actions and routes them back into the app; the browser mirror
358
+ ; uses them to decide what kind of element to render. Booleans rather than a
359
+ ; bitmask because Ranger has no bitwise operators, and because the JSON that
360
+ ; crosses the seam is then readable by a person debugging it.
361
+ ;
362
+ ; `focusable` is the focus action; these are the rest.
363
+ def actActivate:boolean false
364
+ def actSetValue:boolean false
365
+ def actExpand:boolean false
366
+ def actScrollTo:boolean false
367
+ def live:int 0
368
+
369
+ Constructor () {
370
+ }
371
+ }
372
+
373
+ class EVGA11yTree {
374
+ def nodes:[EVGA11yNode]
375
+ def rootId:string ""
376
+ ; The app's focus, never the host's. One owner, or the two chase each other
377
+ ; forever: the mirror focuses a node, the browser reports focusin, the app
378
+ ; moves its focus, the mirror focuses a node…
379
+ def focusId:string ""
380
+ ; The same counter the scene carries, so a host can skip a rebuild it has
381
+ ; already done — a platform accessibility API wants events, not a fresh
382
+ ; tree sixty times a second.
383
+ def generation:int 0
384
+ ; What the BUILDER could not represent, in its own words. A builder walks
385
+ ; a source it does not fully control — an element tree, a row list — and
386
+ ; anything it cannot map lands here rather than being dropped in silence.
387
+ ; `lint()` reports them first.
388
+ def notes:[string]
389
+
390
+ Constructor () {
391
+ def n:[string]
392
+ notes = n
393
+ }
394
+
395
+ fn count:int () {
396
+ return (array_length nodes)
397
+ }
398
+
399
+ fn at:EVGA11yNode (i:int) {
400
+ if (i < 0) {
401
+ return (new EVGA11yNode)
402
+ }
403
+ if (i >= (array_length nodes)) {
404
+ return (new EVGA11yNode)
405
+ }
406
+ return (itemAt nodes i)
407
+ }
408
+
409
+ ; Create a node and hang it under `parentId`. The first node added with an
410
+ ; empty parent becomes the root.
411
+ fn node:EVGA11yNode (id:string parentId:string role:int) {
412
+ def n:EVGA11yNode (new EVGA11yNode)
413
+ n.id = id
414
+ n.parentId = parentId
415
+ n.role = role
416
+ push nodes n
417
+ if ((strlen rootId) == 0) {
418
+ if ((strlen parentId) == 0) {
419
+ rootId = id
420
+ }
421
+ }
422
+ return n
423
+ }
424
+
425
+ ; --- the shapes that come up over and over --------------------------------
426
+
427
+ fn group:EVGA11yNode (id:string parentId:string name:string) {
428
+ def n:EVGA11yNode (this.node(id parentId (EVGA11yRole.group())))
429
+ n.name = name
430
+ return n
431
+ }
432
+
433
+ fn label:EVGA11yNode (id:string parentId:string text:string) {
434
+ def n:EVGA11yNode (this.node(id parentId (EVGA11yRole.text())))
435
+ n.name = text
436
+ return n
437
+ }
438
+
439
+ fn button:EVGA11yNode (id:string parentId:string name:string) {
440
+ def n:EVGA11yNode (this.node(id parentId (EVGA11yRole.button())))
441
+ n.name = name
442
+ n.focusable = true
443
+ n.actActivate = true
444
+ return n
445
+ }
446
+
447
+ fn field:EVGA11yNode (id:string parentId:string name:string value:string) {
448
+ def n:EVGA11yNode (this.node(id parentId (EVGA11yRole.textField())))
449
+ n.name = name
450
+ n.value = value
451
+ n.focusable = true
452
+ n.actSetValue = true
453
+ return n
454
+ }
455
+
456
+ ; A region whose changes are announced without the user going to look —
457
+ ; "Recalculated 1,200 cells", "Sorted by Region". Polite by default:
458
+ ; assertive interrupts whatever is being read, which is for errors only.
459
+ fn statusRegion:EVGA11yNode (id:string parentId:string text:string) {
460
+ def n:EVGA11yNode (this.node(id parentId (EVGA11yRole.status())))
461
+ n.value = text
462
+ n.live = (EVGA11yLive.polite())
463
+ return n
464
+ }
465
+
466
+ fn place:void (n:EVGA11yNode nx:int ny:int nw:int nh:int) {
467
+ n.x = nx
468
+ n.y = ny
469
+ n.w = nw
470
+ n.h = nh
471
+ }
472
+
473
+ ; --- lookup ---------------------------------------------------------------
474
+
475
+ fn find:EVGA11yNode (id:string) {
476
+ def i:int 0
477
+ while (i < (array_length nodes)) {
478
+ def n:EVGA11yNode (itemAt nodes i)
479
+ if (n.id == id) {
480
+ return n
481
+ }
482
+ i = (i + 1)
483
+ }
484
+ return (new EVGA11yNode)
485
+ }
486
+
487
+ fn has:boolean (id:string) {
488
+ def n:EVGA11yNode (this.find(id))
489
+ return ((strlen n.id) > 0)
490
+ }
491
+
492
+ ; Mark one node focused and clear the rest. Focus is a property of the
493
+ ; tree, not of a node, and letting a caller set the flag by hand is how a
494
+ ; tree ends up with two focused nodes and a reader with none.
495
+ fn setFocus:void (id:string) {
496
+ focusId = id
497
+ def i:int 0
498
+ while (i < (array_length nodes)) {
499
+ def n:EVGA11yNode (itemAt nodes i)
500
+ n.focused = (n.id == id)
501
+ i = (i + 1)
502
+ }
503
+ }
504
+
505
+ ; --- serialization --------------------------------------------------------
506
+ ; Short keys and omitted defaults, because this crosses the same seam the
507
+ ; display list does and a grid emits a few hundred nodes per frame.
508
+
509
+ fn toJson:string () {
510
+ def out:string "{\"root\":"
511
+ out = (out + (EVGA11yTree.jsonString(rootId)))
512
+ out = (out + ",\"focus\":" + (EVGA11yTree.jsonString(focusId)))
513
+ out = (out + ",\"gen\":" + (to_string generation))
514
+ out = (out + ",\"nodes\":[")
515
+ def i:int 0
516
+ while (i < (array_length nodes)) {
517
+ def n:EVGA11yNode (itemAt nodes i)
518
+ if (i > 0) {
519
+ out = (out + ",")
520
+ }
521
+ out = (out + (this.nodeJson(n)))
522
+ i = (i + 1)
523
+ }
524
+ return (out + "]}")
525
+ }
526
+
527
+ fn nodeJson:string (n:EVGA11yNode) {
528
+ def out:string "{\"id\":"
529
+ out = (out + (EVGA11yTree.jsonString(n.id)))
530
+ if ((strlen n.parentId) > 0) {
531
+ out = (out + ",\"p\":" + (EVGA11yTree.jsonString(n.parentId)))
532
+ }
533
+ out = (out + ",\"role\":" + (EVGA11yTree.jsonString((EVGA11yRole.ariaName(n.role)))))
534
+ if ((strlen n.name) > 0) {
535
+ out = (out + ",\"name\":" + (EVGA11yTree.jsonString(n.name)))
536
+ }
537
+ if ((strlen n.value) > 0) {
538
+ out = (out + ",\"value\":" + (EVGA11yTree.jsonString(n.value)))
539
+ }
540
+ if ((strlen n.hasPopup) > 0) {
541
+ out = (out + ",\"haspopup\":" + (EVGA11yTree.jsonString(n.hasPopup)))
542
+ }
543
+ if ((strlen n.description) > 0) {
544
+ out = (out + ",\"desc\":" + (EVGA11yTree.jsonString(n.description)))
545
+ }
546
+ if ((strlen n.describedBy) > 0) {
547
+ out = (out + ",\"describedby\":" + (EVGA11yTree.jsonString(n.describedBy)))
548
+ }
549
+ if ((strlen n.controls) > 0) {
550
+ out = (out + ",\"controls\":" + (EVGA11yTree.jsonString(n.controls)))
551
+ }
552
+ if ((strlen n.labelledBy) > 0) {
553
+ out = (out + ",\"labelledby\":" + (EVGA11yTree.jsonString(n.labelledBy)))
554
+ }
555
+ if n.hasValueNow {
556
+ out = (out + ",\"now\":" + (to_string n.valueNow))
557
+ }
558
+ if n.hasValueRange {
559
+ out = (out + ",\"min\":" + (to_string n.valueMin))
560
+ out = (out + ",\"max\":" + (to_string n.valueMax))
561
+ }
562
+ out = (out + ",\"b\":[" + (to_string n.x) + "," + (to_string n.y))
563
+ out = (out + "," + (to_string n.w) + "," + (to_string n.h) + "]")
564
+ if n.focusable {
565
+ out = (out + ",\"focusable\":true")
566
+ }
567
+ if n.focused {
568
+ out = (out + ",\"focused\":true")
569
+ }
570
+ if n.disabled {
571
+ out = (out + ",\"disabled\":true")
572
+ }
573
+ if n.selected {
574
+ out = (out + ",\"selected\":true")
575
+ }
576
+ if ((strlen n.required) > 0) {
577
+ out = (out + ",\"required\":" + (EVGA11yTree.jsonString(n.required)))
578
+ }
579
+ if ((strlen n.invalid) > 0) {
580
+ out = (out + ",\"invalid\":" + (EVGA11yTree.jsonString(n.invalid)))
581
+ }
582
+ if ((strlen n.readOnly) > 0) {
583
+ out = (out + ",\"readonly\":" + (EVGA11yTree.jsonString(n.readOnly)))
584
+ }
585
+ if n.modal {
586
+ out = (out + ",\"modal\":true")
587
+ }
588
+ if (n.checked != 0) {
589
+ out = (out + ",\"checked\":" + (to_string n.checked))
590
+ }
591
+
592
+ if (n.pressed != 0) {
593
+ out = (out + ",\"pressed\":" + (to_string n.pressed))
594
+ }
595
+ if ((strlen n.orientation) > 0) {
596
+ out = (out + ",\"orientation\":\"" + n.orientation + "\"")
597
+ }
598
+ if ((strlen n.current) > 0) {
599
+ out = (out + ",\"current\":\"" + n.current + "\"")
600
+ }
601
+ if (n.sorted != 0) {
602
+ out = (out + ",\"sorted\":" + (to_string n.sorted))
603
+ }
604
+ if (n.expanded != 0) {
605
+ out = (out + ",\"expanded\":" + (to_string n.expanded))
606
+ }
607
+ if (n.rowIndex != 0) {
608
+ out = (out + ",\"row\":" + (to_string n.rowIndex))
609
+ }
610
+ if (n.colIndex != 0) {
611
+ out = (out + ",\"col\":" + (to_string n.colIndex))
612
+ }
613
+ if (n.rowCount != 0) {
614
+ out = (out + ",\"rows\":" + (to_string n.rowCount))
615
+ }
616
+ if (n.colCount != 0) {
617
+ out = (out + ",\"cols\":" + (to_string n.colCount))
618
+ }
619
+ if ((strlen n.roleDescription) > 0) {
620
+ out = (out + ",\"roledesc\":" + (EVGA11yTree.jsonString(n.roleDescription)))
621
+ }
622
+ if (n.posInSet != 0) {
623
+ out = (out + ",\"pos\":" + (to_string n.posInSet))
624
+ }
625
+ if (n.level != 0) {
626
+ out = (out + ",\"level\":" + (to_string n.level))
627
+ }
628
+ if (n.setSize != 0) {
629
+ out = (out + ",\"size\":" + (to_string n.setSize))
630
+ }
631
+ if (n.caret != 0) {
632
+ out = (out + ",\"caret\":" + (to_string n.caret))
633
+ }
634
+ if (n.selStart != n.selEnd) {
635
+ out = (out + ",\"sel\":[" + (to_string n.selStart) + "," + (to_string n.selEnd) + "]")
636
+ }
637
+ if n.actActivate {
638
+ out = (out + ",\"activate\":true")
639
+ }
640
+ if n.actSetValue {
641
+ out = (out + ",\"setValue\":true")
642
+ }
643
+ if n.actExpand {
644
+ out = (out + ",\"expandable\":true")
645
+ }
646
+ if n.actScrollTo {
647
+ out = (out + ",\"scrollTo\":true")
648
+ }
649
+ if (n.live != 0) {
650
+ out = (out + ",\"live\":" + (to_string n.live))
651
+ }
652
+ return (out + "}")
653
+ }
654
+
655
+ ; --- the golden dump ------------------------------------------------------
656
+ ; The tree is text, which makes accessibility unusually easy to regression
657
+ ; test: check this in beside the display-list dumps and a refactor that
658
+ ; silently drops a name shows up as a diff. No host and no screen reader
659
+ ; are involved.
660
+
661
+ fn dump:string () {
662
+ return (this.dumpFrom(rootId 0))
663
+ }
664
+
665
+ fn dumpFrom:string (id:string depth:int) {
666
+ def n:EVGA11yNode (this.find(id))
667
+ if ((strlen n.id) == 0) {
668
+ return ""
669
+ }
670
+ def out:string (EVGA11yTree.indent(depth))
671
+ out = (out + (EVGA11yRole.ariaName(n.role)))
672
+ out = (out + " #" + n.id)
673
+ if ((strlen n.name) > 0) {
674
+ out = (out + " \"" + n.name + "\"")
675
+ }
676
+ if ((strlen n.value) > 0) {
677
+ out = (out + " =" + n.value)
678
+ }
679
+ def st:string (this.stateText(n))
680
+ if ((strlen st) > 0) {
681
+ out = (out + " [" + st + "]")
682
+ }
683
+ out = (out + " (" + (to_string n.x) + "," + (to_string n.y))
684
+ out = (out + " " + (to_string n.w) + "x" + (to_string n.h) + ")\n")
685
+ def i:int 0
686
+ while (i < (array_length nodes)) {
687
+ def c:EVGA11yNode (itemAt nodes i)
688
+ if (c.parentId == id) {
689
+ def nd:int (depth + 1)
690
+ out = (out + (this.dumpFrom(c.id nd)))
691
+ }
692
+ i = (i + 1)
693
+ }
694
+ return out
695
+ }
696
+
697
+ fn stateText:string (n:EVGA11yNode) {
698
+ def out:string ""
699
+ if n.focused {
700
+ out = (EVGA11yTree.addWord(out "focused"))
701
+ }
702
+ if n.focusable {
703
+ if (n.focused == false) {
704
+ out = (EVGA11yTree.addWord(out "focusable"))
705
+ }
706
+ }
707
+ if n.disabled {
708
+ out = (EVGA11yTree.addWord(out "disabled"))
709
+ }
710
+ if n.selected {
711
+ out = (EVGA11yTree.addWord(out "selected"))
712
+ }
713
+ ; The words a reader hears. Only a "true" is spoken: "not required" is
714
+ ; not something anybody says out loud, and `aria-required="false"` is
715
+ ; still a claim worth carrying in the tree even though it is silent.
716
+ if (n.required == "true") {
717
+ out = (EVGA11yTree.addWord(out "required"))
718
+ }
719
+ if (n.invalid == "true") {
720
+ out = (EVGA11yTree.addWord(out "invalid"))
721
+ }
722
+ if (n.readOnly == "true") {
723
+ out = (EVGA11yTree.addWord(out "readonly"))
724
+ }
725
+ if n.modal {
726
+ out = (EVGA11yTree.addWord(out "modal"))
727
+ }
728
+ if (n.checked == 2) {
729
+ out = (EVGA11yTree.addWord(out "checked"))
730
+ }
731
+ if (n.checked == 3) {
732
+ out = (EVGA11yTree.addWord(out "mixed"))
733
+ }
734
+ if (n.live == 1) {
735
+ out = (EVGA11yTree.addWord(out "live"))
736
+ }
737
+ if (n.live == 2) {
738
+ out = (EVGA11yTree.addWord(out "live!"))
739
+ }
740
+ if (n.rowIndex > 0) {
741
+ out = (EVGA11yTree.addWord(out ("r" + (to_string n.rowIndex))))
742
+ }
743
+ if (n.colIndex > 0) {
744
+ out = (EVGA11yTree.addWord(out ("c" + (to_string n.colIndex))))
745
+ }
746
+ if (n.rowCount > 0) {
747
+ out = (EVGA11yTree.addWord(out ("of " + (to_string n.rowCount) + " rows")))
748
+ }
749
+ return out
750
+ }
751
+
752
+ ; --- the lints ------------------------------------------------------------
753
+ ; Failures a sighted developer cannot see. These belong in the test suite,
754
+ ; because "the button has no name" is invisible on screen and total for
755
+ ; someone using a reader.
756
+
757
+ fn lint:[string] () {
758
+ def problems:[string]
759
+ ; Whatever the BUILDER could not represent, reported before anything
760
+ ; this tree can see for itself. A node that was never made breaks no
761
+ ; rule about the nodes that were, so without this the failure mode of
762
+ ; an unknown role is a clean audit over an empty tree — which is what
763
+ ; the tree demo got: eighteen rows on screen, one node, PASS.
764
+ def i0:int 0
765
+ while (i0 < (array_length notes)) {
766
+ push problems (itemAt notes i0)
767
+ i0 = (i0 + 1)
768
+ }
769
+ if ((strlen rootId) == 0) {
770
+ push problems "tree has no root"
771
+ }
772
+ def i:int 0
773
+ while (i < (array_length nodes)) {
774
+ def n:EVGA11yNode (itemAt nodes i)
775
+ def dup:int 0
776
+ def j:int 0
777
+ while (j < (array_length nodes)) {
778
+ def m:EVGA11yNode (itemAt nodes j)
779
+ if (m.id == n.id) {
780
+ dup = (dup + 1)
781
+ }
782
+ j = (j + 1)
783
+ }
784
+ if (dup > 1) {
785
+ if (i == 0) {
786
+ push problems ("duplicate id: " + n.id)
787
+ } {
788
+ def prevSeen:boolean false
789
+ def k:int 0
790
+ while (k < i) {
791
+ def p:EVGA11yNode (itemAt nodes k)
792
+ if (p.id == n.id) {
793
+ prevSeen = true
794
+ }
795
+ k = (k + 1)
796
+ }
797
+ if (prevSeen == false) {
798
+ push problems ("duplicate id: " + n.id)
799
+ }
800
+ }
801
+ }
802
+ if ((strlen n.id) == 0) {
803
+ push problems "node with an empty id"
804
+ }
805
+ if ((strlen n.parentId) > 0) {
806
+ if ((this.has(n.parentId)) == false) {
807
+ push problems (n.id + ": parent " + n.parentId + " is not in the tree")
808
+ }
809
+ } {
810
+ if (n.id != rootId) {
811
+ push problems (n.id + ": no parent, and it is not the root")
812
+ }
813
+ }
814
+ if n.focusable {
815
+ def named:boolean ((strlen n.name) > 0)
816
+ if (named == false) {
817
+ push problems (n.id + ": focusable with no accessible name")
818
+ }
819
+ if (n.w <= 0) {
820
+ push problems (n.id + ": focusable with no width")
821
+ }
822
+ if (n.h <= 0) {
823
+ push problems (n.id + ": focusable with no height")
824
+ }
825
+ }
826
+ if (n.role == 0) {
827
+ if ((strlen n.name) > 0) {
828
+ push problems (n.id + ": named, but the role is decoration")
829
+ }
830
+ }
831
+ i = (i + 1)
832
+ }
833
+ if ((strlen focusId) > 0) {
834
+ if ((this.has(focusId)) == false) {
835
+ push problems ("focus points at " + focusId + ", which is not in the tree")
836
+ }
837
+ }
838
+ return problems
839
+ }
840
+
841
+ ; --- helpers --------------------------------------------------------------
842
+
843
+ sfn addWord:string (acc:string word:string) {
844
+ if ((strlen acc) == 0) {
845
+ return word
846
+ }
847
+ return (acc + " " + word)
848
+ }
849
+
850
+ sfn indent:string (depth:int) {
851
+ def out:string ""
852
+ def i:int 0
853
+ while (i < depth) {
854
+ out = (out + " ")
855
+ i = (i + 1)
856
+ }
857
+ return out
858
+ }
859
+
860
+ sfn jsonString:string (v:string) {
861
+ def out:string "\""
862
+ def i:int 0
863
+ while (i < (strlen v)) {
864
+ def ch:int (charAt v i)
865
+ if (ch == 34) {
866
+ out = (out + "\\\"")
867
+ } {
868
+ if (ch == 92) {
869
+ out = (out + "\\\\")
870
+ } {
871
+ if (ch == 10) {
872
+ out = (out + "\\n")
873
+ } {
874
+ if (ch == 13) {
875
+ out = (out + "\\r")
876
+ } {
877
+ if (ch == 9) {
878
+ out = (out + "\\t")
879
+ } {
880
+ if (ch < 32) {
881
+ out = (out + " ")
882
+ } {
883
+ out = (out + (strfromcode ch))
884
+ }
885
+ }
886
+ }
887
+ }
888
+ }
889
+ }
890
+ i = (i + 1)
891
+ }
892
+ return (out + "\"")
893
+ }
894
+ }