scarpe 0.2.2 → 0.3.0
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/.yardopts +1 -0
- data/CHANGELOG.md +16 -2
- data/Gemfile.lock +7 -3
- data/README.md +24 -8
- data/Rakefile +1 -1
- data/examples/animate.rb +20 -0
- data/examples/arrow.rb +10 -0
- data/examples/btn_tooltip.rb +7 -0
- data/examples/button_style_changed.rb +7 -0
- data/examples/button_styles_default.rb +6 -0
- data/examples/gen.rb +8 -8
- data/examples/highlander.rb +3 -3
- data/examples/legacy/README.md +6 -0
- data/examples/legacy/not_checked/shoes-contrib/basic/shoes-notes.rb +1 -1
- data/examples/legacy/not_checked/simple/anim-shapes.rb +1 -1
- data/examples/legacy/not_checked/speedometer_app.rb +55 -0
- data/examples/legacy/working/simple/image-icon.rb +3 -0
- data/examples/legacy/{not_checked → working}/simple/image.rb +1 -1
- data/examples/list_box_choose.rb +17 -0
- data/examples/local_assets/local_file_server.rb +82 -0
- data/examples/local_assets/sample.gif +0 -0
- data/examples/local_assets/sample.mp4 +0 -0
- data/examples/local_fonts.rb +2 -2
- data/examples/local_images.rb +2 -3
- data/examples/para/para_text.rb +14 -0
- data/examples/progress.rb +31 -0
- data/examples/radio/radio_groups.rb +2 -2
- data/examples/rect.rb +4 -0
- data/examples/rotate_shapes.rb +17 -0
- data/examples/simpler-menu.rb +21 -0
- data/exe/scarpe +2 -1
- data/lacci/Gemfile +2 -0
- data/lacci/Gemfile.lock +8 -1
- data/lacci/lacci.gemspec +1 -1
- data/lacci/lib/lacci/scarpe_cli.rb +2 -1
- data/lacci/lib/lacci/scarpe_core.rb +2 -1
- data/lacci/lib/lacci/version.rb +1 -1
- data/lacci/lib/scarpe/niente/app.rb +23 -0
- data/lacci/lib/scarpe/niente/display_service.rb +62 -0
- data/lacci/lib/scarpe/niente/drawable.rb +57 -0
- data/lacci/lib/scarpe/niente/logger.rb +29 -0
- data/lacci/lib/scarpe/niente/shoes_spec.rb +87 -0
- data/lacci/lib/scarpe/niente.rb +20 -0
- data/lacci/lib/shoes/app.rb +88 -43
- data/lacci/lib/shoes/background.rb +2 -2
- data/lacci/lib/shoes/border.rb +2 -2
- data/lacci/lib/shoes/builtins.rb +63 -0
- data/lacci/lib/shoes/changelog.rb +52 -0
- data/lacci/lib/shoes/colors.rb +3 -1
- data/lacci/lib/shoes/constants.rb +19 -1
- data/lacci/lib/shoes/display_service.rb +39 -16
- data/lacci/lib/shoes/download.rb +2 -2
- data/lacci/lib/shoes/drawable.rb +380 -0
- data/lacci/lib/shoes/drawables/arc.rb +49 -0
- data/lacci/lib/shoes/drawables/arrow.rb +41 -0
- data/lacci/lib/shoes/drawables/button.rb +73 -0
- data/lacci/lib/shoes/{widgets → drawables}/check.rb +5 -4
- data/lacci/lib/shoes/{widgets → drawables}/document_root.rb +3 -3
- data/lacci/lib/shoes/{widgets → drawables}/edit_box.rb +6 -6
- data/lacci/lib/shoes/{widgets → drawables}/edit_line.rb +6 -6
- data/lacci/lib/shoes/{widgets → drawables}/flow.rb +6 -6
- data/lacci/lib/shoes/{widgets → drawables}/image.rb +6 -6
- data/lacci/lib/shoes/{widgets → drawables}/line.rb +7 -5
- data/lacci/lib/shoes/drawables/link.rb +34 -0
- data/lacci/lib/shoes/drawables/list_box.rb +56 -0
- data/lacci/lib/shoes/drawables/para.rb +118 -0
- data/lacci/lib/shoes/drawables/progress.rb +14 -0
- data/lacci/lib/shoes/drawables/radio.rb +33 -0
- data/lacci/lib/shoes/drawables/rect.rb +17 -0
- data/lacci/lib/shoes/{widgets → drawables}/shape.rb +6 -7
- data/lacci/lib/shoes/{widgets → drawables}/slot.rb +32 -20
- data/lacci/lib/shoes/{widgets → drawables}/span.rb +8 -7
- data/lacci/lib/shoes/{widgets → drawables}/stack.rb +6 -4
- data/lacci/lib/shoes/drawables/star.rb +50 -0
- data/lacci/lib/shoes/drawables/subscription_item.rb +93 -0
- data/lacci/lib/shoes/drawables/text_drawable.rb +63 -0
- data/lacci/lib/shoes/drawables/video.rb +16 -0
- data/lacci/lib/shoes/drawables/widget.rb +69 -0
- data/lacci/lib/shoes/drawables.rb +31 -0
- data/lacci/lib/shoes/errors.rb +28 -0
- data/lacci/lib/shoes/log.rb +2 -2
- data/lacci/lib/shoes/ruby_extensions.rb +15 -0
- data/lacci/lib/shoes/spacing.rb +2 -2
- data/lacci/lib/shoes-spec.rb +93 -0
- data/lacci/lib/shoes.rb +27 -7
- data/lacci/test/test_helper.rb +54 -0
- data/lacci/test/test_lacci.rb +12 -3
- data/lacci/test/test_shoes_errors.rb +49 -0
- data/lib/scarpe/cats_cradle.rb +81 -59
- data/lib/scarpe/errors.rb +77 -0
- data/lib/scarpe/evented_assertions.rb +50 -17
- data/lib/scarpe/shoes_spec.rb +181 -0
- data/lib/scarpe/version.rb +2 -2
- data/lib/scarpe/wv/app.rb +20 -20
- data/lib/scarpe/wv/arc.rb +4 -47
- data/lib/scarpe/wv/arrow.rb +9 -0
- data/lib/scarpe/wv/button.rb +7 -35
- data/lib/scarpe/wv/check.rb +3 -5
- data/lib/scarpe/wv/control_interface.rb +18 -20
- data/lib/scarpe/wv/document_root.rb +81 -4
- data/lib/scarpe/wv/{widget.rb → drawable.rb} +66 -43
- data/lib/scarpe/wv/edit_box.rb +4 -17
- data/lib/scarpe/wv/edit_line.rb +4 -18
- data/lib/scarpe/wv/flow.rb +2 -18
- data/lib/scarpe/wv/image.rb +8 -28
- data/lib/scarpe/wv/line.rb +3 -25
- data/lib/scarpe/wv/link.rb +3 -16
- data/lib/scarpe/wv/list_box.rb +6 -29
- data/lib/scarpe/wv/para.rb +11 -30
- data/lib/scarpe/wv/progress.rb +19 -0
- data/lib/scarpe/wv/radio.rb +9 -10
- data/lib/scarpe/wv/rect.rb +13 -0
- data/lib/scarpe/wv/shape.rb +3 -8
- data/lib/scarpe/wv/slot.rb +8 -25
- data/lib/scarpe/wv/span.rb +3 -27
- data/lib/scarpe/wv/stack.rb +2 -18
- data/lib/scarpe/wv/star.rb +3 -53
- data/lib/scarpe/wv/subscription_item.rb +38 -4
- data/lib/scarpe/wv/text_drawable.rb +32 -0
- data/lib/scarpe/wv/video.rb +15 -15
- data/lib/scarpe/wv/web_wrangler.rb +299 -329
- data/lib/scarpe/wv/webview_local_display.rb +48 -33
- data/lib/scarpe/wv/webview_relay_display.rb +12 -12
- data/lib/scarpe/wv/webview_relay_util.rb +7 -10
- data/lib/scarpe/wv/wv_display_worker.rb +2 -2
- data/lib/scarpe/wv.rb +45 -12
- data/lib/scarpe/wv_local.rb +1 -1
- data/lib/scarpe/wv_relay.rb +1 -1
- data/lib/scarpe.rb +1 -0
- data/logger/debug_web_wrangler.json +1 -1
- data/logger/scarpe_wv_test.json +1 -1
- data/scarpe-components/Gemfile.lock +86 -0
- data/scarpe-components/lib/scarpe/components/base64.rb +3 -7
- data/scarpe-components/lib/scarpe/components/calzini/alert.rb +49 -0
- data/scarpe-components/lib/scarpe/components/calzini/art_widgets.rb +203 -0
- data/scarpe-components/lib/scarpe/components/calzini/button.rb +39 -0
- data/scarpe-components/lib/scarpe/components/calzini/misc.rb +146 -0
- data/scarpe-components/lib/scarpe/components/calzini/para.rb +35 -0
- data/scarpe-components/lib/scarpe/components/calzini/slots.rb +155 -0
- data/scarpe-components/lib/scarpe/components/calzini/text_widgets.rb +65 -0
- data/scarpe-components/lib/scarpe/components/calzini.rb +149 -0
- data/scarpe-components/lib/scarpe/components/errors.rb +20 -0
- data/scarpe-components/lib/scarpe/components/file_helpers.rb +1 -0
- data/scarpe-components/lib/scarpe/components/html.rb +131 -0
- data/scarpe-components/lib/scarpe/components/minitest_export_reporter.rb +75 -0
- data/scarpe-components/lib/scarpe/components/minitest_import_runnable.rb +98 -0
- data/scarpe-components/lib/scarpe/components/minitest_result.rb +86 -0
- data/scarpe-components/lib/scarpe/components/modular_logger.rb +5 -5
- data/scarpe-components/lib/scarpe/components/print_logger.rb +9 -5
- data/scarpe-components/lib/scarpe/components/promises.rb +14 -14
- data/scarpe-components/lib/scarpe/components/segmented_file_loader.rb +36 -17
- data/scarpe-components/lib/scarpe/components/string_helpers.rb +10 -0
- data/scarpe-components/lib/scarpe/components/tiranti.rb +225 -0
- data/scarpe-components/lib/scarpe/components/unit_test_helpers.rb +45 -5
- data/scarpe-components/lib/scarpe/components/version.rb +2 -2
- data/scarpe-components/test/calzini/test_calzini_alert.rb +30 -0
- data/scarpe-components/test/calzini/test_calzini_art_drawables.rb +105 -0
- data/scarpe-components/test/calzini/test_calzini_button.rb +52 -0
- data/scarpe-components/test/calzini/test_calzini_misc.rb +115 -0
- data/scarpe-components/test/calzini/test_calzini_para.rb +37 -0
- data/scarpe-components/test/calzini/test_calzini_slots.rb +130 -0
- data/scarpe-components/test/calzini/test_calzini_text_drawables.rb +41 -0
- data/scarpe-components/test/mtr_data/exception.json +1 -0
- data/scarpe-components/test/mtr_data/fail_with_message.json +1 -0
- data/scarpe-components/test/mtr_data/skipped_no_message.json +1 -0
- data/scarpe-components/test/mtr_data/skipped_w_msg.json +1 -0
- data/scarpe-components/test/mtr_data/succeed_2_asserts.json +1 -0
- data/scarpe-components/test/test_dimensions.rb +26 -0
- data/scarpe-components/test/test_helper.rb +20 -0
- data/scarpe-components/test/test_html.rb +65 -0
- data/scarpe-components/test/test_minitest_result.rb +61 -0
- data/scarpe-components/test/test_promises.rb +5 -4
- data/scarpe-components/test/test_segmented_app_files.rb +8 -6
- data/scarpegen.rb +14 -14
- data/sig/scarpe.rbs +1 -1
- data/templates/basic_class_template.erb +13 -14
- data/templates/class_template_with_event_bind.erb +4 -4
- data/templates/class_template_with_shapes.erb +8 -17
- data/templates/example_template.erb +1 -1
- data/templates/module_template.erb +4 -4
- data/templates/webview_template.erb +3 -2
- metadata +113 -55
- data/examples/legacy/not_checked/shoes-contrib/elements/image-icon.rb +0 -3
- data/lacci/lib/shoes/widget.rb +0 -218
- data/lacci/lib/shoes/widgets/alert.rb +0 -19
- data/lacci/lib/shoes/widgets/arc.rb +0 -51
- data/lacci/lib/shoes/widgets/button.rb +0 -35
- data/lacci/lib/shoes/widgets/font.rb +0 -14
- data/lacci/lib/shoes/widgets/link.rb +0 -25
- data/lacci/lib/shoes/widgets/list_box.rb +0 -25
- data/lacci/lib/shoes/widgets/para.rb +0 -68
- data/lacci/lib/shoes/widgets/radio.rb +0 -35
- data/lacci/lib/shoes/widgets/star.rb +0 -44
- data/lacci/lib/shoes/widgets/subscription_item.rb +0 -60
- data/lacci/lib/shoes/widgets/text_widget.rb +0 -51
- data/lacci/lib/shoes/widgets/video.rb +0 -15
- data/lacci/lib/shoes/widgets.rb +0 -29
- data/lib/scarpe/wv/alert.rb +0 -66
- data/lib/scarpe/wv/background.rb +0 -27
- data/lib/scarpe/wv/border.rb +0 -24
- data/lib/scarpe/wv/control_interface_test.rb +0 -238
- data/lib/scarpe/wv/dimensions.rb +0 -22
- data/lib/scarpe/wv/font.rb +0 -36
- data/lib/scarpe/wv/html.rb +0 -108
- data/lib/scarpe/wv/spacing.rb +0 -41
- data/lib/scarpe/wv/text_widget.rb +0 -30
- /data/examples/legacy/not_checked/{expert → shoes-contrib/basic}/definr.rb +0 -0
- /data/examples/legacy/not_checked/{expert → shoes-contrib/basic}/funnies.rb +0 -0
- /data/examples/legacy/not_checked/shoes-contrib/{elements → basic}/list_box-select-class.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/basic → working/simple}/basic-edit-box.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/elements → working/simple}/basic-fps.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/elements → working/simple}/border-cat.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/elements → working/simple}/check-mate.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/manipulation → working/simple}/clear-slot.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/basic → working/simple}/clock.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/basic → working/simple}/gradient-shoes.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/basic → working/simple}/list_box-shape-report.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/elements → working/simple}/list_box.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/elements → working/simple}/phat-button.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib → working}/simple/simple-calc.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/position → working/simple}/stack-width.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/elements → working/simple}/width-introspec.rb +0 -0
|
@@ -8,9 +8,9 @@ require "cgi"
|
|
|
8
8
|
# After creation, it starts in setup mode, and you can
|
|
9
9
|
# use setup-mode callbacks.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
module Scarpe::Webview
|
|
12
12
|
# The Scarpe WebWrangler, for Webview, manages a lot of Webviews quirks. It provides
|
|
13
|
-
# a simpler underlying abstraction for DOMWrangler and the Webview
|
|
13
|
+
# a simpler underlying abstraction for DOMWrangler and the Webview drawables.
|
|
14
14
|
# Webview can be picky - if you send it too many messages, it can crash. If the
|
|
15
15
|
# messages you send it are too large, it can crash. If you don't return control
|
|
16
16
|
# to its event loop, it can crash. It doesn't save references to all event handlers,
|
|
@@ -51,26 +51,6 @@ class Scarpe
|
|
|
51
51
|
# A reference to the control_interface that manages internal Scarpe Webview events.
|
|
52
52
|
attr_reader :control_interface
|
|
53
53
|
|
|
54
|
-
# This error indicates a problem when running ConfirmedEval
|
|
55
|
-
class JSEvalError < Scarpe::Error
|
|
56
|
-
def initialize(data)
|
|
57
|
-
@data = data
|
|
58
|
-
super(data[:msg] || (self.class.name + "!"))
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# An error running the supplied JS code string in confirmed_eval
|
|
63
|
-
class JSRuntimeError < JSEvalError
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# The code timed out for some reason
|
|
67
|
-
class JSTimeoutError < JSEvalError
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# We got weird or nonsensical results that seem like an error on WebWrangler's part
|
|
71
|
-
class InternalError < JSEvalError
|
|
72
|
-
end
|
|
73
|
-
|
|
74
54
|
# This is the JS function name for eval results (internal-only)
|
|
75
55
|
EVAL_RESULT = "scarpeAsyncEvalResult"
|
|
76
56
|
|
|
@@ -85,7 +65,7 @@ class Scarpe
|
|
|
85
65
|
# @param resizable [Boolean] whether the window should be resizable by the user
|
|
86
66
|
# @param heartbeat [Float] time between heartbeats in seconds
|
|
87
67
|
def initialize(title:, width:, height:, resizable: false, heartbeat: 0.1)
|
|
88
|
-
log_init("
|
|
68
|
+
log_init("Webview::WebWrangler")
|
|
89
69
|
|
|
90
70
|
@log.debug("Creating WebWrangler...")
|
|
91
71
|
|
|
@@ -149,7 +129,7 @@ class Scarpe
|
|
|
149
129
|
# @param name [String] the Javascript name for the new function
|
|
150
130
|
# @yield The Ruby block to be invoked when JS calls the function
|
|
151
131
|
def bind(name, &block)
|
|
152
|
-
raise "App is running, javascript binding no longer works because it uses WebView init!" if @is_running
|
|
132
|
+
raise Scarpe::JSBindingError, "App is running, javascript binding no longer works because it uses WebView init!" if @is_running
|
|
153
133
|
|
|
154
134
|
@webview.bind(name, &block)
|
|
155
135
|
end
|
|
@@ -160,7 +140,7 @@ class Scarpe
|
|
|
160
140
|
# @param name [String] the Javascript name for the init function
|
|
161
141
|
# @yield The Ruby block to be invoked when Webview runs
|
|
162
142
|
def init_code(name, &block)
|
|
163
|
-
raise "App is running, javascript init no longer works!" if @is_running
|
|
143
|
+
raise Scarpe::JSInitError, "App is running, javascript init no longer works!" if @is_running
|
|
164
144
|
|
|
165
145
|
# Save a reference to the init string so that it doesn't get GC'd
|
|
166
146
|
code_str = "#{name}();"
|
|
@@ -176,6 +156,8 @@ class Scarpe
|
|
|
176
156
|
# so it should be invoked when the WebWrangler is in setup mode,
|
|
177
157
|
# before the Webview is running.
|
|
178
158
|
#
|
|
159
|
+
# TODO: add a way to stop this loop and unsubscribe.
|
|
160
|
+
#
|
|
179
161
|
# @param name [String] the name of the Javascript init function, if needed
|
|
180
162
|
# @param interval [Float] the duration between invoking this block
|
|
181
163
|
# @yield the Ruby block to invoke periodically
|
|
@@ -188,7 +170,7 @@ class Scarpe
|
|
|
188
170
|
# new window. But will there ever be a new page/window? Can we just
|
|
189
171
|
# use eval instead of init to set up a periodic handler and call it
|
|
190
172
|
# good?
|
|
191
|
-
raise "App is running, can't set up new periodic handlers with init!"
|
|
173
|
+
raise Scarpe::PeriodicHandlerSetupError, "App is running, can't set up new periodic handlers with init!"
|
|
192
174
|
end
|
|
193
175
|
|
|
194
176
|
js_interval = (interval.to_f * 1_000.0).to_i
|
|
@@ -214,7 +196,7 @@ class Scarpe
|
|
|
214
196
|
# @param code [String] the Javascript code to attempt to execute
|
|
215
197
|
# @return [void]
|
|
216
198
|
def js_eventually(code)
|
|
217
|
-
raise "WebWrangler isn't running, eval doesn't work!" unless @is_running
|
|
199
|
+
raise Scarpe::WebWranglerNotRunningError, "WebWrangler isn't running, eval doesn't work!" unless @is_running
|
|
218
200
|
|
|
219
201
|
@log.warn "Deprecated: please do NOT use js_eventually, it's basically never what you want!" unless ENV["CI"]
|
|
220
202
|
|
|
@@ -242,10 +224,10 @@ class Scarpe
|
|
|
242
224
|
#
|
|
243
225
|
# @param code [String] the Javascript code to execute
|
|
244
226
|
# @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
|
|
227
|
+
# @param wait_for [Array<Scarpe::Promise>] promises that must complete successfully before this JS is scheduled
|
|
246
228
|
def eval_js_async(code, timeout: EVAL_DEFAULT_TIMEOUT, wait_for: [])
|
|
247
229
|
unless @is_running
|
|
248
|
-
raise "WebWrangler isn't running, so evaluating JS won't work!"
|
|
230
|
+
raise Scarpe::WebWranglerNotRunningError, "WebWrangler isn't running, so evaluating JS won't work!"
|
|
249
231
|
end
|
|
250
232
|
|
|
251
233
|
this_eval_serial = @eval_counter
|
|
@@ -320,7 +302,7 @@ class Scarpe
|
|
|
320
302
|
def receive_eval_result(r_type, id, val)
|
|
321
303
|
entry = @pending_evals.delete(id)
|
|
322
304
|
unless entry
|
|
323
|
-
raise "Received an eval result for a nonexistent ID #{id.inspect}!"
|
|
305
|
+
raise Scarpe::NonexistentEvalResultError, "Received an eval result for a nonexistent ID #{id.inspect}!"
|
|
324
306
|
end
|
|
325
307
|
|
|
326
308
|
@log.debug("Got JS value: #{r_type} / #{id} / #{val.inspect}")
|
|
@@ -331,13 +313,13 @@ class Scarpe
|
|
|
331
313
|
when "success"
|
|
332
314
|
promise.fulfilled!(val)
|
|
333
315
|
when "error"
|
|
334
|
-
promise.rejected! JSRuntimeError.new(
|
|
316
|
+
promise.rejected! Scarpe::JSRuntimeError.new(
|
|
335
317
|
msg: "JS runtime error: #{val.inspect}!",
|
|
336
318
|
code: entry[:code],
|
|
337
319
|
ret_value: val,
|
|
338
320
|
)
|
|
339
321
|
else
|
|
340
|
-
promise.rejected!
|
|
322
|
+
promise.rejected! Scarpe::JSInternalError.new(
|
|
341
323
|
msg: "JS eval internal error! r_type: #{r_type.inspect}",
|
|
342
324
|
code: entry[:code],
|
|
343
325
|
ret_value: val,
|
|
@@ -373,13 +355,15 @@ class Scarpe
|
|
|
373
355
|
timed_out_ids.each do |id|
|
|
374
356
|
@log.error "Timing out JS eval! #{@pending_evals[id][:code]}"
|
|
375
357
|
entry = @pending_evals.delete(id)
|
|
376
|
-
err = JSTimeoutError.new(msg: "JS timeout error!", code: entry[:code], ret_value: nil)
|
|
358
|
+
err = Scarpe::JSTimeoutError.new(msg: "JS timeout error!", code: entry[:code], ret_value: nil)
|
|
377
359
|
entry[:promise].rejected!(err)
|
|
378
360
|
end
|
|
379
361
|
end
|
|
380
362
|
|
|
381
363
|
public
|
|
382
364
|
|
|
365
|
+
attr_writer :empty_page
|
|
366
|
+
|
|
383
367
|
# After setup, we call run to go to "running" mode.
|
|
384
368
|
# No more setup callbacks should be called, only running callbacks.
|
|
385
369
|
def run
|
|
@@ -387,14 +371,18 @@ class Scarpe
|
|
|
387
371
|
|
|
388
372
|
# From webview:
|
|
389
373
|
# 0 - Width and height are default size
|
|
390
|
-
# 1 - Width and height are minimum
|
|
391
|
-
# 2 - Width and height are maximum
|
|
374
|
+
# 1 - Width and height are minimum bounds
|
|
375
|
+
# 2 - Width and height are maximum bounds
|
|
392
376
|
# 3 - Window size can not be changed by a user
|
|
393
377
|
hint = @resizable ? 0 : 3
|
|
394
378
|
|
|
395
379
|
@webview.set_title(@title)
|
|
396
380
|
@webview.set_size(@width, @height, hint)
|
|
397
|
-
@
|
|
381
|
+
unless @empty_page
|
|
382
|
+
raise Scarpe::EmptyPageNotSetError, "No empty page markup was set!"
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
@webview.navigate("data:text/html, #{CGI.escape @empty_page}")
|
|
398
386
|
|
|
399
387
|
monkey_patch_console(@webview)
|
|
400
388
|
|
|
@@ -440,29 +428,7 @@ class Scarpe
|
|
|
440
428
|
end
|
|
441
429
|
|
|
442
430
|
def empty
|
|
443
|
-
|
|
444
|
-
<html>
|
|
445
|
-
<head id='head-wvroot'>
|
|
446
|
-
<style id='style-wvroot'>
|
|
447
|
-
/** Style resets **/
|
|
448
|
-
body {
|
|
449
|
-
font-family: arial, Helvetica, sans-serif;
|
|
450
|
-
margin: 0;
|
|
451
|
-
height: 100%;
|
|
452
|
-
overflow: hidden;
|
|
453
|
-
}
|
|
454
|
-
p {
|
|
455
|
-
margin: 0;
|
|
456
|
-
}
|
|
457
|
-
</style>
|
|
458
|
-
</head>
|
|
459
|
-
<body id='body-wvroot'>
|
|
460
|
-
<div id='wrapper-wvroot'></div>
|
|
461
|
-
</body>
|
|
462
|
-
</html>
|
|
463
|
-
HTML
|
|
464
|
-
|
|
465
|
-
CGI.escape(html)
|
|
431
|
+
Scarpe::Components::Calzini.empty_page_element
|
|
466
432
|
end
|
|
467
433
|
|
|
468
434
|
public
|
|
@@ -535,325 +501,329 @@ class Scarpe
|
|
|
535
501
|
end
|
|
536
502
|
end
|
|
537
503
|
|
|
538
|
-
class Scarpe
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
include Shoes::Log
|
|
560
|
-
|
|
561
|
-
# Changes that have not yet been executed
|
|
562
|
-
attr_reader :waiting_changes
|
|
504
|
+
class Scarpe::Webview::WebWrangler
|
|
505
|
+
# Leaving DOM changes as "meh, async, we'll see when it happens" is terrible for testing.
|
|
506
|
+
# Instead, we need to track whether particular changes have committed yet or not.
|
|
507
|
+
# So we add a single gateway for all DOM changes, and we make sure its work is done
|
|
508
|
+
# before we consider a redraw complete.
|
|
509
|
+
#
|
|
510
|
+
# DOMWrangler batches up changes into fewer RPC calls. It's fine to have a redraw
|
|
511
|
+
# "in flight" and have changes waiting to catch the next bus. But we don't want more
|
|
512
|
+
# than one in flight, since it seems like having too many pending RPC requests can
|
|
513
|
+
# crash Webview. So we allow one redraw scheduled and one redraw promise waiting,
|
|
514
|
+
# at maximum.
|
|
515
|
+
#
|
|
516
|
+
# A WebWrangler will create and wrap a DOMWrangler, serving as the interface
|
|
517
|
+
# for all DOM operations.
|
|
518
|
+
#
|
|
519
|
+
# A batch of DOMWrangler changes may be removed if a full update is scheduled. That
|
|
520
|
+
# update is considered to replace the previous incremental changes. Any changes that
|
|
521
|
+
# need to execute even if a full update happens should be scheduled through
|
|
522
|
+
# WebWrangler#eval_js_async, not DOMWrangler.
|
|
523
|
+
class DOMWrangler
|
|
524
|
+
include Shoes::Log
|
|
563
525
|
|
|
564
|
-
|
|
565
|
-
|
|
526
|
+
# Changes that have not yet been executed
|
|
527
|
+
attr_reader :waiting_changes
|
|
528
|
+
|
|
529
|
+
# A Scarpe::Promise for JS that has been scheduled to execute but is not yet verified complete
|
|
530
|
+
attr_reader :pending_redraw_promise
|
|
531
|
+
|
|
532
|
+
# A Scarpe::Promise for waiting changes - it will be fulfilled when all waiting changes
|
|
533
|
+
# have been verified complete, or when a full redraw that removed them has been
|
|
534
|
+
# verified complete. If many small changes are scheduled, the same promise will be
|
|
535
|
+
# returned for many of them.
|
|
536
|
+
attr_reader :waiting_redraw_promise
|
|
537
|
+
|
|
538
|
+
# Create a DOMWrangler that is paired with a WebWrangler. The WebWrangler is
|
|
539
|
+
# treated as an underlying abstraction for reliable JS evaluation.
|
|
540
|
+
def initialize(web_wrangler)
|
|
541
|
+
log_init("Webview::WebWrangler::DOMWrangler")
|
|
542
|
+
|
|
543
|
+
@wrangler = web_wrangler
|
|
544
|
+
|
|
545
|
+
@waiting_changes = []
|
|
546
|
+
@pending_redraw_promise = nil
|
|
547
|
+
@waiting_redraw_promise = nil
|
|
548
|
+
|
|
549
|
+
@fully_up_to_date_promise = nil
|
|
550
|
+
|
|
551
|
+
# Initially we're waiting for a full replacement to happen.
|
|
552
|
+
# It's possible to request updates/changes before we have
|
|
553
|
+
# a DOM in place and before Webview is running. If we do
|
|
554
|
+
# that, we should discard those updates.
|
|
555
|
+
@first_draw_requested = false
|
|
556
|
+
|
|
557
|
+
@redraw_handlers = []
|
|
558
|
+
|
|
559
|
+
# The "fully up to date" logic is complicated and not
|
|
560
|
+
# as well tested as I'd like. This makes it far less
|
|
561
|
+
# likely that the event simply won't fire.
|
|
562
|
+
# With more comprehensive testing, this should be
|
|
563
|
+
# removable.
|
|
564
|
+
web_wrangler.periodic_code("scarpeDOMWranglerHeartbeat") do
|
|
565
|
+
if @fully_up_to_date_promise && fully_updated?
|
|
566
|
+
@log.info("Fulfilling up-to-date promise on heartbeat")
|
|
567
|
+
@fully_up_to_date_promise.fulfilled!
|
|
568
|
+
@fully_up_to_date_promise = nil
|
|
569
|
+
end
|
|
570
|
+
end
|
|
571
|
+
end
|
|
566
572
|
|
|
567
|
-
|
|
568
|
-
#
|
|
569
|
-
|
|
570
|
-
# returned for many of them.
|
|
571
|
-
attr_reader :waiting_redraw_promise
|
|
573
|
+
def request_change(js_code)
|
|
574
|
+
# No updates until there's something to update
|
|
575
|
+
return unless @first_draw_requested
|
|
572
576
|
|
|
573
|
-
|
|
574
|
-
# treated as an underlying abstraction for reliable JS evaluation.
|
|
575
|
-
def initialize(web_wrangler)
|
|
576
|
-
log_init("WV::WebWrangler::DOMWrangler")
|
|
577
|
+
@waiting_changes << js_code
|
|
577
578
|
|
|
578
|
-
|
|
579
|
+
promise_redraw
|
|
580
|
+
end
|
|
579
581
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
@fully_up_to_date_promise = nil
|
|
585
|
-
|
|
586
|
-
# Initially we're waiting for a full replacement to happen.
|
|
587
|
-
# It's possible to request updates/changes before we have
|
|
588
|
-
# a DOM in place and before Webview is running. If we do
|
|
589
|
-
# that, we should discard those updates.
|
|
590
|
-
@first_draw_requested = false
|
|
591
|
-
|
|
592
|
-
@redraw_handlers = []
|
|
593
|
-
|
|
594
|
-
# The "fully up to date" logic is complicated and not
|
|
595
|
-
# as well tested as I'd like. This makes it far less
|
|
596
|
-
# likely that the event simply won't fire.
|
|
597
|
-
# With more comprehensive testing, this should be
|
|
598
|
-
# removable.
|
|
599
|
-
web_wrangler.periodic_code("scarpeDOMWranglerHeartbeat") do
|
|
600
|
-
if @fully_up_to_date_promise && fully_updated?
|
|
601
|
-
@log.info("Fulfilling up-to-date promise on heartbeat")
|
|
602
|
-
@fully_up_to_date_promise.fulfilled!
|
|
603
|
-
@fully_up_to_date_promise = nil
|
|
604
|
-
end
|
|
605
|
-
end
|
|
606
|
-
end
|
|
582
|
+
def self.replacement_code(html_text)
|
|
583
|
+
"document.getElementById('wrapper-wvroot').innerHTML = `#{html_text}`; true"
|
|
584
|
+
end
|
|
607
585
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
586
|
+
def request_replace(html_text)
|
|
587
|
+
# Replace other pending changes, they're not needed any more
|
|
588
|
+
@waiting_changes = [DOMWrangler.replacement_code(html_text)]
|
|
589
|
+
@first_draw_requested = true
|
|
611
590
|
|
|
612
|
-
|
|
591
|
+
@log.debug("Requesting DOM replacement...")
|
|
592
|
+
promise_redraw
|
|
593
|
+
end
|
|
613
594
|
|
|
614
|
-
|
|
615
|
-
|
|
595
|
+
def on_every_redraw(&block)
|
|
596
|
+
@redraw_handlers << block
|
|
597
|
+
end
|
|
616
598
|
|
|
617
|
-
|
|
618
|
-
|
|
599
|
+
# promise_redraw returns a Scarpe::Promise which will be fulfilled after all current
|
|
600
|
+
# pending or waiting changes have completed. This may require creating a new
|
|
601
|
+
# promise.
|
|
602
|
+
#
|
|
603
|
+
# What are the states of redraw?
|
|
604
|
+
# "empty" - no waiting promise, no pending-redraw promise, no pending changes
|
|
605
|
+
# "pending only" - no waiting promise, but we have a pending redraw with some changes; it hasn't committed yet
|
|
606
|
+
# "pending and waiting" - we have a waiting promise for our unscheduled changes; we can add more unscheduled
|
|
607
|
+
# changes since we haven't scheduled them yet.
|
|
608
|
+
#
|
|
609
|
+
# This is often called after adding a new waiting change or replacing them, so the state may have just changed.
|
|
610
|
+
# It can also be called when no changes have been made and no updates need to happen.
|
|
611
|
+
def promise_redraw
|
|
612
|
+
if fully_updated?
|
|
613
|
+
# No changes to make, nothing in-process or waiting, so just return a pre-fulfilled promise
|
|
614
|
+
@log.debug("Requesting redraw but there are no pending changes or promises, return pre-fulfilled")
|
|
615
|
+
return ::Scarpe::Promise.fulfilled
|
|
619
616
|
end
|
|
620
617
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
@
|
|
625
|
-
|
|
626
|
-
@log.debug("Requesting DOM replacement...")
|
|
627
|
-
promise_redraw
|
|
618
|
+
# Already have a redraw requested *and* one on deck? Then all current changes will have committed
|
|
619
|
+
# when we (eventually) fulfill the waiting_redraw_promise.
|
|
620
|
+
if @waiting_redraw_promise
|
|
621
|
+
@log.debug("Promising eventual redraw of #{@waiting_changes.size} waiting unscheduled changes.")
|
|
622
|
+
return @waiting_redraw_promise
|
|
628
623
|
end
|
|
629
624
|
|
|
630
|
-
|
|
631
|
-
|
|
625
|
+
if @waiting_changes.empty?
|
|
626
|
+
# There's no waiting_redraw_promise. There are no waiting changes. But we're not fully updated.
|
|
627
|
+
# So there must be a redraw in flight, and we don't need to schedule a new waiting_redraw_promise.
|
|
628
|
+
@log.debug("Returning in-flight redraw promise")
|
|
629
|
+
return @pending_redraw_promise
|
|
632
630
|
end
|
|
633
631
|
|
|
634
|
-
#
|
|
635
|
-
# pending or waiting changes have completed. This may require creating a new
|
|
636
|
-
# promise.
|
|
637
|
-
#
|
|
638
|
-
# What are the states of redraw?
|
|
639
|
-
# "empty" - no waiting promise, no pending-redraw promise, no pending changes
|
|
640
|
-
# "pending only" - no waiting promise, but we have a pending redraw with some changes; it hasn't committed yet
|
|
641
|
-
# "pending and waiting" - we have a waiting promise for our unscheduled changes; we can add more unscheduled
|
|
642
|
-
# changes since we haven't scheduled them yet.
|
|
643
|
-
#
|
|
644
|
-
# This is often called after adding a new waiting change or replacing them, so the state may have just changed.
|
|
645
|
-
# It can also be called when no changes have been made and no updates need to happen.
|
|
646
|
-
def promise_redraw
|
|
647
|
-
if fully_updated?
|
|
648
|
-
# No changes to make, nothing in-process or waiting, so just return a pre-fulfilled promise
|
|
649
|
-
@log.debug("Requesting redraw but there are no pending changes or promises, return pre-fulfilled")
|
|
650
|
-
return Promise.fulfilled
|
|
651
|
-
end
|
|
632
|
+
@log.debug("Requesting redraw with #{@waiting_changes.size} waiting changes and no waiting promise - need to schedule something!")
|
|
652
633
|
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
634
|
+
# We have at least one waiting change, possibly newly-added. We have no waiting_redraw_promise.
|
|
635
|
+
# Do we already have a redraw in-flight?
|
|
636
|
+
if @pending_redraw_promise
|
|
637
|
+
# Yes we do. Schedule a new waiting promise. When it turns into the pending_redraw_promise it will
|
|
638
|
+
# grab all waiting changes. In the mean time, it sits here and waits.
|
|
639
|
+
#
|
|
640
|
+
# We *could* do a fancy promise thing and have it update @waiting_changes for itself, etc, when it
|
|
641
|
+
# schedules itself. But we should always be calling promise_redraw or having a redraw fulfilled (see below)
|
|
642
|
+
# when these things change. I'd rather keep the logic in this method. It's easier to reason through
|
|
643
|
+
# all the cases.
|
|
644
|
+
@waiting_redraw_promise = ::Scarpe::Promise.new
|
|
659
645
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
@log.debug("Returning in-flight redraw promise")
|
|
664
|
-
return @pending_redraw_promise
|
|
665
|
-
end
|
|
666
|
-
|
|
667
|
-
@log.debug("Requesting redraw with #{@waiting_changes.size} waiting changes and no waiting promise - need to schedule something!")
|
|
668
|
-
|
|
669
|
-
# We have at least one waiting change, possibly newly-added. We have no waiting_redraw_promise.
|
|
670
|
-
# Do we already have a redraw in-flight?
|
|
671
|
-
if @pending_redraw_promise
|
|
672
|
-
# Yes we do. Schedule a new waiting promise. When it turns into the pending_redraw_promise it will
|
|
673
|
-
# grab all waiting changes. In the mean time, it sits here and waits.
|
|
674
|
-
#
|
|
675
|
-
# We *could* do a fancy promise thing and have it update @waiting_changes for itself, etc, when it
|
|
676
|
-
# schedules itself. But we should always be calling promise_redraw or having a redraw fulfilled (see below)
|
|
677
|
-
# when these things change. I'd rather keep the logic in this method. It's easier to reason through
|
|
678
|
-
# all the cases.
|
|
679
|
-
@waiting_redraw_promise = Promise.new
|
|
680
|
-
|
|
681
|
-
@log.debug("Creating a new waiting promise since a pending promise is already in place")
|
|
682
|
-
return @waiting_redraw_promise
|
|
683
|
-
end
|
|
646
|
+
@log.debug("Creating a new waiting promise since a pending promise is already in place")
|
|
647
|
+
return @waiting_redraw_promise
|
|
648
|
+
end
|
|
684
649
|
|
|
685
|
-
|
|
686
|
-
|
|
650
|
+
# We have no redraw in-flight and no pre-existing waiting line. The new change(s) are presumably right
|
|
651
|
+
# after things were fully up-to-date. We can schedule them for immediate redraw.
|
|
687
652
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
653
|
+
@log.debug("Requesting redraw with #{@waiting_changes.size} waiting changes - scheduling a new redraw for them!")
|
|
654
|
+
promise = schedule_waiting_changes # This clears the waiting changes
|
|
655
|
+
@pending_redraw_promise = promise
|
|
691
656
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
657
|
+
promise.on_fulfilled do
|
|
658
|
+
@redraw_handlers.each(&:call)
|
|
659
|
+
@pending_redraw_promise = nil
|
|
695
660
|
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
661
|
+
if @waiting_redraw_promise
|
|
662
|
+
# While this redraw was in flight, more waiting changes got added and we made a promise
|
|
663
|
+
# about when they'd complete. Now they get scheduled, and we'll fulfill the waiting
|
|
664
|
+
# promise when that redraw finishes. Clear the old waiting promise. We'll add a new one
|
|
665
|
+
# when/if more changes are scheduled during this redraw.
|
|
666
|
+
old_waiting_promise = @waiting_redraw_promise
|
|
667
|
+
@waiting_redraw_promise = nil
|
|
703
668
|
|
|
704
|
-
|
|
669
|
+
@log.debug "Fulfilled redraw with #{@waiting_changes.size} waiting changes - scheduling a new redraw for them!"
|
|
705
670
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
end
|
|
719
|
-
else
|
|
720
|
-
@log.error "WHOAH, WHAT? My logic must be wrong, because there's " +
|
|
721
|
-
"no waiting promise, but waiting changes!"
|
|
671
|
+
new_promise = promise_redraw
|
|
672
|
+
new_promise.on_fulfilled { old_waiting_promise.fulfilled! }
|
|
673
|
+
else
|
|
674
|
+
# The in-flight redraw completed, and there's still no waiting promise. Good! That means
|
|
675
|
+
# we should be fully up-to-date.
|
|
676
|
+
@log.debug "Fulfilled redraw with no waiting changes - marking us as up to date!"
|
|
677
|
+
if @waiting_changes.empty?
|
|
678
|
+
# We're fully up to date! Fulfill the promise. Now we don't need it again until somebody asks
|
|
679
|
+
# us for another.
|
|
680
|
+
if @fully_up_to_date_promise
|
|
681
|
+
@fully_up_to_date_promise.fulfilled!
|
|
682
|
+
@fully_up_to_date_promise = nil
|
|
722
683
|
end
|
|
684
|
+
else
|
|
685
|
+
@log.error "WHOAH, WHAT? My logic must be wrong, because there's " +
|
|
686
|
+
"no waiting promise, but waiting changes!"
|
|
723
687
|
end
|
|
688
|
+
end
|
|
724
689
|
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
raise "JS Redraw failed! Bailing!"
|
|
690
|
+
@log.debug("Redraw is now fully up-to-date") if fully_updated?
|
|
691
|
+
end.on_rejected do
|
|
692
|
+
@log.error "Could not complete JS redraw! #{promise.reason.full_message}"
|
|
693
|
+
@log.debug("REDRAW FULLY UP TO DATE BUT JS FAILED") if fully_updated?
|
|
731
694
|
|
|
732
|
-
|
|
733
|
-
end
|
|
734
|
-
end
|
|
695
|
+
raise Scarpe::JSRedrawError, "JS Redraw failed! Bailing!"
|
|
735
696
|
|
|
736
|
-
|
|
737
|
-
@pending_redraw_promise.nil? && @waiting_redraw_promise.nil? && @waiting_changes.empty?
|
|
697
|
+
# Later we should figure out how to handle this. Clear the promises and queues and request another redraw?
|
|
738
698
|
end
|
|
699
|
+
end
|
|
739
700
|
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
# No changes to make, nothing in-process or waiting, so just return a pre-fulfilled promise
|
|
744
|
-
return Promise.fulfilled
|
|
745
|
-
end
|
|
701
|
+
def fully_updated?
|
|
702
|
+
@pending_redraw_promise.nil? && @waiting_redraw_promise.nil? && @waiting_changes.empty?
|
|
703
|
+
end
|
|
746
704
|
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
705
|
+
# Return a promise which will be fulfilled when the DOM is fully up-to-date
|
|
706
|
+
def promise_fully_updated
|
|
707
|
+
if fully_updated?
|
|
708
|
+
# No changes to make, nothing in-process or waiting, so just return a pre-fulfilled promise
|
|
709
|
+
return ::Scarpe::Promise.fulfilled
|
|
710
|
+
end
|
|
751
711
|
|
|
752
|
-
|
|
753
|
-
|
|
712
|
+
# Do we already have a promise for this? Return it. Everybody can share one.
|
|
713
|
+
if @fully_up_to_date_promise
|
|
714
|
+
return @fully_up_to_date_promise
|
|
754
715
|
end
|
|
755
716
|
|
|
756
|
-
|
|
717
|
+
# We're not fully updated, so we need a promise. Create it, return it.
|
|
718
|
+
@fully_up_to_date_promise = ::Scarpe::Promise.new
|
|
719
|
+
end
|
|
757
720
|
|
|
758
|
-
|
|
759
|
-
# Return it as a promise.
|
|
760
|
-
def schedule_waiting_changes
|
|
761
|
-
return if @waiting_changes.empty?
|
|
721
|
+
private
|
|
762
722
|
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
723
|
+
# Put together the waiting changes into a new in-flight redraw request.
|
|
724
|
+
# Return it as a promise.
|
|
725
|
+
def schedule_waiting_changes
|
|
726
|
+
return if @waiting_changes.empty?
|
|
727
|
+
|
|
728
|
+
js_code = @waiting_changes.join(";")
|
|
729
|
+
@waiting_changes = [] # They're not waiting any more!
|
|
730
|
+
@wrangler.eval_js_async(js_code)
|
|
767
731
|
end
|
|
768
732
|
end
|
|
769
|
-
end
|
|
770
733
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
734
|
+
# An ElementWrangler provides a way for a Drawable to manipulate is DOM element(s)
|
|
735
|
+
# via their HTML IDs. The most straightforward Drawables can have a single HTML ID
|
|
736
|
+
# and use a single ElementWrangler to make any needed changes.
|
|
737
|
+
#
|
|
738
|
+
# For now we don't need an ElementWrangler to add DOM elements, just to manipulate them
|
|
739
|
+
# after initial render. New DOM objects for Drawables are normally added via full
|
|
740
|
+
# redraws rather than incremental updates.
|
|
741
|
+
#
|
|
742
|
+
# Any changes made via ElementWrangler may be cancelled if a full redraw occurs,
|
|
743
|
+
# since it is assumed that small DOM manipulations are no longer needed. If a
|
|
744
|
+
# change would need to be made even if a full redraw occurred, it should be
|
|
745
|
+
# scheduled via WebWrangler#eval_js_async, not via an ElementWrangler.
|
|
746
|
+
class ElementWrangler
|
|
747
|
+
attr_reader :html_id
|
|
748
|
+
|
|
749
|
+
# Create an ElementWrangler for the given HTML ID
|
|
776
750
|
#
|
|
777
|
-
#
|
|
778
|
-
|
|
779
|
-
|
|
751
|
+
# @param html_id [String] the HTML ID for the DOM element
|
|
752
|
+
def initialize(html_id)
|
|
753
|
+
@webwrangler = ::Scarpe::Webview::DisplayService.instance.wrangler
|
|
754
|
+
raise Scarpe::MissingWranglerError, "Can't get WebWrangler!" unless @webwrangler
|
|
755
|
+
|
|
756
|
+
@html_id = html_id
|
|
757
|
+
end
|
|
758
|
+
|
|
759
|
+
# Return a promise that will be fulfilled when all changes scheduled via
|
|
760
|
+
# this ElementWrangler are verified complete.
|
|
780
761
|
#
|
|
781
|
-
#
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
class ElementWrangler
|
|
786
|
-
attr_reader :html_id
|
|
787
|
-
|
|
788
|
-
# Create an ElementWrangler for the given HTML ID
|
|
789
|
-
#
|
|
790
|
-
# @param html_id [String] the HTML ID for the DOM element
|
|
791
|
-
def initialize(html_id)
|
|
792
|
-
@webwrangler = WebviewDisplayService.instance.wrangler
|
|
793
|
-
@html_id = html_id
|
|
794
|
-
end
|
|
762
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when scheduled changes are complete
|
|
763
|
+
def promise_update
|
|
764
|
+
@webwrangler.dom_promise_redraw
|
|
765
|
+
end
|
|
795
766
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
767
|
+
# Update the JS DOM element's value. The given Ruby value will be converted to string and assigned in backquotes.
|
|
768
|
+
#
|
|
769
|
+
# @param new_value [String] the new value
|
|
770
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete
|
|
771
|
+
def value=(new_value)
|
|
772
|
+
@webwrangler.dom_change("document.getElementById('" + html_id + "').value = `" + new_value + "`; true")
|
|
773
|
+
end
|
|
803
774
|
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
775
|
+
# Update the JS DOM element's inner_text. The given Ruby value will be converted to string and assigned in single-quotes.
|
|
776
|
+
#
|
|
777
|
+
# @param new_text [String] the new inner_text
|
|
778
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete
|
|
779
|
+
def inner_text=(new_text)
|
|
780
|
+
@webwrangler.dom_change("document.getElementById('" + html_id + "').innerText = '" + new_text + "'; true")
|
|
781
|
+
end
|
|
811
782
|
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
783
|
+
# Update the JS DOM element's inner_html. The given Ruby value will be converted to string and assigned in backquotes.
|
|
784
|
+
#
|
|
785
|
+
# @param new_html [String] the new inner_html
|
|
786
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete
|
|
787
|
+
def inner_html=(new_html)
|
|
788
|
+
@webwrangler.dom_change("document.getElementById(\"" + html_id + "\").innerHTML = `" + new_html + "`; true")
|
|
789
|
+
end
|
|
819
790
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
791
|
+
# Update the JS DOM element's outer_html. The given Ruby value will be converted to string and assigned in backquotes.
|
|
792
|
+
#
|
|
793
|
+
# @param new_html [String] the new outer_html
|
|
794
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete
|
|
795
|
+
def outer_html=(new_html)
|
|
796
|
+
@webwrangler.dom_change("document.getElementById(\"" + html_id + "\").outerHTML = `" + new_html + "`; true")
|
|
797
|
+
end
|
|
827
798
|
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
799
|
+
# Update the JS DOM element's attribute. The given Ruby value will be inspected and assigned.
|
|
800
|
+
#
|
|
801
|
+
# @param attribute [String] the attribute name
|
|
802
|
+
# @param value [String] the new attribute value
|
|
803
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete
|
|
804
|
+
def set_attribute(attribute, value)
|
|
805
|
+
@webwrangler.dom_change("document.getElementById(\"" + html_id + "\").setAttribute(" + attribute.inspect + "," + value.inspect + "); true")
|
|
806
|
+
end
|
|
836
807
|
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
808
|
+
# Update an attribute of the JS DOM element's style. The given Ruby value will be inspected and assigned.
|
|
809
|
+
#
|
|
810
|
+
# @param style_attr [String] the style attribute name
|
|
811
|
+
# @param value [String] the new style attribute value
|
|
812
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete
|
|
813
|
+
def set_style(style_attr, value)
|
|
814
|
+
@webwrangler.dom_change("document.getElementById(\"" + html_id + "\").style.#{style_attr} = " + value.inspect + "; true")
|
|
815
|
+
end
|
|
845
816
|
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
817
|
+
# Remove the specified DOM element
|
|
818
|
+
#
|
|
819
|
+
# @return [Scarpe::Promise] a promise that wil be fulfilled when the element is removed
|
|
820
|
+
def remove
|
|
821
|
+
@webwrangler.dom_change("document.getElementById('" + html_id + "').remove(); true")
|
|
822
|
+
end
|
|
852
823
|
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
end
|
|
824
|
+
def toggle_input_button(mark)
|
|
825
|
+
checked_value = mark ? "true" : "false"
|
|
826
|
+
@webwrangler.dom_change("document.getElementById('#{html_id}').checked = #{checked_value};")
|
|
857
827
|
end
|
|
858
828
|
end
|
|
859
829
|
end
|