scarpe 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|