scarpe 0.2.1 → 0.2.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -0
- data/.yardopts +11 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +112 -0
- data/README.md +31 -24
- data/Rakefile +13 -1
- 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/background_with_image.rb +16 -0
- data/examples/bloopsaphone/working/bronx_army_knife.rb +66 -0
- data/examples/bloopsaphone/working/morning_serenity.rb +21 -0
- data/examples/bloopsaphone/working/simpsons_theme_song_by_why.rb +6 -4
- data/examples/button_go_away.rb +1 -1
- data/examples/check.rb +18 -0
- data/examples/clear_and_append.rb +24 -0
- data/examples/download_and_show_image.rb +28 -0
- data/examples/edit_box.rb +3 -5
- data/examples/fonts.rb +2 -2
- data/examples/get_headers.rb +10 -0
- data/examples/highlander.rb +2 -0
- data/examples/link.rb +2 -2
- data/examples/local_fonts.rb +4 -0
- data/examples/local_images.rb +4 -0
- data/examples/motion_events.rb +20 -0
- data/examples/parse_xl_funnies.rb +58 -0
- data/examples/radio/radio.rb +16 -0
- data/examples/radio/radio_groups.rb +18 -0
- data/examples/radio/radio_same_slot.rb +6 -0
- data/examples/ruby_racer.rb +13 -15
- data/examples/selfitude.rb +18 -0
- data/examples/shapes/shapes_fill.rb +4 -3
- data/examples/shoes_school.rb +2 -4
- data/examples/show_hide.rb +6 -0
- data/examples/skip_ci/change_my_audio_source.rb +21 -0
- data/examples/skip_ci/guitar_fretboard.rb +137 -0
- data/examples/video.rb +10 -0
- data/exe/scarpe +42 -66
- data/fonts/Pacifico.ttf +0 -0
- data/lacci/Gemfile +22 -0
- data/lacci/Gemfile.lock +72 -0
- data/lacci/Rakefile +12 -0
- data/lacci/lacci.gemspec +37 -0
- data/lacci/lib/lacci/scarpe_cli.rb +70 -0
- data/lacci/lib/lacci/scarpe_core.rb +21 -0
- data/lacci/lib/lacci/version.rb +13 -0
- data/lacci/lib/shoes/app.rb +264 -0
- data/{lib/scarpe → lacci/lib/shoes}/background.rb +1 -1
- data/{lib/scarpe → lacci/lib/shoes}/border.rb +1 -1
- data/{lib/scarpe → lacci/lib/shoes}/colors.rb +1 -1
- data/lacci/lib/shoes/constants.rb +29 -0
- data/{lib/scarpe → lacci/lib/shoes}/display_service.rb +40 -45
- data/lacci/lib/shoes/download.rb +123 -0
- data/lacci/lib/shoes/log.rb +71 -0
- data/lacci/lib/shoes/spacing.rb +9 -0
- data/{lib/scarpe → lacci/lib/shoes}/widget.rb +63 -43
- data/{lib/scarpe → lacci/lib/shoes/widgets}/alert.rb +3 -3
- data/{lib/scarpe → lacci/lib/shoes/widgets}/arc.rb +7 -5
- data/{lib/scarpe → lacci/lib/shoes/widgets}/button.rb +3 -3
- data/lacci/lib/shoes/widgets/check.rb +28 -0
- data/lacci/lib/shoes/widgets/document_root.rb +20 -0
- data/{lib/scarpe → lacci/lib/shoes/widgets}/edit_box.rb +10 -5
- data/{lib/scarpe → lacci/lib/shoes/widgets}/edit_line.rb +2 -2
- data/lacci/lib/shoes/widgets/flow.rb +22 -0
- data/lacci/lib/shoes/widgets/font.rb +14 -0
- data/{lib/scarpe → lacci/lib/shoes/widgets}/image.rb +3 -7
- data/lacci/lib/shoes/widgets/line.rb +18 -0
- data/{lib/scarpe → lacci/lib/shoes/widgets}/link.rb +2 -2
- data/{lib/scarpe → lacci/lib/shoes/widgets}/list_box.rb +2 -2
- data/{lib/scarpe → lacci/lib/shoes/widgets}/para.rb +4 -26
- data/lacci/lib/shoes/widgets/radio.rb +35 -0
- data/lacci/lib/shoes/widgets/shape.rb +37 -0
- data/lacci/lib/shoes/widgets/slot.rb +75 -0
- data/{lib/scarpe → lacci/lib/shoes/widgets}/span.rb +2 -2
- data/lacci/lib/shoes/widgets/stack.rb +24 -0
- data/{lib/scarpe → lacci/lib/shoes/widgets}/star.rb +6 -9
- data/lacci/lib/shoes/widgets/subscription_item.rb +60 -0
- data/lacci/lib/shoes/widgets/text_widget.rb +51 -0
- data/lacci/lib/shoes/widgets/video.rb +15 -0
- data/lacci/lib/shoes/widgets.rb +29 -0
- data/lacci/lib/shoes.rb +127 -0
- data/lacci/test/test_colors.rb +39 -0
- data/lacci/test/test_helper.rb +9 -0
- data/lacci/test/test_lacci.rb +9 -0
- data/lib/scarpe/cats_cradle.rb +249 -0
- data/lib/scarpe/evented_assertions.rb +88 -0
- data/lib/scarpe/version.rb +1 -1
- data/lib/scarpe/wv/alert.rb +3 -2
- data/lib/scarpe/wv/app.rb +30 -8
- data/lib/scarpe/wv/arc.rb +5 -6
- data/lib/scarpe/wv/background.rb +10 -1
- data/lib/scarpe/wv/border.rb +5 -3
- data/lib/scarpe/wv/button.rb +11 -9
- data/lib/scarpe/wv/check.rb +29 -0
- data/lib/scarpe/wv/control_interface.rb +14 -20
- data/lib/scarpe/wv/control_interface_test.rb +13 -28
- data/lib/scarpe/wv/document_root.rb +3 -45
- data/lib/scarpe/wv/edit_box.rb +5 -7
- data/lib/scarpe/wv/edit_line.rb +2 -2
- data/lib/scarpe/wv/flow.rb +10 -20
- data/lib/scarpe/wv/font.rb +36 -0
- data/lib/scarpe/wv/html.rb +3 -2
- data/lib/scarpe/wv/image.rb +7 -2
- data/lib/scarpe/wv/line.rb +4 -7
- data/lib/scarpe/wv/link.rb +1 -0
- data/lib/scarpe/wv/list_box.rb +3 -3
- data/lib/scarpe/wv/para.rb +16 -14
- data/lib/scarpe/wv/radio.rb +34 -0
- data/lib/scarpe/wv/shape.rb +44 -8
- data/lib/scarpe/wv/slot.rb +81 -0
- data/lib/scarpe/wv/spacing.rb +1 -1
- data/lib/scarpe/wv/span.rb +10 -8
- data/lib/scarpe/wv/stack.rb +10 -30
- data/lib/scarpe/wv/star.rb +11 -12
- data/lib/scarpe/wv/subscription_item.rb +50 -0
- data/lib/scarpe/wv/video.rb +34 -0
- data/lib/scarpe/wv/web_wrangler.rb +238 -58
- data/lib/scarpe/wv/webview_local_display.rb +27 -5
- data/lib/scarpe/wv/webview_relay_display.rb +18 -119
- data/lib/scarpe/wv/webview_relay_util.rb +143 -0
- data/lib/scarpe/wv/widget.rb +80 -11
- data/lib/scarpe/wv/wv_display_worker.rb +17 -4
- data/lib/scarpe/wv.rb +33 -4
- data/lib/scarpe/wv_local.rb +1 -1
- data/lib/scarpe/wv_relay.rb +1 -1
- data/lib/scarpe.rb +3 -32
- data/scarpe-components/.gitignore +1 -0
- data/scarpe-components/Gemfile +22 -0
- data/scarpe-components/README.md +35 -0
- data/scarpe-components/Rakefile +12 -0
- data/scarpe-components/lib/scarpe/components/base64.rb +29 -0
- data/scarpe-components/lib/scarpe/components/file_helpers.rb +65 -0
- data/scarpe-components/lib/scarpe/components/modular_logger.rb +113 -0
- data/scarpe-components/lib/scarpe/components/print_logger.rb +43 -0
- data/{lib/scarpe → scarpe-components/lib/scarpe/components}/promises.rb +102 -35
- data/scarpe-components/lib/scarpe/components/segmented_file_loader.rb +170 -0
- data/scarpe-components/lib/scarpe/components/unit_test_helpers.rb +217 -0
- data/scarpe-components/lib/scarpe/components/version.rb +7 -0
- data/scarpe-components/scarpe-components.gemspec +38 -0
- data/scarpe-components/test/test_components.rb +9 -0
- data/scarpe-components/test/test_helper.rb +23 -0
- data/scarpe-components/test/test_promises.rb +260 -0
- data/scarpe-components/test/test_segmented_app_files.rb +182 -0
- data/{lib/scarpe → spikes}/glibui/widget.rb +2 -2
- data/{lib/scarpe → spikes}/glibui.rb +1 -1
- data/templates/basic_class_template.erb +1 -1
- data/templates/class_template_with_event_bind.erb +1 -1
- data/templates/class_template_with_shapes.erb +1 -1
- data/templates/webview_template.erb +0 -3
- metadata +151 -118
- data/examples/fill.rb +0 -25
- data/examples/legacy/not_checked/shoes-contrib/basic/class-book.yaml +0 -387
- data/examples/legacy/not_checked/shoes-contrib/good/good-clock.rb +0 -51
- data/examples/legacy/not_checked/shoes-contrib/good/good-follow.rb +0 -26
- data/examples/legacy/not_checked/shoes-contrib/good/good-reminder.rb +0 -174
- data/examples/legacy/not_checked/shoes-contrib/good/good-vjot.rb +0 -56
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-timer.rb +0 -13
- data/examples/legacy/not_checked/shoes-dep-samples/good-clock.rb +0 -51
- data/examples/legacy/not_checked/shoes-dep-samples/good-follow.rb +0 -26
- data/examples/legacy/not_checked/shoes-dep-samples/good-reminder.rb +0 -174
- data/examples/legacy/not_checked/shoes-dep-samples/good-vjot.rb +0 -56
- data/examples/legacy/not_checked/shoes-dep-samples/simple-accordion.rb +0 -75
- data/examples/legacy/not_checked/shoes-dep-samples/simple-anim-shapes.rb +0 -17
- data/examples/legacy/not_checked/shoes-dep-samples/simple-anim-text.rb +0 -13
- data/examples/legacy/not_checked/shoes-dep-samples/simple-arc.rb +0 -23
- data/examples/legacy/not_checked/shoes-dep-samples/simple-bounce.rb +0 -24
- data/examples/legacy/not_checked/shoes-dep-samples/simple-calc.rb +0 -70
- data/examples/legacy/not_checked/shoes-dep-samples/simple-chipmunk.rb +0 -26
- data/examples/legacy/not_checked/shoes-dep-samples/simple-control-sizes.rb +0 -24
- data/examples/legacy/not_checked/shoes-dep-samples/simple-curve.rb +0 -26
- data/examples/legacy/not_checked/shoes-dep-samples/simple-dialogs.rb +0 -29
- data/examples/legacy/not_checked/shoes-dep-samples/simple-draw.rb +0 -13
- data/examples/legacy/not_checked/shoes-dep-samples/simple-editor.rb +0 -28
- data/examples/legacy/not_checked/shoes-dep-samples/simple-form.rb +0 -28
- data/examples/legacy/not_checked/shoes-dep-samples/simple-form.shy +0 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-mask.rb +0 -21
- data/examples/legacy/not_checked/shoes-dep-samples/simple-menu.rb +0 -31
- data/examples/legacy/not_checked/shoes-dep-samples/simple-menu1.rb +0 -35
- data/examples/legacy/not_checked/shoes-dep-samples/simple-rubygems.rb +0 -29
- data/examples/legacy/not_checked/shoes-dep-samples/simple-slide.rb +0 -45
- data/examples/legacy/not_checked/shoes-dep-samples/simple-sphere.rb +0 -28
- data/examples/legacy/not_checked/shoes-dep-samples/simple-sqlite3.rb +0 -13
- data/examples/legacy/not_checked/shoes-dep-samples/simple-timer.rb +0 -13
- data/examples/legacy/not_checked/shoes-dep-samples/simple-video.rb +0 -13
- data/examples/legacy/not_checked/simple/anim-text.rb +0 -13
- data/examples/legacy/not_checked/simple/arc.rb +0 -23
- data/examples/legacy/not_checked/simple/bounce.rb +0 -24
- data/examples/legacy/not_checked/simple/chipmunk.rb +0 -26
- data/examples/legacy/not_checked/simple/curve.rb +0 -26
- data/examples/legacy/not_checked/simple/dialogs.rb +0 -29
- data/examples/legacy/not_checked/simple/downloader.rb +0 -40
- data/examples/legacy/not_checked/simple/draw.rb +0 -13
- data/examples/legacy/not_checked/simple/mask.rb +0 -21
- data/examples/legacy/not_checked/simple/slide.rb +0 -45
- data/examples/legacy/not_checked/simple/sphere.rb +0 -28
- data/lib/constants.rb +0 -5
- data/lib/scarpe/app.rb +0 -78
- data/lib/scarpe/document_root.rb +0 -20
- data/lib/scarpe/fill.rb +0 -23
- data/lib/scarpe/flow.rb +0 -19
- data/lib/scarpe/line.rb +0 -25
- data/lib/scarpe/logger.rb +0 -155
- data/lib/scarpe/shape.rb +0 -19
- data/lib/scarpe/spacing.rb +0 -9
- data/lib/scarpe/stack.rb +0 -70
- data/lib/scarpe/text_widget.rb +0 -42
- data/lib/scarpe/unit_test_helpers.rb +0 -163
- data/lib/scarpe/widgets.rb +0 -30
- data/lib/scarpe/wv/fill.rb +0 -30
- data/lib/scarpe/wv/shape_helper.rb +0 -44
- data/scarpe-0.2.0.gem +0 -0
- /data/{lib/scarpe → spikes}/glibui/README.md +0 -0
- /data/{lib/scarpe → spikes}/glibui/alert.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/app.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/background.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/border.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/button.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/dimensions.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/document_root.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/edit_box.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/edit_line.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/flow.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/html.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/image.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/link.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/local_display.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/para.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/spacing.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/stack.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/text_widget.rb +0 -0
- /data/{lib/scarpe → spikes}/libui/alert.rb +0 -0
- /data/{lib/scarpe → spikes}/libui/button.rb +0 -0
- /data/{lib/scarpe → spikes}/libui/colors.rb +0 -0
- /data/{lib/scarpe → spikes}/libui/core.rb +0 -0
- /data/{lib/scarpe → spikes}/libui/flow.rb +0 -0
- /data/{lib/scarpe → spikes}/libui/libui.rb +0 -0
- /data/{lib/scarpe → spikes}/libui/notepad.md +0 -0
- /data/{lib/scarpe → spikes}/libui/para.rb +0 -0
- /data/{lib/scarpe → spikes}/libui/stack.rb +0 -0
data/lib/scarpe/wv/star.rb
CHANGED
|
@@ -1,34 +1,37 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "shape_helper"
|
|
4
|
-
|
|
5
3
|
class Scarpe
|
|
6
4
|
class WebviewStar < Scarpe::WebviewWidget
|
|
7
|
-
include ShapeHelper
|
|
8
5
|
def initialize(properties)
|
|
9
6
|
super(properties)
|
|
10
7
|
end
|
|
11
8
|
|
|
12
9
|
def element(&block)
|
|
10
|
+
fill = @draw_context["fill"]
|
|
11
|
+
stroke = @draw_context["stroke"]
|
|
12
|
+
fill = "black" if fill == ""
|
|
13
|
+
stroke = "black" if stroke == ""
|
|
13
14
|
HTML.render do |h|
|
|
14
15
|
h.div(id: html_id, style: style) do
|
|
15
|
-
h.svg(width: @outer, height: @outer, style: "fill:#{
|
|
16
|
-
h.polygon(points: star_points, style: "stroke:#{
|
|
16
|
+
h.svg(width: @outer, height: @outer, style: "fill:#{fill};") do
|
|
17
|
+
h.polygon(points: star_points, style: "stroke:#{stroke};stroke-width:2")
|
|
17
18
|
end
|
|
18
19
|
block.call(h) if block_given?
|
|
19
20
|
end
|
|
20
21
|
end
|
|
21
22
|
end
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
protected
|
|
24
25
|
|
|
25
26
|
def style
|
|
26
|
-
{
|
|
27
|
+
super.merge({
|
|
27
28
|
width: Dimensions.length(@width),
|
|
28
29
|
height: Dimensions.length(@height),
|
|
29
|
-
}
|
|
30
|
+
})
|
|
30
31
|
end
|
|
31
32
|
|
|
33
|
+
private
|
|
34
|
+
|
|
32
35
|
def star_points
|
|
33
36
|
get_star_points.join(",")
|
|
34
37
|
end
|
|
@@ -56,9 +59,5 @@ class Scarpe
|
|
|
56
59
|
|
|
57
60
|
[outer_x, outer_y, inner_x, inner_y]
|
|
58
61
|
end
|
|
59
|
-
|
|
60
|
-
def stroke_color
|
|
61
|
-
"black"
|
|
62
|
-
end
|
|
63
62
|
end
|
|
64
63
|
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Scarpe::WebviewSubscriptionItem < Scarpe::WebviewWidget
|
|
4
|
+
def initialize(properties)
|
|
5
|
+
super
|
|
6
|
+
|
|
7
|
+
bind(@shoes_api_name) do |*args|
|
|
8
|
+
send_self_event(*args, event_name: @shoes_api_name)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def element
|
|
13
|
+
""
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# This will get called once we know the parent, which is useful for events
|
|
17
|
+
# like hover, where our subscription is likely to depend on what our parent is.
|
|
18
|
+
def set_parent(new_parent)
|
|
19
|
+
super
|
|
20
|
+
|
|
21
|
+
case @shoes_api_name
|
|
22
|
+
when "motion"
|
|
23
|
+
# TODO: what do we do for whole-screen mousemove outside the window?
|
|
24
|
+
# Those should be set on body, which right now doesn't have a widget.
|
|
25
|
+
# TODO: figure out how to handle alt and meta keys - does Shoes3 recognise those?
|
|
26
|
+
new_parent.set_event_callback(
|
|
27
|
+
self,
|
|
28
|
+
"onmousemove",
|
|
29
|
+
handler_js_code(
|
|
30
|
+
@shoes_api_name,
|
|
31
|
+
"arguments[0].x",
|
|
32
|
+
"arguments[0].y",
|
|
33
|
+
"arguments[0].ctrlKey",
|
|
34
|
+
"arguments[0].shiftKey",
|
|
35
|
+
),
|
|
36
|
+
)
|
|
37
|
+
when "hover"
|
|
38
|
+
new_parent.set_event_callback(self, "onmouseenter", handler_js_code(@shoes_api_name))
|
|
39
|
+
when "click"
|
|
40
|
+
new_parent.set_event_callback(self, "onclick", handler_js_code(@shoes_api_name, "arguments[0].button", "arguments[0].x", "arguments[0].y"))
|
|
41
|
+
else
|
|
42
|
+
raise "Unknown Shoes event API: #{@shoes_api_name}!"
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def destroy_self
|
|
47
|
+
@parent.remove_event_callbacks(self)
|
|
48
|
+
super
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Scarpe
|
|
4
|
+
class WebviewVideo < Scarpe::WebviewWidget
|
|
5
|
+
SUPPORTED_FORMATS = {
|
|
6
|
+
"video/mp4" => [".mp4"],
|
|
7
|
+
"video/webp" => [".webp"],
|
|
8
|
+
"video/quicktime" => [".mov"],
|
|
9
|
+
"video/x-matroska" => [".mkv"],
|
|
10
|
+
# Add more formats and their associated file extensions if needed
|
|
11
|
+
}.freeze
|
|
12
|
+
|
|
13
|
+
def initialize(properties)
|
|
14
|
+
@url = properties[:url]
|
|
15
|
+
super
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def element
|
|
19
|
+
HTML.render do |h|
|
|
20
|
+
h.video(id: html_id, style: style, controls: true) do
|
|
21
|
+
supported_formats.each do |format|
|
|
22
|
+
h.source(src: @url, type: format)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def supported_formats
|
|
31
|
+
SUPPORTED_FORMATS.select { |_format, extensions| extensions.include?(File.extname(@url)) }.keys
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -9,12 +9,46 @@ require "cgi"
|
|
|
9
9
|
# use setup-mode callbacks.
|
|
10
10
|
|
|
11
11
|
class Scarpe
|
|
12
|
+
# The Scarpe WebWrangler, for Webview, manages a lot of Webviews quirks. It provides
|
|
13
|
+
# a simpler underlying abstraction for DOMWrangler and the Webview widgets.
|
|
14
|
+
# Webview can be picky - if you send it too many messages, it can crash. If the
|
|
15
|
+
# messages you send it are too large, it can crash. If you don't return control
|
|
16
|
+
# to its event loop, it can crash. It doesn't save references to all event handlers,
|
|
17
|
+
# so if you don't save references to them, garbage collection will cause it to
|
|
18
|
+
# crash.
|
|
19
|
+
#
|
|
20
|
+
# As well, Webview only supports asynchronous JS code evaluation with no value
|
|
21
|
+
# being returned. One of WebWrangler's responsibilities is to make asynchronous
|
|
22
|
+
# JS calls, detect when they return a value or time out, and make the result clear
|
|
23
|
+
# to other Scarpe code.
|
|
24
|
+
#
|
|
25
|
+
# Some Webview API functions will crash on some platforms if called from a
|
|
26
|
+
# background thread. Webview will halt all background threads when it runs its
|
|
27
|
+
# event loop. So it's best to assume no Ruby background threads will be available
|
|
28
|
+
# while Webview is running. If a Ruby app wants ongoing work to occur, that work
|
|
29
|
+
# should be registered via a heartbeat handler on the Webview.
|
|
30
|
+
#
|
|
31
|
+
# A WebWrangler is initially in Setup mode, where the underlying Webview exists
|
|
32
|
+
# but does not yet control the event loop. In Setup mode you can bind JS functions,
|
|
33
|
+
# set up initialization code, but nothing is yet running.
|
|
34
|
+
#
|
|
35
|
+
# Once run() is called on WebWrangler, we will hand control of the event loop to
|
|
36
|
+
# the Webview. This will also stop any background threads in Ruby.
|
|
12
37
|
class WebWrangler
|
|
13
|
-
include
|
|
38
|
+
include Shoes::Log
|
|
14
39
|
|
|
40
|
+
# Whether Webview has been started. Once Webview is running you can't add new
|
|
41
|
+
# Javascript bindings. Until it is running, you can't use eval to run Javascript.
|
|
15
42
|
attr_reader :is_running
|
|
43
|
+
|
|
44
|
+
# Once Webview is marked terminated, it's attempting to shut down. If we get
|
|
45
|
+
# events (e.g. heartbeats) after that, we should ignore them.
|
|
16
46
|
attr_reader :is_terminated
|
|
17
|
-
|
|
47
|
+
|
|
48
|
+
# This is the time between heartbeats in seconds, usually fractional
|
|
49
|
+
attr_reader :heartbeat
|
|
50
|
+
|
|
51
|
+
# A reference to the control_interface that manages internal Scarpe Webview events.
|
|
18
52
|
attr_reader :control_interface
|
|
19
53
|
|
|
20
54
|
# This error indicates a problem when running ConfirmedEval
|
|
@@ -25,7 +59,7 @@ class Scarpe
|
|
|
25
59
|
end
|
|
26
60
|
end
|
|
27
61
|
|
|
28
|
-
#
|
|
62
|
+
# An error running the supplied JS code string in confirmed_eval
|
|
29
63
|
class JSRuntimeError < JSEvalError
|
|
30
64
|
end
|
|
31
65
|
|
|
@@ -37,20 +71,27 @@ class Scarpe
|
|
|
37
71
|
class InternalError < JSEvalError
|
|
38
72
|
end
|
|
39
73
|
|
|
40
|
-
# This is the JS function name for eval results
|
|
74
|
+
# This is the JS function name for eval results (internal-only)
|
|
41
75
|
EVAL_RESULT = "scarpeAsyncEvalResult"
|
|
42
76
|
|
|
43
|
-
# Allow
|
|
77
|
+
# Allow this many seconds for Webview to finish our JS eval before we decide it's not going to
|
|
44
78
|
EVAL_DEFAULT_TIMEOUT = 0.5
|
|
45
79
|
|
|
46
|
-
|
|
80
|
+
# Create a new WebWrangler.
|
|
81
|
+
#
|
|
82
|
+
# @param title [String] window title
|
|
83
|
+
# @param width [Integer] window width in pixels
|
|
84
|
+
# @param height [Integer] window height in pixels
|
|
85
|
+
# @param resizable [Boolean] whether the window should be resizable by the user
|
|
86
|
+
# @param heartbeat [Float] time between heartbeats in seconds
|
|
87
|
+
def initialize(title:, width:, height:, resizable: false, heartbeat: 0.1)
|
|
47
88
|
log_init("WV::WebWrangler")
|
|
48
89
|
|
|
49
90
|
@log.debug("Creating WebWrangler...")
|
|
50
91
|
|
|
51
|
-
# For now, always allow inspect element
|
|
92
|
+
# For now, always allow inspect element, so pass debug: true
|
|
52
93
|
@webview = WebviewRuby::Webview.new debug: true
|
|
53
|
-
@webview =
|
|
94
|
+
@webview = Shoes::LoggedWrapper.new(@webview, "WebviewAPI") if ENV["SCARPE_DEBUG"]
|
|
54
95
|
@init_refs = {} # Inits don't go away so keep a reference to them to prevent GC
|
|
55
96
|
|
|
56
97
|
@title = title
|
|
@@ -59,8 +100,8 @@ class Scarpe
|
|
|
59
100
|
@resizable = resizable
|
|
60
101
|
@heartbeat = heartbeat
|
|
61
102
|
|
|
62
|
-
#
|
|
63
|
-
#
|
|
103
|
+
# JS setInterval uses RPC and is quite expensive. For many periodic operations
|
|
104
|
+
# we can group them under a single heartbeat handler and avoid extra JS calls or RPC.
|
|
64
105
|
@heartbeat_handlers = []
|
|
65
106
|
|
|
66
107
|
# Need to keep track of which WebView Javascript evals are still pending,
|
|
@@ -100,16 +141,28 @@ class Scarpe
|
|
|
100
141
|
|
|
101
142
|
### Setup-mode Callbacks
|
|
102
143
|
|
|
144
|
+
# Bind a Javascript-callable function by name. When JS calls the function,
|
|
145
|
+
# an async message is sent to Ruby via RPC and will eventually cause the
|
|
146
|
+
# block to be called. This method only works in setup mode, before the
|
|
147
|
+
# underlying Webview has been told to run.
|
|
148
|
+
#
|
|
149
|
+
# @param name [String] the Javascript name for the new function
|
|
150
|
+
# @yield The Ruby block to be invoked when JS calls the function
|
|
103
151
|
def bind(name, &block)
|
|
104
152
|
raise "App is running, javascript binding no longer works because it uses WebView init!" if @is_running
|
|
105
153
|
|
|
106
154
|
@webview.bind(name, &block)
|
|
107
155
|
end
|
|
108
156
|
|
|
157
|
+
# Request that this block of code be run initially when the Webview is run.
|
|
158
|
+
# This operates via #init and will not work if Webview is already running.
|
|
159
|
+
#
|
|
160
|
+
# @param name [String] the Javascript name for the init function
|
|
161
|
+
# @yield The Ruby block to be invoked when Webview runs
|
|
109
162
|
def init_code(name, &block)
|
|
110
163
|
raise "App is running, javascript init no longer works!" if @is_running
|
|
111
164
|
|
|
112
|
-
# Save a reference to the init string so that it
|
|
165
|
+
# Save a reference to the init string so that it doesn't get GC'd
|
|
113
166
|
code_str = "#{name}();"
|
|
114
167
|
@init_refs[name] = code_str
|
|
115
168
|
|
|
@@ -118,8 +171,14 @@ class Scarpe
|
|
|
118
171
|
end
|
|
119
172
|
|
|
120
173
|
# Run the specified code periodically, every "interval" seconds.
|
|
121
|
-
# If
|
|
122
|
-
#
|
|
174
|
+
# If interval is unspecified, run per-heartbeat. This avoids extra
|
|
175
|
+
# RPC and Javascript overhead. This may use the #init mechanism,
|
|
176
|
+
# so it should be invoked when the WebWrangler is in setup mode,
|
|
177
|
+
# before the Webview is running.
|
|
178
|
+
#
|
|
179
|
+
# @param name [String] the name of the Javascript init function, if needed
|
|
180
|
+
# @param interval [Float] the duration between invoking this block
|
|
181
|
+
# @yield the Ruby block to invoke periodically
|
|
123
182
|
def periodic_code(name, interval = heartbeat, &block)
|
|
124
183
|
if interval == heartbeat
|
|
125
184
|
@heartbeat_handlers << block
|
|
@@ -143,7 +202,7 @@ class Scarpe
|
|
|
143
202
|
|
|
144
203
|
# Running callbacks
|
|
145
204
|
|
|
146
|
-
# js_eventually is a
|
|
205
|
+
# js_eventually is a native Webview JS evaluation. On syntax error, nothing happens.
|
|
147
206
|
# On runtime error, execution stops at the error with no further
|
|
148
207
|
# effect or notification. This is rarely what you want.
|
|
149
208
|
# The js_eventually code is run asynchronously, returning neither error
|
|
@@ -151,10 +210,13 @@ class Scarpe
|
|
|
151
210
|
#
|
|
152
211
|
# This method does *not* return a promise, and there is no way to track
|
|
153
212
|
# its progress or its success or failure.
|
|
213
|
+
#
|
|
214
|
+
# @param code [String] the Javascript code to attempt to execute
|
|
215
|
+
# @return [void]
|
|
154
216
|
def js_eventually(code)
|
|
155
217
|
raise "WebWrangler isn't running, eval doesn't work!" unless @is_running
|
|
156
218
|
|
|
157
|
-
@log.
|
|
219
|
+
@log.warn "Deprecated: please do NOT use js_eventually, it's basically never what you want!" unless ENV["CI"]
|
|
158
220
|
|
|
159
221
|
@webview.eval(code)
|
|
160
222
|
end
|
|
@@ -163,19 +225,25 @@ class Scarpe
|
|
|
163
225
|
# promise which will be fulfilled or rejected after the JS executes
|
|
164
226
|
# or times out.
|
|
165
227
|
#
|
|
166
|
-
#
|
|
228
|
+
# We *both* care whether the JS has finished after it was
|
|
167
229
|
# scheduled *and* whether it ever got scheduled at all. If it
|
|
168
|
-
# depends on tasks that never fulfill or reject then it
|
|
169
|
-
#
|
|
230
|
+
# depends on tasks that never fulfill or reject then it will
|
|
231
|
+
# raise a timed-out exception.
|
|
170
232
|
#
|
|
171
|
-
# Right now we can't/don't
|
|
172
|
-
# promises. To do that,
|
|
173
|
-
#
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
233
|
+
# Right now we can't/don't pass arguments through from previous fulfilled
|
|
234
|
+
# promises. To do that, you can schedule the JS to run after the
|
|
235
|
+
# other promises succeed.
|
|
236
|
+
#
|
|
237
|
+
# Webview does not allow interacting with a JS eval once it has
|
|
238
|
+
# been scheduled. So there is no way to guarantee that a piece of JS has
|
|
239
|
+
# not executed, or will not execute in the future. A timeout exception
|
|
240
|
+
# only means that WebWrangler will no longer wait for confirmation or
|
|
241
|
+
# fulfill the promise if the JS later completes.
|
|
242
|
+
#
|
|
243
|
+
# @param code [String] the Javascript code to execute
|
|
244
|
+
# @param timeout [Float] how long to allow before raising a timeout exception
|
|
245
|
+
# @param wait_for [Array<Promise>] promises that must complete successfully before this JS is scheduled
|
|
246
|
+
def eval_js_async(code, timeout: EVAL_DEFAULT_TIMEOUT, wait_for: [])
|
|
179
247
|
unless @is_running
|
|
180
248
|
raise "WebWrangler isn't running, so evaluating JS won't work!"
|
|
181
249
|
end
|
|
@@ -192,9 +260,8 @@ class Scarpe
|
|
|
192
260
|
|
|
193
261
|
# We'll need this inside the promise-scheduling block
|
|
194
262
|
pending_evals = @pending_evals
|
|
195
|
-
timeout = opts[:timeout] || EVAL_DEFAULT_TIMEOUT
|
|
196
263
|
|
|
197
|
-
promise = Scarpe::Promise.new(parents:
|
|
264
|
+
promise = Scarpe::Promise.new(parents: wait_for) do
|
|
198
265
|
# Are we mid-shutdown?
|
|
199
266
|
if @webview
|
|
200
267
|
wrapped_code = WebWrangler.js_wrapped_code(code, this_eval_serial)
|
|
@@ -220,6 +287,16 @@ class Scarpe
|
|
|
220
287
|
promise
|
|
221
288
|
end
|
|
222
289
|
|
|
290
|
+
# This method takes a piece of Javascript code and wraps it in the WebWrangler
|
|
291
|
+
# boilerplate to see if it parses successfully, run it, and see if it succeeds.
|
|
292
|
+
# This function would normally be used by testing code, to mock Webview and
|
|
293
|
+
# watch for code being run. Javascript code containing backticks
|
|
294
|
+
# could potentially break this abstraction layer, which would cause the resulting
|
|
295
|
+
# code to fail to parse and Webview would return no error. This should not be
|
|
296
|
+
# used for random or untrusted code.
|
|
297
|
+
#
|
|
298
|
+
# @param code [String] the Javascript code to be wrapped
|
|
299
|
+
# @param eval_id [Integer] the tracking code to use when calling EVAL_RESULT
|
|
223
300
|
def self.js_wrapped_code(code, eval_id)
|
|
224
301
|
<<~JS_CODE
|
|
225
302
|
(function() {
|
|
@@ -268,11 +345,11 @@ class Scarpe
|
|
|
268
345
|
end
|
|
269
346
|
end
|
|
270
347
|
|
|
271
|
-
#
|
|
272
|
-
#
|
|
273
|
-
#
|
|
274
|
-
#
|
|
275
|
-
#
|
|
348
|
+
# @todo would be good to keep 'tombstone' results for awhile after timeout, maybe up to around a minute,
|
|
349
|
+
# so we can detect if we're timing things out and then having them return successfully after a delay.
|
|
350
|
+
# Then we could adjust the timeouts. We could also check if later serial numbers have returned, and time
|
|
351
|
+
# out earlier serial numbers... *if* we're sure Webview will always execute JS evals in order.
|
|
352
|
+
# This all adds complexity, though. For now, do timeouts on a simple max duration.
|
|
276
353
|
def time_out_eval_results
|
|
277
354
|
t_now = Time.now
|
|
278
355
|
timed_out_from_scheduling = @pending_evals.keys.select do |id|
|
|
@@ -304,8 +381,7 @@ class Scarpe
|
|
|
304
381
|
public
|
|
305
382
|
|
|
306
383
|
# After setup, we call run to go to "running" mode.
|
|
307
|
-
# No more setup callbacks, only running callbacks.
|
|
308
|
-
|
|
384
|
+
# No more setup callbacks should be called, only running callbacks.
|
|
309
385
|
def run
|
|
310
386
|
@log.debug("Run...")
|
|
311
387
|
|
|
@@ -329,6 +405,8 @@ class Scarpe
|
|
|
329
405
|
@webview = nil
|
|
330
406
|
end
|
|
331
407
|
|
|
408
|
+
# Request destruction of WebWrangler, including terminating the underlying
|
|
409
|
+
# Webview and (when possible) destroying it.
|
|
332
410
|
def destroy
|
|
333
411
|
@log.debug("Destroying WebWrangler...")
|
|
334
412
|
@log.debug(" (WebWrangler was already terminated)") if @is_terminated
|
|
@@ -389,69 +467,112 @@ class Scarpe
|
|
|
389
467
|
|
|
390
468
|
public
|
|
391
469
|
|
|
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
470
|
# Replace the entire DOM - return a promise for when this has been done.
|
|
397
471
|
# This will often get rid of smaller changes in the queue, which is
|
|
398
472
|
# a good thing since they won't have to be run.
|
|
473
|
+
#
|
|
474
|
+
# @param html_text [String] The new HTML for the new full DOM
|
|
475
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when the update is complete
|
|
399
476
|
def replace(html_text)
|
|
400
477
|
@dom_wrangler.request_replace(html_text)
|
|
401
478
|
end
|
|
402
479
|
|
|
403
480
|
# Request a DOM change - return a promise for when this has been done.
|
|
481
|
+
# If a full replacement (see #replace) is requested, this change may
|
|
482
|
+
# be lost. Only use it for changes that are preserved by a full update.
|
|
483
|
+
#
|
|
484
|
+
# @param js [String] the JS to execute to alter the DOM
|
|
485
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when the update is complete
|
|
404
486
|
def dom_change(js)
|
|
405
487
|
@dom_wrangler.request_change(js)
|
|
406
488
|
end
|
|
407
489
|
|
|
408
490
|
# Return whether the DOM is, right this moment, confirmed to be fully
|
|
409
491
|
# up to date or not.
|
|
492
|
+
#
|
|
493
|
+
# @return [Boolean] true if the window is fully updated, false if changes are pending
|
|
410
494
|
def dom_fully_updated?
|
|
411
495
|
@dom_wrangler.fully_updated?
|
|
412
496
|
end
|
|
413
497
|
|
|
414
498
|
# Return a promise that will be fulfilled when all current DOM changes
|
|
415
|
-
# have committed
|
|
499
|
+
# have committed. If other changes are requested before these
|
|
500
|
+
# complete, the promise will ***not*** wait for them. If you wish to
|
|
501
|
+
# wait until all changes from all sources have completed, use
|
|
502
|
+
# #promise_dom_fully_updated.
|
|
503
|
+
#
|
|
504
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when all current changes complete
|
|
416
505
|
def dom_promise_redraw
|
|
417
506
|
@dom_wrangler.promise_redraw
|
|
418
507
|
end
|
|
419
508
|
|
|
420
509
|
# Return a promise which will be fulfilled the next time the DOM is
|
|
421
|
-
# fully up to date.
|
|
422
|
-
# take a long time, since it
|
|
423
|
-
#
|
|
424
|
-
#
|
|
425
|
-
#
|
|
510
|
+
# fully up to date. A slow trickle of changes can make this
|
|
511
|
+
# take a long time, since it includes all current and future changes,
|
|
512
|
+
# not just changes before this call.
|
|
513
|
+
#
|
|
514
|
+
# If you want to know that some specific individual change is done, it's often
|
|
515
|
+
# easiest to use the promise returned by #dom_change, which will
|
|
516
|
+
# be fulfilled when that specific change is verified complete.
|
|
517
|
+
#
|
|
518
|
+
# If no changes are pending, promise_dom_fully_updated will
|
|
519
|
+
# return a promise that is already fulfilled.
|
|
520
|
+
#
|
|
521
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when all changes are complete
|
|
426
522
|
def promise_dom_fully_updated
|
|
427
523
|
@dom_wrangler.promise_fully_updated
|
|
428
524
|
end
|
|
429
525
|
|
|
526
|
+
# DOMWrangler will frequently schedule and confirm small JS updates.
|
|
527
|
+
# A handler registered with on_every_redraw will be called after each
|
|
528
|
+
# small update.
|
|
529
|
+
#
|
|
530
|
+
# @yield Called after each update or batch of updates is verified complete
|
|
531
|
+
# @return [void]
|
|
430
532
|
def on_every_redraw(&block)
|
|
431
533
|
@dom_wrangler.on_every_redraw(&block)
|
|
432
534
|
end
|
|
433
535
|
end
|
|
434
536
|
end
|
|
435
537
|
|
|
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
538
|
class Scarpe
|
|
446
539
|
class WebWrangler
|
|
540
|
+
# Leaving DOM changes as "meh, async, we'll see when it happens" is terrible for testing.
|
|
541
|
+
# Instead, we need to track whether particular changes have committed yet or not.
|
|
542
|
+
# So we add a single gateway for all DOM changes, and we make sure its work is done
|
|
543
|
+
# before we consider a redraw complete.
|
|
544
|
+
#
|
|
545
|
+
# DOMWrangler batches up changes into fewer RPC calls. It's fine to have a redraw
|
|
546
|
+
# "in flight" and have changes waiting to catch the next bus. But we don't want more
|
|
547
|
+
# than one in flight, since it seems like having too many pending RPC requests can
|
|
548
|
+
# crash Webview. So we allow one redraw scheduled and one redraw promise waiting,
|
|
549
|
+
# at maximum.
|
|
550
|
+
#
|
|
551
|
+
# A WebWrangler will create and wrap a DOMWrangler, serving as the interface
|
|
552
|
+
# for all DOM operations.
|
|
553
|
+
#
|
|
554
|
+
# A batch of DOMWrangler changes may be removed if a full update is scheduled. That
|
|
555
|
+
# update is considered to replace the previous incremental changes. Any changes that
|
|
556
|
+
# need to execute even if a full update happens should be scheduled through
|
|
557
|
+
# WebWrangler#eval_js_async, not DOMWrangler.
|
|
447
558
|
class DOMWrangler
|
|
448
|
-
include
|
|
559
|
+
include Shoes::Log
|
|
449
560
|
|
|
561
|
+
# Changes that have not yet been executed
|
|
450
562
|
attr_reader :waiting_changes
|
|
563
|
+
|
|
564
|
+
# A Scarpe::Promise for JS that has been scheduled to execute but is not yet verified complete
|
|
451
565
|
attr_reader :pending_redraw_promise
|
|
566
|
+
|
|
567
|
+
# A Scarpe::Promise for waiting changes - it will be fulfilled when all waiting changes
|
|
568
|
+
# have been verified complete, or when a full redraw that removed them has been
|
|
569
|
+
# verified complete. If many small changes are scheduled, the same promise will be
|
|
570
|
+
# returned for many of them.
|
|
452
571
|
attr_reader :waiting_redraw_promise
|
|
453
572
|
|
|
454
|
-
|
|
573
|
+
# Create a DOMWrangler that is paired with a WebWrangler. The WebWrangler is
|
|
574
|
+
# treated as an underlying abstraction for reliable JS evaluation.
|
|
575
|
+
def initialize(web_wrangler)
|
|
455
576
|
log_init("WV::WebWrangler::DOMWrangler")
|
|
456
577
|
|
|
457
578
|
@wrangler = web_wrangler
|
|
@@ -510,6 +631,10 @@ class Scarpe
|
|
|
510
631
|
@redraw_handlers << block
|
|
511
632
|
end
|
|
512
633
|
|
|
634
|
+
# promise_redraw returns a Scarpe::Promise which will be fulfilled after all current
|
|
635
|
+
# pending or waiting changes have completed. This may require creating a new
|
|
636
|
+
# promise.
|
|
637
|
+
#
|
|
513
638
|
# What are the states of redraw?
|
|
514
639
|
# "empty" - no waiting promise, no pending-redraw promise, no pending changes
|
|
515
640
|
# "pending only" - no waiting promise, but we have a pending redraw with some changes; it hasn't committed yet
|
|
@@ -643,37 +768,92 @@ class Scarpe
|
|
|
643
768
|
end
|
|
644
769
|
end
|
|
645
770
|
|
|
646
|
-
# For now we don't need one of these to add DOM elements, just to manipulate them
|
|
647
|
-
# after initial render.
|
|
648
771
|
class Scarpe
|
|
649
772
|
class WebWrangler
|
|
773
|
+
# An ElementWrangler provides a way for a Widget to manipulate is DOM element(s)
|
|
774
|
+
# via their HTML IDs. The most straightforward Widgets can have a single HTML ID
|
|
775
|
+
# and use a single ElementWrangler to make any needed changes.
|
|
776
|
+
#
|
|
777
|
+
# For now we don't need an ElementWrangler to add DOM elements, just to manipulate them
|
|
778
|
+
# after initial render. New DOM objects for Widgets are normally added via full
|
|
779
|
+
# redraws rather than incremental updates.
|
|
780
|
+
#
|
|
781
|
+
# Any changes made via ElementWrangler may be cancelled if a full redraw occurs,
|
|
782
|
+
# since it is assumed that small DOM manipulations are no longer needed. If a
|
|
783
|
+
# change would need to be made even if a full redraw occurred, it should be
|
|
784
|
+
# scheduled via WebWrangler#eval_js_async, not via an ElementWrangler.
|
|
650
785
|
class ElementWrangler
|
|
651
786
|
attr_reader :html_id
|
|
652
787
|
|
|
788
|
+
# Create an ElementWrangler for the given HTML ID
|
|
789
|
+
#
|
|
790
|
+
# @param html_id [String] the HTML ID for the DOM element
|
|
653
791
|
def initialize(html_id)
|
|
654
792
|
@webwrangler = WebviewDisplayService.instance.wrangler
|
|
655
793
|
@html_id = html_id
|
|
656
794
|
end
|
|
657
795
|
|
|
796
|
+
# Return a promise that will be fulfilled when all changes scheduled via
|
|
797
|
+
# this ElementWrangler are verified complete.
|
|
798
|
+
#
|
|
799
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when scheduled changes are complete
|
|
658
800
|
def promise_update
|
|
659
801
|
@webwrangler.dom_promise_redraw
|
|
660
802
|
end
|
|
661
803
|
|
|
804
|
+
# Update the JS DOM element's value. The given Ruby value will be converted to string and assigned in backquotes.
|
|
805
|
+
#
|
|
806
|
+
# @param new_value [String] the new value
|
|
807
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete
|
|
662
808
|
def value=(new_value)
|
|
663
809
|
@webwrangler.dom_change("document.getElementById('" + html_id + "').value = `" + new_value + "`; true")
|
|
664
810
|
end
|
|
665
811
|
|
|
812
|
+
# Update the JS DOM element's inner_text. The given Ruby value will be converted to string and assigned in single-quotes.
|
|
813
|
+
#
|
|
814
|
+
# @param new_text [String] the new inner_text
|
|
815
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete
|
|
666
816
|
def inner_text=(new_text)
|
|
667
817
|
@webwrangler.dom_change("document.getElementById('" + html_id + "').innerText = '" + new_text + "'; true")
|
|
668
818
|
end
|
|
669
819
|
|
|
820
|
+
# Update the JS DOM element's inner_html. The given Ruby value will be converted to string and assigned in backquotes.
|
|
821
|
+
#
|
|
822
|
+
# @param new_html [String] the new inner_html
|
|
823
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete
|
|
670
824
|
def inner_html=(new_html)
|
|
671
825
|
@webwrangler.dom_change("document.getElementById(\"" + html_id + "\").innerHTML = `" + new_html + "`; true")
|
|
672
826
|
end
|
|
673
827
|
|
|
828
|
+
# Update the JS DOM element's inner_html. The given Ruby value will be inspected and assigned.
|
|
829
|
+
#
|
|
830
|
+
# @param attribute [String] the attribute name
|
|
831
|
+
# @param value [String] the new attribute value
|
|
832
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete
|
|
833
|
+
def set_attribute(attribute, value)
|
|
834
|
+
@webwrangler.dom_change("document.getElementById(\"" + html_id + "\").setAttribute(" + attribute.inspect + "," + value.inspect + "); true")
|
|
835
|
+
end
|
|
836
|
+
|
|
837
|
+
# Update an attribute of the JS DOM element's style. The given Ruby value will be inspected and assigned.
|
|
838
|
+
#
|
|
839
|
+
# @param style_attr [String] the style attribute name
|
|
840
|
+
# @param value [String] the new style attribute value
|
|
841
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete
|
|
842
|
+
def set_style(style_attr, value)
|
|
843
|
+
@webwrangler.dom_change("document.getElementById(\"" + html_id + "\").style.#{style_attr} = " + value.inspect + "; true")
|
|
844
|
+
end
|
|
845
|
+
|
|
846
|
+
# Remove the specified DOM element
|
|
847
|
+
#
|
|
848
|
+
# @return [Scarpe::Promise] a promise that wil be fulfilled when the element is removed
|
|
674
849
|
def remove
|
|
675
850
|
@webwrangler.dom_change("document.getElementById('" + html_id + "').remove(); true")
|
|
676
851
|
end
|
|
852
|
+
|
|
853
|
+
def toggle_input_button(mark)
|
|
854
|
+
checked_value = mark ? "true" : "false"
|
|
855
|
+
@webwrangler.dom_change("document.getElementById('#{html_id}').checked = #{checked_value};")
|
|
856
|
+
end
|
|
677
857
|
end
|
|
678
858
|
end
|
|
679
859
|
end
|