lacci 0.2.1
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.
- checksums.yaml +7 -0
- data/.rubocop.yml +85 -0
- data/.ruby-version +1 -0
- data/.vscode/extensions.json +5 -0
- data/.yardopts +12 -0
- data/CHANGELOG.md +19 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/CONTRIBUTING.md +20 -0
- data/Gemfile +24 -0
- data/LICENSE.txt +21 -0
- data/README.md +178 -0
- data/Rakefile +15 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/dev.yml +7 -0
- data/docs/static/PKGBUILD +47 -0
- data/docs/static/Shoes.icns +0 -0
- data/docs/static/avatar.png +0 -0
- data/docs/static/code_highlighter.js +188 -0
- data/docs/static/code_highlighter_ruby.js +26 -0
- data/docs/static/icon-debug.png +0 -0
- data/docs/static/icon-error.png +0 -0
- data/docs/static/icon-info.png +0 -0
- data/docs/static/icon-warn.png +0 -0
- data/docs/static/listbox_button1.png +0 -0
- data/docs/static/listbox_button2.png +0 -0
- data/docs/static/man-app.png +0 -0
- data/docs/static/man-builds.png +0 -0
- data/docs/static/man-builds1.png +0 -0
- data/docs/static/man-editor-notepad.png +0 -0
- data/docs/static/man-editor-osx.png +0 -0
- data/docs/static/man-ele-background.png +0 -0
- data/docs/static/man-ele-border.png +0 -0
- data/docs/static/man-ele-button.png +0 -0
- data/docs/static/man-ele-check.png +0 -0
- data/docs/static/man-ele-editbox.png +0 -0
- data/docs/static/man-ele-editline.png +0 -0
- data/docs/static/man-ele-image.png +0 -0
- data/docs/static/man-ele-listbox.png +0 -0
- data/docs/static/man-ele-progress.png +0 -0
- data/docs/static/man-ele-radio.png +0 -0
- data/docs/static/man-ele-shape.png +0 -0
- data/docs/static/man-ele-textblock.png +0 -0
- data/docs/static/man-ele-video.png +0 -0
- data/docs/static/man-intro-dmg.png +0 -0
- data/docs/static/man-intro-exe.png +0 -0
- data/docs/static/man-look-tiger.png +0 -0
- data/docs/static/man-look-ubuntu.png +0 -0
- data/docs/static/man-look-vista.png +0 -0
- data/docs/static/man-run-osx.png +0 -0
- data/docs/static/man-run-vista.png +0 -0
- data/docs/static/man-run-xp.png +0 -0
- data/docs/static/man-shot1.png +0 -0
- data/docs/static/manual-en.txt +3529 -0
- data/docs/static/manual-ja.txt +2829 -0
- data/docs/static/manual.css +171 -0
- data/docs/static/manual.md +3528 -0
- data/docs/static/menu-corner1.png +0 -0
- data/docs/static/menu-corner2.png +0 -0
- data/docs/static/menu-gray.png +0 -0
- data/docs/static/menu-left.png +0 -0
- data/docs/static/menu-right.png +0 -0
- data/docs/static/menu-top.png +0 -0
- data/docs/static/shoes-dmg.jpg +0 -0
- data/docs/static/shoes-icon-blue.png +0 -0
- data/docs/static/shoes-icon.png +0 -0
- data/docs/static/shoes-manual-apps.gif +0 -0
- data/docs/static/shoes_main_window.png +0 -0
- data/docs/static/stripe.png +0 -0
- data/docs/static/stubs/blank.exe +0 -0
- data/docs/static/stubs/blank.hfz +0 -0
- data/docs/static/stubs/blank.run +375 -0
- data/docs/static/stubs/cocoa-install +0 -0
- data/docs/static/stubs/sh-install +49 -0
- data/docs/static/stubs/shoes-stub-inject.exe +0 -0
- data/docs/static/stubs/shoes-stub.exe +0 -0
- data/docs/static/tutor-back.png +0 -0
- data/docs/yard/catscradle.md +44 -0
- data/docs/yard/template/default/fulldoc/html/setup.rb +13 -0
- data/docs/yard/template/default/layout/html/setup.rb +9 -0
- data/examples/backdround_with_image.rb +16 -0
- data/examples/bloopsaphone/working/1901_by_Aanand_Prasad.rb +478 -0
- data/examples/bloopsaphone/working/b1.rb +64 -0
- data/examples/bloopsaphone/working/b2.rb +36 -0
- data/examples/bloopsaphone/working/bloops_test.rb +41 -0
- data/examples/bloopsaphone/working/bloopsaphone_theme_song_by_why.rb +31 -0
- data/examples/bloopsaphone/working/feepogram.rb +67 -0
- data/examples/bloopsaphone/working/simpsons_theme_song_by_why.rb +14 -0
- data/examples/bloopsaphone/working/tune_cheeky_drat.rb +210 -0
- data/examples/button.rb +10 -0
- data/examples/button_alert.rb +6 -0
- data/examples/button_go_away.rb +6 -0
- data/examples/button_with_position_and_size.rb +7 -0
- data/examples/coffee.rb +14 -0
- data/examples/download.rb +5 -0
- data/examples/download_and_show_image.rb +28 -0
- data/examples/edit_box.rb +8 -0
- data/examples/edit_line.rb +8 -0
- data/examples/fill.rb +25 -0
- data/examples/flow.rb +11 -0
- data/examples/fonts.rb +6 -0
- data/examples/gen.rb +140 -0
- data/examples/get_headers.rb +10 -0
- data/examples/hello_world.rb +3 -0
- data/examples/highlander.rb +87 -0
- data/examples/image/clickable_image.rb +5 -0
- data/examples/image/image.rb +3 -0
- data/examples/image/image_size.rb +7 -0
- data/examples/image/image_with_position_and_size.rb +3 -0
- data/examples/info.rb +9 -0
- data/examples/legacy/not_checked/expert/colours.rb +105 -0
- data/examples/legacy/not_checked/expert/curve-animation.rb +31 -0
- data/examples/legacy/not_checked/expert/curve-control-point.rb +51 -0
- data/examples/legacy/not_checked/expert/custom-list-box.rb +53 -0
- data/examples/legacy/not_checked/expert/definr.rb +23 -0
- data/examples/legacy/not_checked/expert/funnies.rb +56 -0
- data/examples/legacy/not_checked/expert/minesweeper.rb +267 -0
- data/examples/legacy/not_checked/expert/othello.rb +318 -0
- data/examples/legacy/not_checked/expert/pong.rb +62 -0
- data/examples/legacy/not_checked/expert/tankspank.rb +385 -0
- data/examples/legacy/not_checked/expert/tooltips.rb +45 -0
- data/examples/legacy/not_checked/expert/url.rb +37 -0
- data/examples/legacy/not_checked/expert/video-player.rb +256 -0
- data/examples/legacy/not_checked/good/_why-stories.rb +44 -0
- data/examples/legacy/not_checked/good/_why-stories.yaml +387 -0
- data/examples/legacy/not_checked/good/arc.rb +37 -0
- data/examples/legacy/not_checked/good/cardflip.rb +141 -0
- data/examples/legacy/not_checked/good/clock.rb +51 -0
- data/examples/legacy/not_checked/good/console.rb +21 -0
- data/examples/legacy/not_checked/good/follow.rb +26 -0
- data/examples/legacy/not_checked/good/image-rotate.rb +14 -0
- data/examples/legacy/not_checked/good/paris.svg +7236 -0
- data/examples/legacy/not_checked/good/path-animation.rb +46 -0
- data/examples/legacy/not_checked/good/plots.rb +100 -0
- data/examples/legacy/not_checked/good/reminder.rb +174 -0
- data/examples/legacy/not_checked/good/svgview.rb +113 -0
- data/examples/legacy/not_checked/good/vjot.rb +56 -0
- data/examples/legacy/not_checked/shoes-contrib/.gitignore +2 -0
- data/examples/legacy/not_checked/shoes-contrib/README.md +34 -0
- data/examples/legacy/not_checked/shoes-contrib/animation/animate-ovals.rb +21 -0
- data/examples/legacy/not_checked/shoes-contrib/animation/flowers.rb +59 -0
- data/examples/legacy/not_checked/shoes-contrib/animation/happy-trails.rb +31 -0
- data/examples/legacy/not_checked/shoes-contrib/animation/mice-satellites.rb +27 -0
- data/examples/legacy/not_checked/shoes-contrib/animation/oval-motion.rb +12 -0
- data/examples/legacy/not_checked/shoes-contrib/animation/pink-bubbles.rb +34 -0
- data/examples/legacy/not_checked/shoes-contrib/animation/pulsate.rb +18 -0
- data/examples/legacy/not_checked/shoes-contrib/animation/rotating-star.rb +18 -0
- data/examples/legacy/not_checked/shoes-contrib/app/download-and-save.rb +11 -0
- data/examples/legacy/not_checked/shoes-contrib/app/download.rb +10 -0
- data/examples/legacy/not_checked/shoes-contrib/app/get-google.rb +11 -0
- data/examples/legacy/not_checked/shoes-contrib/app/mouse-detection.rb +7 -0
- data/examples/legacy/not_checked/shoes-contrib/app/resizeable-false.rb +6 -0
- data/examples/legacy/not_checked/shoes-contrib/art/bubble-bullseye.rb +23 -0
- data/examples/legacy/not_checked/shoes-contrib/art/faded.rb +17 -0
- data/examples/legacy/not_checked/shoes-contrib/art/fill-oval.rb +5 -0
- data/examples/legacy/not_checked/shoes-contrib/art/mask.rb +15 -0
- data/examples/legacy/not_checked/shoes-contrib/art/oval-gradient.rb +6 -0
- data/examples/legacy/not_checked/shoes-contrib/art/star-gradient.rb +4 -0
- data/examples/legacy/not_checked/shoes-contrib/basic/basic-edit-box.rb +8 -0
- data/examples/legacy/not_checked/shoes-contrib/basic/basic-new-window.rb +8 -0
- data/examples/legacy/not_checked/shoes-contrib/basic/basic-oval-image.rb +8 -0
- data/examples/legacy/not_checked/shoes-contrib/basic/basic-oval-shape.rb +8 -0
- data/examples/legacy/not_checked/shoes-contrib/basic/basic-oval.rb +6 -0
- data/examples/legacy/not_checked/shoes-contrib/basic/class-book.rb +43 -0
- data/examples/legacy/not_checked/shoes-contrib/basic/class-book.yaml +387 -0
- data/examples/legacy/not_checked/shoes-contrib/basic/clock.rb +6 -0
- data/examples/legacy/not_checked/shoes-contrib/basic/edit-stack +14 -0
- data/examples/legacy/not_checked/shoes-contrib/basic/gradient-shoes.rb +7 -0
- data/examples/legacy/not_checked/shoes-contrib/basic/list_box-shape-report.rb +19 -0
- data/examples/legacy/not_checked/shoes-contrib/basic/rect-arrow.rb +7 -0
- data/examples/legacy/not_checked/shoes-contrib/basic/scribble.rb +8 -0
- data/examples/legacy/not_checked/shoes-contrib/basic/search.rb +32 -0
- data/examples/legacy/not_checked/shoes-contrib/basic/shoes-notes.rb +16 -0
- data/examples/legacy/not_checked/shoes-contrib/basic/url-shoes-subclassing.rb +24 -0
- data/examples/legacy/not_checked/shoes-contrib/browser.rb +21 -0
- data/examples/legacy/not_checked/shoes-contrib/elements/background-column.rb +3 -0
- data/examples/legacy/not_checked/shoes-contrib/elements/background.rb +4 -0
- data/examples/legacy/not_checked/shoes-contrib/elements/basic-fps.rb +6 -0
- data/examples/legacy/not_checked/shoes-contrib/elements/border-cat.rb +6 -0
- data/examples/legacy/not_checked/shoes-contrib/elements/button-block.rb +8 -0
- data/examples/legacy/not_checked/shoes-contrib/elements/check-mate.rb +15 -0
- data/examples/legacy/not_checked/shoes-contrib/elements/common-styles.rb +13 -0
- data/examples/legacy/not_checked/shoes-contrib/elements/displace-animation.rb +13 -0
- data/examples/legacy/not_checked/shoes-contrib/elements/edit_box-character-count.rb +7 -0
- data/examples/legacy/not_checked/shoes-contrib/elements/edit_line-character-count.rb +7 -0
- data/examples/legacy/not_checked/shoes-contrib/elements/image-icon.rb +3 -0
- data/examples/legacy/not_checked/shoes-contrib/elements/list_box-select-class.rb +13 -0
- data/examples/legacy/not_checked/shoes-contrib/elements/list_box.rb +6 -0
- data/examples/legacy/not_checked/shoes-contrib/elements/move-flow-animate.rb +13 -0
- data/examples/legacy/not_checked/shoes-contrib/elements/phat-button.rb +7 -0
- data/examples/legacy/not_checked/shoes-contrib/elements/popup.rb +7 -0
- data/examples/legacy/not_checked/shoes-contrib/elements/progress-bar.rb +10 -0
- data/examples/legacy/not_checked/shoes-contrib/elements/radio-dreamcast-favs.rb +17 -0
- data/examples/legacy/not_checked/shoes-contrib/elements/timer.rb +6 -0
- data/examples/legacy/not_checked/shoes-contrib/elements/width-introspec.rb +8 -0
- data/examples/legacy/not_checked/shoes-contrib/events/background-hover.rb +11 -0
- data/examples/legacy/not_checked/shoes-contrib/events/motion-detect.rb +9 -0
- data/examples/legacy/not_checked/shoes-contrib/events/pressed-key.rb +6 -0
- data/examples/legacy/not_checked/shoes-contrib/expert/expert-funnies.rb +51 -0
- data/examples/legacy/not_checked/shoes-contrib/expert/expert-irb.rb +112 -0
- data/examples/legacy/not_checked/shoes-contrib/expert/expert-othello.rb +319 -0
- data/examples/legacy/not_checked/shoes-contrib/good/good-arc.rb +37 -0
- data/examples/legacy/not_checked/shoes-contrib/good/good-clock.rb +51 -0
- data/examples/legacy/not_checked/shoes-contrib/good/good-follow.rb +26 -0
- data/examples/legacy/not_checked/shoes-contrib/good/good-reminder.rb +174 -0
- data/examples/legacy/not_checked/shoes-contrib/good/good-vjot.rb +56 -0
- data/examples/legacy/not_checked/shoes-contrib/kernel/ask.rb +4 -0
- data/examples/legacy/not_checked/shoes-contrib/kernel/ask_color.rb +4 -0
- data/examples/legacy/not_checked/shoes-contrib/kernel/ask_open_file.rb +4 -0
- data/examples/legacy/not_checked/shoes-contrib/kernel/ask_save_file.rb +7 -0
- data/examples/legacy/not_checked/shoes-contrib/kernel/confirm.rb +3 -0
- data/examples/legacy/not_checked/shoes-contrib/kernel/debug.rb +4 -0
- data/examples/legacy/not_checked/shoes-contrib/kernel/exit.rb +5 -0
- data/examples/legacy/not_checked/shoes-contrib/kernel/font.rb +5 -0
- data/examples/legacy/not_checked/shoes-contrib/kernel/fonts.rb +3 -0
- data/examples/legacy/not_checked/shoes-contrib/manipulation/append-slot.rb +9 -0
- data/examples/legacy/not_checked/shoes-contrib/manipulation/clear-slot.rb +6 -0
- data/examples/legacy/not_checked/shoes-contrib/manipulation/prepend-slot.rb +6 -0
- data/examples/legacy/not_checked/shoes-contrib/manipulation/roll.rb +17 -0
- data/examples/legacy/not_checked/shoes-contrib/position/gutter-margin.rb +5 -0
- data/examples/legacy/not_checked/shoes-contrib/position/stack-width.rb +3 -0
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-accordion.rb +75 -0
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-anim-shapes.rb +17 -0
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-anim-text.rb +13 -0
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-arc.rb +23 -0
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-bounce.rb +24 -0
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-calc.rb +70 -0
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-chipmunk.rb +26 -0
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-control-sizes.rb +24 -0
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-curve.rb +26 -0
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-dialogs.rb +29 -0
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-downloader.rb +27 -0
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-draw.rb +13 -0
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-editor.rb +28 -0
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-form.rb +28 -0
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-form.shy +0 -0
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-mask.rb +21 -0
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-menu.rb +31 -0
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-menu1.rb +35 -0
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-rubygems.rb +29 -0
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-slide.rb +45 -0
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-sphere.rb +28 -0
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-sqlite3.rb +13 -0
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-timer.rb +13 -0
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-video.rb +13 -0
- data/examples/legacy/not_checked/shoes-contrib/styles/alignment.rb +4 -0
- data/examples/legacy/not_checked/shoes-contrib/styles/attachment.rb +5 -0
- data/examples/legacy/not_checked/shoes-contrib/styles/emphasis.rb +3 -0
- data/examples/legacy/not_checked/shoes-contrib/styles/gradient-angle.rb +3 -0
- data/examples/legacy/not_checked/shoes-contrib/styles/para-methods.rb +5 -0
- data/examples/legacy/not_checked/shoes-contrib/styles/para-red-underlined.rb +3 -0
- data/examples/legacy/not_checked/shoes-contrib/styles/para-style-method.rb +4 -0
- data/examples/legacy/not_checked/shoes-dep-samples/expert-game-of-life.rb +243 -0
- data/examples/legacy/not_checked/shoes-dep-samples/expert-othello.rb +319 -0
- data/examples/legacy/not_checked/shoes-dep-samples/good-clock.rb +51 -0
- data/examples/legacy/not_checked/shoes-dep-samples/good-follow.rb +26 -0
- data/examples/legacy/not_checked/shoes-dep-samples/good-reminder.rb +174 -0
- data/examples/legacy/not_checked/shoes-dep-samples/good-vjot.rb +56 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-accordion.rb +75 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-anim-shapes.rb +17 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-anim-text.rb +13 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-arc.rb +23 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-bounce.rb +24 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-calc.rb +70 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-chipmunk.rb +26 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-control-sizes.rb +24 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-curve.rb +26 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-dialogs.rb +29 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-draw.rb +13 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-editor.rb +28 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-form.rb +28 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-form.shy +0 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-mask.rb +21 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-menu.rb +31 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-menu1.rb +35 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-rubygems.rb +29 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-slide.rb +45 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-sphere.rb +28 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-sqlite3.rb +13 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-timer.rb +13 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-video.rb +13 -0
- data/examples/legacy/not_checked/shoes3-tests/AnemicCinema1926marcelDuchampCut.mp4 +0 -0
- data/examples/legacy/not_checked/shoes3-tests/button/button.rb +53 -0
- data/examples/legacy/not_checked/shoes3-tests/cache/cache.rb +41 -0
- data/examples/legacy/not_checked/shoes3-tests/combo/combo.rb +24 -0
- data/examples/legacy/not_checked/shoes3-tests/curl/bare.rb +34 -0
- data/examples/legacy/not_checked/shoes3-tests/curl/downloader.rb +40 -0
- data/examples/legacy/not_checked/shoes3-tests/curl/m1.rb +11 -0
- data/examples/legacy/not_checked/shoes3-tests/curl/m2.rb +12 -0
- data/examples/legacy/not_checked/shoes3-tests/curl/m3.rb +11 -0
- data/examples/legacy/not_checked/shoes3-tests/curl/m4.rb +5 -0
- data/examples/legacy/not_checked/shoes3-tests/curl/typ.rb +30 -0
- data/examples/legacy/not_checked/shoes3-tests/cursor/c1.rb +21 -0
- data/examples/legacy/not_checked/shoes3-tests/decoration.rb +14 -0
- data/examples/legacy/not_checked/shoes3-tests/dialogs/ask.rb +6 -0
- data/examples/legacy/not_checked/shoes3-tests/dialogs/confirm.rb +6 -0
- data/examples/legacy/not_checked/shoes3-tests/editbox/editbox.rb +36 -0
- data/examples/legacy/not_checked/shoes3-tests/editline/editline.rb +27 -0
- data/examples/legacy/not_checked/shoes3-tests/events/button.rb +32 -0
- data/examples/legacy/not_checked/shoes3-tests/events/button.yaml +40 -0
- data/examples/legacy/not_checked/shoes3-tests/events/capture.rb +57 -0
- data/examples/legacy/not_checked/shoes3-tests/events/chipmunk.yaml +47 -0
- data/examples/legacy/not_checked/shoes3-tests/events/event0.rb +8 -0
- data/examples/legacy/not_checked/shoes3-tests/events/event1.rb +51 -0
- data/examples/legacy/not_checked/shoes3-tests/events/event1.yaml +26 -0
- data/examples/legacy/not_checked/shoes3-tests/events/event2.rb +46 -0
- data/examples/legacy/not_checked/shoes3-tests/events/event3.rb +44 -0
- data/examples/legacy/not_checked/shoes3-tests/events/event4.rb +65 -0
- data/examples/legacy/not_checked/shoes3-tests/events/event5.rb +17 -0
- data/examples/legacy/not_checked/shoes3-tests/events/event6.rb +51 -0
- data/examples/legacy/not_checked/shoes3-tests/events/replay.rb +60 -0
- data/examples/legacy/not_checked/shoes3-tests/gapp/fullscreen.rb +38 -0
- data/examples/legacy/not_checked/shoes3-tests/gapp/icon.rb +56 -0
- data/examples/legacy/not_checked/shoes3-tests/gapp/mon1.rb +46 -0
- data/examples/legacy/not_checked/shoes3-tests/gapp/settings1.rb +22 -0
- data/examples/legacy/not_checked/shoes3-tests/gapp/title1.rb +34 -0
- data/examples/legacy/not_checked/shoes3-tests/indian.m4a +0 -0
- data/examples/legacy/not_checked/shoes3-tests/menus/event.rb +33 -0
- data/examples/legacy/not_checked/shoes3-tests/menus/menu1.rb +8 -0
- data/examples/legacy/not_checked/shoes3-tests/menus/menu2.rb +25 -0
- data/examples/legacy/not_checked/shoes3-tests/menus/menu3.rb +103 -0
- data/examples/legacy/not_checked/shoes3-tests/menus/menu4.rb +41 -0
- data/examples/legacy/not_checked/shoes3-tests/menus/tests.txt +42 -0
- data/examples/legacy/not_checked/shoes3-tests/opacity.rb +13 -0
- data/examples/legacy/not_checked/shoes3-tests/plot/^IRX.R +5219 -0
- data/examples/legacy/not_checked/shoes3-tests/plot/^SPX +5219 -0
- data/examples/legacy/not_checked/shoes3-tests/plot/cstest.rb +51 -0
- data/examples/legacy/not_checked/shoes3-tests/plot/gr1.rb +73 -0
- data/examples/legacy/not_checked/shoes3-tests/plot/gr2.rb +40 -0
- data/examples/legacy/not_checked/shoes3-tests/plot/gr3.rb +47 -0
- data/examples/legacy/not_checked/shoes3-tests/plot/gr4.rb +35 -0
- data/examples/legacy/not_checked/shoes3-tests/plot/gr5.rb +31 -0
- data/examples/legacy/not_checked/shoes3-tests/plot/gr6.rb +35 -0
- data/examples/legacy/not_checked/shoes3-tests/plot/gr7.rb +37 -0
- data/examples/legacy/not_checked/shoes3-tests/plot/grcsv.rb +219 -0
- data/examples/legacy/not_checked/shoes3-tests/plot/manual.rb +40 -0
- data/examples/legacy/not_checked/shoes3-tests/progress/progress.rb +29 -0
- data/examples/legacy/not_checked/shoes3-tests/radio/clear.rb +20 -0
- data/examples/legacy/not_checked/shoes3-tests/radio/group.rb +18 -0
- data/examples/legacy/not_checked/shoes3-tests/radio/multiple.rb +32 -0
- data/examples/legacy/not_checked/shoes3-tests/radio/nogroup.rb +9 -0
- data/examples/legacy/not_checked/shoes3-tests/simpletest.svg +14 -0
- data/examples/legacy/not_checked/shoes3-tests/spinner.rb +15 -0
- data/examples/legacy/not_checked/shoes3-tests/svg.rb +208 -0
- data/examples/legacy/not_checked/shoes3-tests/switch/switch.rb +28 -0
- data/examples/legacy/not_checked/shoes3-tests/systray/back.rb +20 -0
- data/examples/legacy/not_checked/shoes3-tests/systray/note.rb +17 -0
- data/examples/legacy/not_checked/shoes3-tests/terminal/cursor.rb +27 -0
- data/examples/legacy/not_checked/shoes3-tests/terminal/ed.rb +65 -0
- data/examples/legacy/not_checked/shoes3-tests/terminal/el.rb +50 -0
- data/examples/legacy/not_checked/shoes3-tests/terminal/logattr.rb +31 -0
- data/examples/legacy/not_checked/shoes3-tests/tests_color.rb +138 -0
- data/examples/legacy/not_checked/shoes3-tests/tests_svg.rb +228 -0
- data/examples/legacy/not_checked/shoes3-tests/tests_video_vlc.rb +186 -0
- data/examples/legacy/not_checked/shoes3-tests/tooltips.rb +20 -0
- data/examples/legacy/not_checked/shoes3-tests/video_vlc.rb +242 -0
- data/examples/legacy/not_checked/shoes3-tests/wheel/wheel1.rb +21 -0
- data/examples/legacy/not_checked/simple/accordion.rb +81 -0
- data/examples/legacy/not_checked/simple/anim-shapes.rb +17 -0
- data/examples/legacy/not_checked/simple/anim-text.rb +13 -0
- data/examples/legacy/not_checked/simple/arc.rb +23 -0
- data/examples/legacy/not_checked/simple/bounce.rb +24 -0
- data/examples/legacy/not_checked/simple/chipmunk.rb +26 -0
- data/examples/legacy/not_checked/simple/control-sizes.rb +24 -0
- data/examples/legacy/not_checked/simple/curve-control-points.rb +24 -0
- data/examples/legacy/not_checked/simple/curve.rb +26 -0
- data/examples/legacy/not_checked/simple/dialogs.rb +29 -0
- data/examples/legacy/not_checked/simple/downloader.rb +40 -0
- data/examples/legacy/not_checked/simple/downloader1.rb +31 -0
- data/examples/legacy/not_checked/simple/draw.rb +13 -0
- data/examples/legacy/not_checked/simple/editor.rb +28 -0
- data/examples/legacy/not_checked/simple/form.rb +30 -0
- data/examples/legacy/not_checked/simple/image.rb +21 -0
- data/examples/legacy/not_checked/simple/info.rb +78 -0
- data/examples/legacy/not_checked/simple/mask.rb +21 -0
- data/examples/legacy/not_checked/simple/mask2.rb +27 -0
- data/examples/legacy/not_checked/simple/menu.rb +31 -0
- data/examples/legacy/not_checked/simple/menu1.rb +35 -0
- data/examples/legacy/not_checked/simple/slide.rb +45 -0
- data/examples/legacy/not_checked/simple/sphere.rb +28 -0
- data/examples/legacy/not_checked/superleg.rb +244 -0
- data/examples/legacy/working/shoes-contrib/basic/a-poem.rb +24 -0
- data/examples/legacy/working/shoes-contrib/basic/basic-blocks-instances.rb +7 -0
- data/examples/legacy/working/shoes-contrib/basic/basic-blocks.rb +7 -0
- data/examples/legacy/working/shoes-contrib/basic/basic-slot.rb +8 -0
- data/examples/legacy/working/shoes-contrib/basic/intro.rb +20 -0
- data/examples/legacy/working/shoes-contrib/basic/two-column.rb +12 -0
- data/examples/legacy/working/shoes-contrib/elements/edit_box.rb +6 -0
- data/examples/legacy/working/shoes-contrib/kernel/alert.rb +3 -0
- data/examples/legacy/working/simple/calc.rb +71 -0
- data/examples/legacy/working/simple/sqlite3.rb +27 -0
- data/examples/legacy/working/simple/timer.rb +13 -0
- data/examples/line.rb +5 -0
- data/examples/link.rb +20 -0
- data/examples/list_box.rb +8 -0
- data/examples/local_fonts.rb +4 -0
- data/examples/local_images.rb +4 -0
- data/examples/motion_events.rb +20 -0
- data/examples/para/collection_of_arguments.rb +9 -0
- data/examples/para/hello_world.rb +5 -0
- data/examples/para/hide_and_show.rb +9 -0
- data/examples/para/rainbow.rb +11 -0
- data/examples/para/rainbow_2.rb +11 -0
- data/examples/para/sizes.rb +11 -0
- data/examples/para/sizes_2.rb +12 -0
- data/examples/para/strong.rb +3 -0
- data/examples/para_text_widgets.rb +4 -0
- data/examples/parse_xl_funnies.rb +58 -0
- data/examples/pirate.png +0 -0
- data/examples/raw_flow.rb +11 -0
- data/examples/ruby_racer.rb +50 -0
- data/examples/selfitude.rb +18 -0
- data/examples/shapes/arc.rb +8 -0
- data/examples/shapes/shapes.rb +8 -0
- data/examples/shapes/shapes_fill.rb +10 -0
- data/examples/shapes/star.rb +5 -0
- data/examples/shoes_school.rb +66 -0
- data/examples/shoes_splorer.rb +150 -0
- data/examples/simple_slides.rb +73 -0
- data/examples/skip_ci/parrot.rb +22 -0
- data/examples/skip_ci/say.rb +20 -0
- data/examples/sleepless.rb +26 -0
- data/examples/slots.rb +10 -0
- data/examples/spacing.rb +18 -0
- data/examples/span.rb +6 -0
- data/examples/stack/background.rb +35 -0
- data/examples/stack/border.rb +11 -0
- data/examples/stack/gradients.rb +6 -0
- data/examples/stack/raw_stack.rb +8 -0
- data/examples/stack/stack.rb +27 -0
- data/examples/text_change.rb +4 -0
- data/examples/text_sizes.rb +10 -0
- data/examples/timmy.rb +109 -0
- data/examples/title_and_resize.rb +3 -0
- data/examples/video.rb +10 -0
- data/exe/scarpe +107 -0
- data/fonts/Pacifico.ttf +0 -0
- data/lib/scarpe/alert.rb +19 -0
- data/lib/scarpe/arc.rb +49 -0
- data/lib/scarpe/background.rb +14 -0
- data/lib/scarpe/base64.rb +27 -0
- data/lib/scarpe/border.rb +16 -0
- data/lib/scarpe/button.rb +35 -0
- data/lib/scarpe/cats_cradle.rb +255 -0
- data/lib/scarpe/colors.rb +215 -0
- data/lib/scarpe/display_service.rb +185 -0
- data/lib/scarpe/document_root.rb +20 -0
- data/lib/scarpe/download.rb +123 -0
- data/lib/scarpe/edit_box.rb +29 -0
- data/lib/scarpe/edit_line.rb +25 -0
- data/lib/scarpe/fill.rb +23 -0
- data/lib/scarpe/flow.rb +18 -0
- data/lib/scarpe/font.rb +14 -0
- data/lib/scarpe/glibui/README.md +34 -0
- data/lib/scarpe/glibui/alert.rb +65 -0
- data/lib/scarpe/glibui/app.rb +27 -0
- data/lib/scarpe/glibui/background.rb +18 -0
- data/lib/scarpe/glibui/border.rb +22 -0
- data/lib/scarpe/glibui/button.rb +38 -0
- data/lib/scarpe/glibui/dimensions.rb +22 -0
- data/lib/scarpe/glibui/document_root.rb +33 -0
- data/lib/scarpe/glibui/edit_box.rb +44 -0
- data/lib/scarpe/glibui/edit_line.rb +43 -0
- data/lib/scarpe/glibui/flow.rb +33 -0
- data/lib/scarpe/glibui/html.rb +77 -0
- data/lib/scarpe/glibui/image.rb +36 -0
- data/lib/scarpe/glibui/link.rb +29 -0
- data/lib/scarpe/glibui/local_display.rb +62 -0
- data/lib/scarpe/glibui/para.rb +84 -0
- data/lib/scarpe/glibui/spacing.rb +41 -0
- data/lib/scarpe/glibui/stack.rb +33 -0
- data/lib/scarpe/glibui/text_widget.rb +30 -0
- data/lib/scarpe/glibui/widget.rb +81 -0
- data/lib/scarpe/glibui.rb +29 -0
- data/lib/scarpe/image.rb +35 -0
- data/lib/scarpe/libui/alert.rb +9 -0
- data/lib/scarpe/libui/button.rb +18 -0
- data/lib/scarpe/libui/colors.rb +157 -0
- data/lib/scarpe/libui/core.rb +66 -0
- data/lib/scarpe/libui/flow.rb +16 -0
- data/lib/scarpe/libui/libui.rb +45 -0
- data/lib/scarpe/libui/notepad.md +26 -0
- data/lib/scarpe/libui/para.rb +154 -0
- data/lib/scarpe/libui/stack.rb +34 -0
- data/lib/scarpe/line.rb +25 -0
- data/lib/scarpe/link.rb +25 -0
- data/lib/scarpe/list_box.rb +25 -0
- data/lib/scarpe/logger.rb +159 -0
- data/lib/scarpe/para.rb +92 -0
- data/lib/scarpe/promises.rb +405 -0
- data/lib/scarpe/shape.rb +20 -0
- data/lib/scarpe/slot.rb +7 -0
- data/lib/scarpe/spacing.rb +9 -0
- data/lib/scarpe/span.rb +26 -0
- data/lib/scarpe/stack.rb +20 -0
- data/lib/scarpe/star.rb +47 -0
- data/lib/scarpe/subscription_item.rb +60 -0
- data/lib/scarpe/unit_test_helpers.rb +236 -0
- data/lib/scarpe/version.rb +5 -0
- data/lib/scarpe/video.rb +15 -0
- data/lib/scarpe/widgets.rb +35 -0
- data/lib/scarpe/wv/alert.rb +65 -0
- data/lib/scarpe/wv/app.rb +104 -0
- data/lib/scarpe/wv/arc.rb +57 -0
- data/lib/scarpe/wv/background.rb +27 -0
- data/lib/scarpe/wv/border.rb +24 -0
- data/lib/scarpe/wv/button.rb +50 -0
- data/lib/scarpe/wv/control_interface.rb +147 -0
- data/lib/scarpe/wv/control_interface_test.rb +234 -0
- data/lib/scarpe/wv/dimensions.rb +22 -0
- data/lib/scarpe/wv/document_root.rb +8 -0
- data/lib/scarpe/wv/edit_box.rb +44 -0
- data/lib/scarpe/wv/edit_line.rb +43 -0
- data/lib/scarpe/wv/fill.rb +30 -0
- data/lib/scarpe/wv/flow.rb +24 -0
- data/lib/scarpe/wv/font.rb +36 -0
- data/lib/scarpe/wv/html.rb +108 -0
- data/lib/scarpe/wv/image.rb +41 -0
- data/lib/scarpe/wv/line.rb +38 -0
- data/lib/scarpe/wv/link.rb +29 -0
- data/lib/scarpe/wv/list_box.rb +50 -0
- data/lib/scarpe/wv/para.rb +90 -0
- data/lib/scarpe/wv/shape.rb +35 -0
- data/lib/scarpe/wv/shape_helper.rb +44 -0
- data/lib/scarpe/wv/slot.rb +81 -0
- data/lib/scarpe/wv/spacing.rb +41 -0
- data/lib/scarpe/wv/span.rb +66 -0
- data/lib/scarpe/wv/stack.rb +24 -0
- data/lib/scarpe/wv/star.rb +64 -0
- data/lib/scarpe/wv/subscription_item.rb +50 -0
- data/lib/scarpe/wv/text_widget.rb +30 -0
- data/lib/scarpe/wv/video.rb +42 -0
- data/lib/scarpe/wv/web_wrangler.rb +683 -0
- data/lib/scarpe/wv/webview_local_display.rb +73 -0
- data/lib/scarpe/wv/webview_relay_display.rb +185 -0
- data/lib/scarpe/wv/widget.rb +185 -0
- data/lib/scarpe/wv/wv_display_worker.rb +65 -0
- data/lib/scarpe/wv.rb +43 -0
- data/lib/scarpe/wv_local.rb +6 -0
- data/lib/scarpe/wv_relay.rb +6 -0
- data/lib/scarpe.rb +29 -0
- data/lib/shoes/app.rb +187 -0
- data/lib/shoes/constants.rb +26 -0
- data/lib/shoes/text_widget.rb +52 -0
- data/lib/shoes/widget.rb +209 -0
- data/lib/shoes.rb +65 -0
- data/logger/debug_web_wrangler.json +4 -0
- data/logger/scarpe_wv_test.json +4 -0
- data/scarpe-0.2.0.gem +0 -0
- data/scarpe.gemspec +44 -0
- data/scarpegen.rb +193 -0
- data/sig/scarpe.rbs +4 -0
- data/templates/basic_class_template.erb +38 -0
- data/templates/class_template_with_event_bind.erb +27 -0
- data/templates/class_template_with_shapes.erb +43 -0
- data/templates/example_template.erb +3 -0
- data/templates/module_template.erb +12 -0
- data/templates/webview_template.erb +27 -0
- metadata +606 -0
@@ -0,0 +1,683 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "webview_ruby"
|
4
|
+
require "cgi"
|
5
|
+
|
6
|
+
# WebWrangler operates in multiple phases: setup and running.
|
7
|
+
|
8
|
+
# After creation, it starts in setup mode, and you can
|
9
|
+
# use setup-mode callbacks.
|
10
|
+
|
11
|
+
class Scarpe
|
12
|
+
class WebWrangler
|
13
|
+
include Scarpe::Log
|
14
|
+
|
15
|
+
attr_reader :is_running
|
16
|
+
attr_reader :is_terminated
|
17
|
+
attr_reader :heartbeat # This is the heartbeat duration in seconds, usually fractional
|
18
|
+
attr_reader :control_interface
|
19
|
+
|
20
|
+
# This error indicates a problem when running ConfirmedEval
|
21
|
+
class JSEvalError < Scarpe::Error
|
22
|
+
def initialize(data)
|
23
|
+
@data = data
|
24
|
+
super(data[:msg] || (self.class.name + "!"))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# We got an error running the supplied JS code string in confirmed_eval
|
29
|
+
class JSRuntimeError < JSEvalError
|
30
|
+
end
|
31
|
+
|
32
|
+
# The code timed out for some reason
|
33
|
+
class JSTimeoutError < JSEvalError
|
34
|
+
end
|
35
|
+
|
36
|
+
# We got weird or nonsensical results that seem like an error on WebWrangler's part
|
37
|
+
class InternalError < JSEvalError
|
38
|
+
end
|
39
|
+
|
40
|
+
# This is the JS function name for eval results
|
41
|
+
EVAL_RESULT = "scarpeAsyncEvalResult"
|
42
|
+
|
43
|
+
# Allow a half-second for Webview to finish our JS eval before we decide it's not going to
|
44
|
+
EVAL_DEFAULT_TIMEOUT = 0.5
|
45
|
+
|
46
|
+
def initialize(title:, width:, height:, resizable: false, debug: false, heartbeat: 0.1)
|
47
|
+
log_init("WV::WebWrangler")
|
48
|
+
|
49
|
+
@log.debug("Creating WebWrangler...")
|
50
|
+
|
51
|
+
# For now, always allow inspect element
|
52
|
+
@webview = WebviewRuby::Webview.new debug: true
|
53
|
+
@webview = Scarpe::LoggedWrapper.new(@webview, "WebviewAPI") if debug
|
54
|
+
@init_refs = {} # Inits don't go away so keep a reference to them to prevent GC
|
55
|
+
|
56
|
+
@title = title
|
57
|
+
@width = width
|
58
|
+
@height = height
|
59
|
+
@resizable = resizable
|
60
|
+
@heartbeat = heartbeat
|
61
|
+
|
62
|
+
# Better to have a single setInterval than many when we don't care too much
|
63
|
+
# about the timing.
|
64
|
+
@heartbeat_handlers = []
|
65
|
+
|
66
|
+
# Need to keep track of which WebView Javascript evals are still pending,
|
67
|
+
# what handlers to call when they return, etc.
|
68
|
+
@pending_evals = {}
|
69
|
+
@eval_counter = 0
|
70
|
+
|
71
|
+
@dom_wrangler = DOMWrangler.new(self)
|
72
|
+
|
73
|
+
bind("puts") do |*args|
|
74
|
+
puts(*args)
|
75
|
+
end
|
76
|
+
|
77
|
+
@webview.bind(EVAL_RESULT) do |*results|
|
78
|
+
receive_eval_result(*results)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Ruby receives scarpeHeartbeat messages via the window library's main loop.
|
82
|
+
# So this is a way for Ruby to be notified periodically, in time with that loop.
|
83
|
+
@webview.bind("scarpeHeartbeat") do
|
84
|
+
return unless @webview # I think GTK+ may continue to deliver events after shutdown
|
85
|
+
|
86
|
+
periodic_js_callback
|
87
|
+
@heartbeat_handlers.each(&:call)
|
88
|
+
@control_interface.dispatch_event(:heartbeat)
|
89
|
+
end
|
90
|
+
js_interval = (heartbeat.to_f * 1_000.0).to_i
|
91
|
+
@webview.init("setInterval(scarpeHeartbeat,#{js_interval})")
|
92
|
+
end
|
93
|
+
|
94
|
+
# Shorter name for better stack trace messages
|
95
|
+
def inspect
|
96
|
+
"Scarpe::WebWrangler:#{object_id}"
|
97
|
+
end
|
98
|
+
|
99
|
+
attr_writer :control_interface
|
100
|
+
|
101
|
+
### Setup-mode Callbacks
|
102
|
+
|
103
|
+
def bind(name, &block)
|
104
|
+
raise "App is running, javascript binding no longer works because it uses WebView init!" if @is_running
|
105
|
+
|
106
|
+
@webview.bind(name, &block)
|
107
|
+
end
|
108
|
+
|
109
|
+
def init_code(name, &block)
|
110
|
+
raise "App is running, javascript init no longer works!" if @is_running
|
111
|
+
|
112
|
+
# Save a reference to the init string so that it goesn't get GC'd
|
113
|
+
code_str = "#{name}();"
|
114
|
+
@init_refs[name] = code_str
|
115
|
+
|
116
|
+
bind(name, &block)
|
117
|
+
@webview.init(code_str)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Run the specified code periodically, every "interval" seconds.
|
121
|
+
# If interface is unspecified, run per-heartbeat, which is very
|
122
|
+
# slightly more efficient.
|
123
|
+
def periodic_code(name, interval = heartbeat, &block)
|
124
|
+
if interval == heartbeat
|
125
|
+
@heartbeat_handlers << block
|
126
|
+
else
|
127
|
+
if @is_running
|
128
|
+
# I *think* we need to use init because we want this done for every
|
129
|
+
# new window. But will there ever be a new page/window? Can we just
|
130
|
+
# use eval instead of init to set up a periodic handler and call it
|
131
|
+
# good?
|
132
|
+
raise "App is running, can't set up new periodic handlers with init!"
|
133
|
+
end
|
134
|
+
|
135
|
+
js_interval = (interval.to_f * 1_000.0).to_i
|
136
|
+
code_str = "setInterval(#{name}, #{js_interval});"
|
137
|
+
@init_refs[name] = code_str
|
138
|
+
|
139
|
+
bind(name, &block)
|
140
|
+
@webview.init(code_str)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Running callbacks
|
145
|
+
|
146
|
+
# js_eventually is a simple JS evaluation. On syntax error, nothing happens.
|
147
|
+
# On runtime error, execution stops at the error with no further
|
148
|
+
# effect or notification. This is rarely what you want.
|
149
|
+
# The js_eventually code is run asynchronously, returning neither error
|
150
|
+
# nor value.
|
151
|
+
#
|
152
|
+
# This method does *not* return a promise, and there is no way to track
|
153
|
+
# its progress or its success or failure.
|
154
|
+
def js_eventually(code)
|
155
|
+
raise "WebWrangler isn't running, eval doesn't work!" unless @is_running
|
156
|
+
|
157
|
+
@log.warning "Deprecated: please do NOT use js_eventually, it's basically never what you want!" unless ENV["CI"]
|
158
|
+
|
159
|
+
@webview.eval(code)
|
160
|
+
end
|
161
|
+
|
162
|
+
# Eval a chunk of JS code asynchronously. This method returns a
|
163
|
+
# promise which will be fulfilled or rejected after the JS executes
|
164
|
+
# or times out.
|
165
|
+
#
|
166
|
+
# Note that we *both* care whether the JS has finished after it was
|
167
|
+
# scheduled *and* whether it ever got scheduled at all. If it
|
168
|
+
# depends on tasks that never fulfill or reject then it may wait
|
169
|
+
# in limbo, potentially forever.
|
170
|
+
#
|
171
|
+
# Right now we can't/don't handle arguments from previous fulfilled
|
172
|
+
# promises. To do that, we'd probably need to know we were passing
|
173
|
+
# in a JS function.
|
174
|
+
EVAL_OPTS = [:timeout, :wait_for]
|
175
|
+
def eval_js_async(code, opts = {})
|
176
|
+
bad_opts = opts.keys - EVAL_OPTS
|
177
|
+
raise("Bad options given to eval_with_handler! #{bad_opts.inspect}") unless bad_opts.empty?
|
178
|
+
|
179
|
+
unless @is_running
|
180
|
+
raise "WebWrangler isn't running, so evaluating JS won't work!"
|
181
|
+
end
|
182
|
+
|
183
|
+
this_eval_serial = @eval_counter
|
184
|
+
@eval_counter += 1
|
185
|
+
|
186
|
+
@pending_evals[this_eval_serial] = {
|
187
|
+
id: this_eval_serial,
|
188
|
+
code: code,
|
189
|
+
start_time: Time.now,
|
190
|
+
timeout_if_not_scheduled: Time.now + EVAL_DEFAULT_TIMEOUT,
|
191
|
+
}
|
192
|
+
|
193
|
+
# We'll need this inside the promise-scheduling block
|
194
|
+
pending_evals = @pending_evals
|
195
|
+
timeout = opts[:timeout] || EVAL_DEFAULT_TIMEOUT
|
196
|
+
|
197
|
+
promise = Scarpe::Promise.new(parents: (opts[:wait_for] || [])) do
|
198
|
+
# Are we mid-shutdown?
|
199
|
+
if @webview
|
200
|
+
wrapped_code = WebWrangler.js_wrapped_code(code, this_eval_serial)
|
201
|
+
|
202
|
+
# We've been scheduled!
|
203
|
+
t_now = Time.now
|
204
|
+
# Hard to be sure Webview keeps a proper reference to this, so we will
|
205
|
+
pending_evals[this_eval_serial][:wrapped_code] = wrapped_code
|
206
|
+
|
207
|
+
pending_evals[this_eval_serial][:scheduled_time] = t_now
|
208
|
+
pending_evals[this_eval_serial].delete(:timeout_if_not_scheduled)
|
209
|
+
|
210
|
+
pending_evals[this_eval_serial][:timeout_if_not_finished] = t_now + timeout
|
211
|
+
@webview.eval(wrapped_code)
|
212
|
+
@log.debug("Scheduled JS: (#{this_eval_serial})\n#{wrapped_code}")
|
213
|
+
else
|
214
|
+
# We're mid-shutdown. No more scheduling things.
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
@pending_evals[this_eval_serial][:promise] = promise
|
219
|
+
|
220
|
+
promise
|
221
|
+
end
|
222
|
+
|
223
|
+
def self.js_wrapped_code(code, eval_id)
|
224
|
+
<<~JS_CODE
|
225
|
+
(function() {
|
226
|
+
var code_string = #{JSON.dump code};
|
227
|
+
try {
|
228
|
+
result = eval(code_string);
|
229
|
+
#{EVAL_RESULT}("success", #{eval_id}, result);
|
230
|
+
} catch(error) {
|
231
|
+
#{EVAL_RESULT}("error", #{eval_id}, error.message);
|
232
|
+
}
|
233
|
+
})();
|
234
|
+
JS_CODE
|
235
|
+
end
|
236
|
+
|
237
|
+
private
|
238
|
+
|
239
|
+
def periodic_js_callback
|
240
|
+
time_out_eval_results
|
241
|
+
end
|
242
|
+
|
243
|
+
def receive_eval_result(r_type, id, val)
|
244
|
+
entry = @pending_evals.delete(id)
|
245
|
+
unless entry
|
246
|
+
raise "Received an eval result for a nonexistent ID #{id.inspect}!"
|
247
|
+
end
|
248
|
+
|
249
|
+
@log.debug("Got JS value: #{r_type} / #{id} / #{val.inspect}")
|
250
|
+
|
251
|
+
promise = entry[:promise]
|
252
|
+
|
253
|
+
case r_type
|
254
|
+
when "success"
|
255
|
+
promise.fulfilled!(val)
|
256
|
+
when "error"
|
257
|
+
promise.rejected! JSRuntimeError.new(
|
258
|
+
msg: "JS runtime error: #{val.inspect}!",
|
259
|
+
code: entry[:code],
|
260
|
+
ret_value: val,
|
261
|
+
)
|
262
|
+
else
|
263
|
+
promise.rejected! InternalError.new(
|
264
|
+
msg: "JS eval internal error! r_type: #{r_type.inspect}",
|
265
|
+
code: entry[:code],
|
266
|
+
ret_value: val,
|
267
|
+
)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
# TODO: would be good to keep 'tombstone' results for awhile after timeout, maybe up to around a minute,
|
272
|
+
# so we can detect if we're timing things out and then having them return successfully after a delay.
|
273
|
+
# Then we could adjust the timeouts. We could also check if later serial numbers have returned, and time
|
274
|
+
# out earlier serial numbers... *if* we're sure Webview will always execute JS evals in order.
|
275
|
+
# This all adds complexity, though. For now, do timeouts on a simple max duration.
|
276
|
+
def time_out_eval_results
|
277
|
+
t_now = Time.now
|
278
|
+
timed_out_from_scheduling = @pending_evals.keys.select do |id|
|
279
|
+
t = @pending_evals[id][:timeout_if_not_scheduled]
|
280
|
+
t && t_now >= t
|
281
|
+
end
|
282
|
+
timed_out_from_finish = @pending_evals.keys.select do |id|
|
283
|
+
t = @pending_evals[id][:timeout_if_not_finished]
|
284
|
+
t && t_now >= t
|
285
|
+
end
|
286
|
+
timed_out_from_scheduling.each do |id|
|
287
|
+
@log.debug("JS timed out because it was never scheduled: (#{id}) #{@pending_evals[id][:code].inspect}")
|
288
|
+
end
|
289
|
+
timed_out_from_finish.each do |id|
|
290
|
+
@log.debug("JS timed out because it never finished: (#{id}) #{@pending_evals[id][:code].inspect}")
|
291
|
+
end
|
292
|
+
|
293
|
+
# A plus *should* be fine since nothing should ever be on both lists. But let's be safe.
|
294
|
+
timed_out_ids = timed_out_from_scheduling | timed_out_from_finish
|
295
|
+
|
296
|
+
timed_out_ids.each do |id|
|
297
|
+
@log.error "Timing out JS eval! #{@pending_evals[id][:code]}"
|
298
|
+
entry = @pending_evals.delete(id)
|
299
|
+
err = JSTimeoutError.new(msg: "JS timeout error!", code: entry[:code], ret_value: nil)
|
300
|
+
entry[:promise].rejected!(err)
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
public
|
305
|
+
|
306
|
+
# After setup, we call run to go to "running" mode.
|
307
|
+
# No more setup callbacks, only running callbacks.
|
308
|
+
|
309
|
+
def run
|
310
|
+
@log.debug("Run...")
|
311
|
+
|
312
|
+
# From webview:
|
313
|
+
# 0 - Width and height are default size
|
314
|
+
# 1 - Width and height are minimum bonds
|
315
|
+
# 2 - Width and height are maximum bonds
|
316
|
+
# 3 - Window size can not be changed by a user
|
317
|
+
hint = @resizable ? 0 : 3
|
318
|
+
|
319
|
+
@webview.set_title(@title)
|
320
|
+
@webview.set_size(@width, @height, hint)
|
321
|
+
@webview.navigate("data:text/html, #{empty}")
|
322
|
+
|
323
|
+
monkey_patch_console(@webview)
|
324
|
+
|
325
|
+
@is_running = true
|
326
|
+
@webview.run
|
327
|
+
@is_running = false
|
328
|
+
@webview.destroy
|
329
|
+
@webview = nil
|
330
|
+
end
|
331
|
+
|
332
|
+
def destroy
|
333
|
+
@log.debug("Destroying WebWrangler...")
|
334
|
+
@log.debug(" (WebWrangler was already terminated)") if @is_terminated
|
335
|
+
@log.debug(" (WebWrangler was already destroyed)") unless @webview
|
336
|
+
if @webview && !@is_terminated
|
337
|
+
@bindings = {}
|
338
|
+
@webview.terminate
|
339
|
+
@is_terminated = true
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
private
|
344
|
+
|
345
|
+
# TODO: can this be an init()?
|
346
|
+
def monkey_patch_console(window)
|
347
|
+
# this forwards all console.log/info/error/warn calls also
|
348
|
+
# to the terminal that is running the scarpe app
|
349
|
+
window.eval <<~JS
|
350
|
+
function patchConsole(fn) {
|
351
|
+
const original = console[fn];
|
352
|
+
console[fn] = function(...args) {
|
353
|
+
original(...args);
|
354
|
+
puts(...args);
|
355
|
+
}
|
356
|
+
};
|
357
|
+
patchConsole('log');
|
358
|
+
patchConsole('info');
|
359
|
+
patchConsole('error');
|
360
|
+
patchConsole('warn');
|
361
|
+
JS
|
362
|
+
end
|
363
|
+
|
364
|
+
def empty
|
365
|
+
html = <<~HTML
|
366
|
+
<html>
|
367
|
+
<head id='head-wvroot'>
|
368
|
+
<style id='style-wvroot'>
|
369
|
+
/** Style resets **/
|
370
|
+
body {
|
371
|
+
font-family: arial, Helvetica, sans-serif;
|
372
|
+
margin: 0;
|
373
|
+
height: 100%;
|
374
|
+
overflow: hidden;
|
375
|
+
}
|
376
|
+
p {
|
377
|
+
margin: 0;
|
378
|
+
}
|
379
|
+
</style>
|
380
|
+
</head>
|
381
|
+
<body id='body-wvroot'>
|
382
|
+
<div id='wrapper-wvroot'></div>
|
383
|
+
</body>
|
384
|
+
</html>
|
385
|
+
HTML
|
386
|
+
|
387
|
+
CGI.escape(html)
|
388
|
+
end
|
389
|
+
|
390
|
+
public
|
391
|
+
|
392
|
+
# For now, the WebWrangler gets a bunch of fairly low-level requests
|
393
|
+
# to mess with the HTML DOM. This needs to be turned into a nicer API,
|
394
|
+
# but first we'll get it all into one place and see what we're doing.
|
395
|
+
|
396
|
+
# Replace the entire DOM - return a promise for when this has been done.
|
397
|
+
# This will often get rid of smaller changes in the queue, which is
|
398
|
+
# a good thing since they won't have to be run.
|
399
|
+
def replace(html_text)
|
400
|
+
@dom_wrangler.request_replace(html_text)
|
401
|
+
end
|
402
|
+
|
403
|
+
# Request a DOM change - return a promise for when this has been done.
|
404
|
+
def dom_change(js)
|
405
|
+
@dom_wrangler.request_change(js)
|
406
|
+
end
|
407
|
+
|
408
|
+
# Return whether the DOM is, right this moment, confirmed to be fully
|
409
|
+
# up to date or not.
|
410
|
+
def dom_fully_updated?
|
411
|
+
@dom_wrangler.fully_updated?
|
412
|
+
end
|
413
|
+
|
414
|
+
# Return a promise that will be fulfilled when all current DOM changes
|
415
|
+
# have committed (but not necessarily any future DOM changes.)
|
416
|
+
def dom_promise_redraw
|
417
|
+
@dom_wrangler.promise_redraw
|
418
|
+
end
|
419
|
+
|
420
|
+
# Return a promise which will be fulfilled the next time the DOM is
|
421
|
+
# fully up to date. Note that a slow trickle of changes can make this
|
422
|
+
# take a long time, since it is *not* only changes up to this point.
|
423
|
+
# If you want to know that some specific change is done, it's often
|
424
|
+
# easiest to use the promise returned by dom_change(), which will
|
425
|
+
# be fulfilled when that specific change commits.
|
426
|
+
def promise_dom_fully_updated
|
427
|
+
@dom_wrangler.promise_fully_updated
|
428
|
+
end
|
429
|
+
|
430
|
+
def on_every_redraw(&block)
|
431
|
+
@dom_wrangler.on_every_redraw(&block)
|
432
|
+
end
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
# Leaving DOM changes as "meh, async, we'll see when it happens" is terrible for testing.
|
437
|
+
# Instead, we need to track whether particular changes have committed yet or not.
|
438
|
+
# So we add a single gateway for all DOM changes, and we make sure its work is done
|
439
|
+
# before we consider a redraw complete.
|
440
|
+
#
|
441
|
+
# DOMWrangler batches up changes - it's fine to have a redraw "in flight" and have
|
442
|
+
# changes waiting to catch the next bus. But we don't want more than one in flight,
|
443
|
+
# since it seems like having too many pending RPC requests can crash Webview. So:
|
444
|
+
# one redraw scheduled and one redraw promise waiting around, at maximum.
|
445
|
+
class Scarpe
|
446
|
+
class WebWrangler
|
447
|
+
class DOMWrangler
|
448
|
+
include Scarpe::Log
|
449
|
+
|
450
|
+
attr_reader :waiting_changes
|
451
|
+
attr_reader :pending_redraw_promise
|
452
|
+
attr_reader :waiting_redraw_promise
|
453
|
+
|
454
|
+
def initialize(web_wrangler, debug: false)
|
455
|
+
log_init("WV::WebWrangler::DOMWrangler")
|
456
|
+
|
457
|
+
@wrangler = web_wrangler
|
458
|
+
|
459
|
+
@waiting_changes = []
|
460
|
+
@pending_redraw_promise = nil
|
461
|
+
@waiting_redraw_promise = nil
|
462
|
+
|
463
|
+
@fully_up_to_date_promise = nil
|
464
|
+
|
465
|
+
# Initially we're waiting for a full replacement to happen.
|
466
|
+
# It's possible to request updates/changes before we have
|
467
|
+
# a DOM in place and before Webview is running. If we do
|
468
|
+
# that, we should discard those updates.
|
469
|
+
@first_draw_requested = false
|
470
|
+
|
471
|
+
@redraw_handlers = []
|
472
|
+
|
473
|
+
# The "fully up to date" logic is complicated and not
|
474
|
+
# as well tested as I'd like. This makes it far less
|
475
|
+
# likely that the event simply won't fire.
|
476
|
+
# With more comprehensive testing, this should be
|
477
|
+
# removable.
|
478
|
+
web_wrangler.periodic_code("scarpeDOMWranglerHeartbeat") do
|
479
|
+
if @fully_up_to_date_promise && fully_updated?
|
480
|
+
@log.info("Fulfilling up-to-date promise on heartbeat")
|
481
|
+
@fully_up_to_date_promise.fulfilled!
|
482
|
+
@fully_up_to_date_promise = nil
|
483
|
+
end
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
def request_change(js_code)
|
488
|
+
# No updates until there's something to update
|
489
|
+
return unless @first_draw_requested
|
490
|
+
|
491
|
+
@waiting_changes << js_code
|
492
|
+
|
493
|
+
promise_redraw
|
494
|
+
end
|
495
|
+
|
496
|
+
def self.replacement_code(html_text)
|
497
|
+
"document.getElementById('wrapper-wvroot').innerHTML = `#{html_text}`; true"
|
498
|
+
end
|
499
|
+
|
500
|
+
def request_replace(html_text)
|
501
|
+
# Replace other pending changes, they're not needed any more
|
502
|
+
@waiting_changes = [DOMWrangler.replacement_code(html_text)]
|
503
|
+
@first_draw_requested = true
|
504
|
+
|
505
|
+
@log.debug("Requesting DOM replacement...")
|
506
|
+
promise_redraw
|
507
|
+
end
|
508
|
+
|
509
|
+
def on_every_redraw(&block)
|
510
|
+
@redraw_handlers << block
|
511
|
+
end
|
512
|
+
|
513
|
+
# What are the states of redraw?
|
514
|
+
# "empty" - no waiting promise, no pending-redraw promise, no pending changes
|
515
|
+
# "pending only" - no waiting promise, but we have a pending redraw with some changes; it hasn't committed yet
|
516
|
+
# "pending and waiting" - we have a waiting promise for our unscheduled changes; we can add more unscheduled
|
517
|
+
# changes since we haven't scheduled them yet.
|
518
|
+
#
|
519
|
+
# This is often called after adding a new waiting change or replacing them, so the state may have just changed.
|
520
|
+
# It can also be called when no changes have been made and no updates need to happen.
|
521
|
+
def promise_redraw
|
522
|
+
if fully_updated?
|
523
|
+
# No changes to make, nothing in-process or waiting, so just return a pre-fulfilled promise
|
524
|
+
@log.debug("Requesting redraw but there are no pending changes or promises, return pre-fulfilled")
|
525
|
+
return Promise.fulfilled
|
526
|
+
end
|
527
|
+
|
528
|
+
# Already have a redraw requested *and* one on deck? Then all current changes will have committed
|
529
|
+
# when we (eventually) fulfill the waiting_redraw_promise.
|
530
|
+
if @waiting_redraw_promise
|
531
|
+
@log.debug("Promising eventual redraw of #{@waiting_changes.size} waiting unscheduled changes.")
|
532
|
+
return @waiting_redraw_promise
|
533
|
+
end
|
534
|
+
|
535
|
+
if @waiting_changes.empty?
|
536
|
+
# There's no waiting_redraw_promise. There are no waiting changes. But we're not fully updated.
|
537
|
+
# So there must be a redraw in flight, and we don't need to schedule a new waiting_redraw_promise.
|
538
|
+
@log.debug("Returning in-flight redraw promise")
|
539
|
+
return @pending_redraw_promise
|
540
|
+
end
|
541
|
+
|
542
|
+
@log.debug("Requesting redraw with #{@waiting_changes.size} waiting changes and no waiting promise - need to schedule something!")
|
543
|
+
|
544
|
+
# We have at least one waiting change, possibly newly-added. We have no waiting_redraw_promise.
|
545
|
+
# Do we already have a redraw in-flight?
|
546
|
+
if @pending_redraw_promise
|
547
|
+
# Yes we do. Schedule a new waiting promise. When it turns into the pending_redraw_promise it will
|
548
|
+
# grab all waiting changes. In the mean time, it sits here and waits.
|
549
|
+
#
|
550
|
+
# We *could* do a fancy promise thing and have it update @waiting_changes for itself, etc, when it
|
551
|
+
# schedules itself. But we should always be calling promise_redraw or having a redraw fulfilled (see below)
|
552
|
+
# when these things change. I'd rather keep the logic in this method. It's easier to reason through
|
553
|
+
# all the cases.
|
554
|
+
@waiting_redraw_promise = Promise.new
|
555
|
+
|
556
|
+
@log.debug("Creating a new waiting promise since a pending promise is already in place")
|
557
|
+
return @waiting_redraw_promise
|
558
|
+
end
|
559
|
+
|
560
|
+
# We have no redraw in-flight and no pre-existing waiting line. The new change(s) are presumably right
|
561
|
+
# after things were fully up-to-date. We can schedule them for immediate redraw.
|
562
|
+
|
563
|
+
@log.debug("Requesting redraw with #{@waiting_changes.size} waiting changes - scheduling a new redraw for them!")
|
564
|
+
promise = schedule_waiting_changes # This clears the waiting changes
|
565
|
+
@pending_redraw_promise = promise
|
566
|
+
|
567
|
+
promise.on_fulfilled do
|
568
|
+
@redraw_handlers.each(&:call)
|
569
|
+
@pending_redraw_promise = nil
|
570
|
+
|
571
|
+
if @waiting_redraw_promise
|
572
|
+
# While this redraw was in flight, more waiting changes got added and we made a promise
|
573
|
+
# about when they'd complete. Now they get scheduled, and we'll fulfill the waiting
|
574
|
+
# promise when that redraw finishes. Clear the old waiting promise. We'll add a new one
|
575
|
+
# when/if more changes are scheduled during this redraw.
|
576
|
+
old_waiting_promise = @waiting_redraw_promise
|
577
|
+
@waiting_redraw_promise = nil
|
578
|
+
|
579
|
+
@log.debug "Fulfilled redraw with #{@waiting_changes.size} waiting changes - scheduling a new redraw for them!"
|
580
|
+
|
581
|
+
new_promise = promise_redraw
|
582
|
+
new_promise.on_fulfilled { old_waiting_promise.fulfilled! }
|
583
|
+
else
|
584
|
+
# The in-flight redraw completed, and there's still no waiting promise. Good! That means
|
585
|
+
# we should be fully up-to-date.
|
586
|
+
@log.debug "Fulfilled redraw with no waiting changes - marking us as up to date!"
|
587
|
+
if @waiting_changes.empty?
|
588
|
+
# We're fully up to date! Fulfill the promise. Now we don't need it again until somebody asks
|
589
|
+
# us for another.
|
590
|
+
if @fully_up_to_date_promise
|
591
|
+
@fully_up_to_date_promise.fulfilled!
|
592
|
+
@fully_up_to_date_promise = nil
|
593
|
+
end
|
594
|
+
else
|
595
|
+
@log.error "WHOAH, WHAT? My logic must be wrong, because there's " +
|
596
|
+
"no waiting promise, but waiting changes!"
|
597
|
+
end
|
598
|
+
end
|
599
|
+
|
600
|
+
@log.debug("Redraw is now fully up-to-date") if fully_updated?
|
601
|
+
end.on_rejected do
|
602
|
+
@log.error "Could not complete JS redraw! #{promise.reason.full_message}"
|
603
|
+
@log.debug("REDRAW FULLY UP TO DATE BUT JS FAILED") if fully_updated?
|
604
|
+
|
605
|
+
raise "JS Redraw failed! Bailing!"
|
606
|
+
|
607
|
+
# Later we should figure out how to handle this. Clear the promises and queues and request another redraw?
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
def fully_updated?
|
612
|
+
@pending_redraw_promise.nil? && @waiting_redraw_promise.nil? && @waiting_changes.empty?
|
613
|
+
end
|
614
|
+
|
615
|
+
# Return a promise which will be fulfilled when the DOM is fully up-to-date
|
616
|
+
def promise_fully_updated
|
617
|
+
if fully_updated?
|
618
|
+
# No changes to make, nothing in-process or waiting, so just return a pre-fulfilled promise
|
619
|
+
return Promise.fulfilled
|
620
|
+
end
|
621
|
+
|
622
|
+
# Do we already have a promise for this? Return it. Everybody can share one.
|
623
|
+
if @fully_up_to_date_promise
|
624
|
+
return @fully_up_to_date_promise
|
625
|
+
end
|
626
|
+
|
627
|
+
# We're not fully updated, so we need a promise. Create it, return it.
|
628
|
+
@fully_up_to_date_promise = Promise.new
|
629
|
+
end
|
630
|
+
|
631
|
+
private
|
632
|
+
|
633
|
+
# Put together the waiting changes into a new in-flight redraw request.
|
634
|
+
# Return it as a promise.
|
635
|
+
def schedule_waiting_changes
|
636
|
+
return if @waiting_changes.empty?
|
637
|
+
|
638
|
+
js_code = @waiting_changes.join(";")
|
639
|
+
@waiting_changes = [] # They're not waiting any more!
|
640
|
+
@wrangler.eval_js_async(js_code)
|
641
|
+
end
|
642
|
+
end
|
643
|
+
end
|
644
|
+
end
|
645
|
+
|
646
|
+
# For now we don't need one of these to add DOM elements, just to manipulate them
|
647
|
+
# after initial render.
|
648
|
+
class Scarpe
|
649
|
+
class WebWrangler
|
650
|
+
class ElementWrangler
|
651
|
+
attr_reader :html_id
|
652
|
+
|
653
|
+
def initialize(html_id)
|
654
|
+
@webwrangler = WebviewDisplayService.instance.wrangler
|
655
|
+
@html_id = html_id
|
656
|
+
end
|
657
|
+
|
658
|
+
def promise_update
|
659
|
+
@webwrangler.dom_promise_redraw
|
660
|
+
end
|
661
|
+
|
662
|
+
def value=(new_value)
|
663
|
+
@webwrangler.dom_change("document.getElementById('" + html_id + "').value = `" + new_value + "`; true")
|
664
|
+
end
|
665
|
+
|
666
|
+
def inner_text=(new_text)
|
667
|
+
@webwrangler.dom_change("document.getElementById('" + html_id + "').innerText = '" + new_text + "'; true")
|
668
|
+
end
|
669
|
+
|
670
|
+
def inner_html=(new_html)
|
671
|
+
@webwrangler.dom_change("document.getElementById(\"" + html_id + "\").innerHTML = `" + new_html + "`; true")
|
672
|
+
end
|
673
|
+
|
674
|
+
def set_attribute(attribute, value)
|
675
|
+
@webwrangler.dom_change("document.getElementById(\"" + html_id + "\").setAttribute(" + attribute.inspect + "," + value.inspect + "); true")
|
676
|
+
end
|
677
|
+
|
678
|
+
def remove
|
679
|
+
@webwrangler.dom_change("document.getElementById('" + html_id + "').remove(); true")
|
680
|
+
end
|
681
|
+
end
|
682
|
+
end
|
683
|
+
end
|