opal-browser 0.2.0.beta1 → 0.3.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 +5 -5
- data/.github/workflows/build.yml +95 -0
- data/.gitignore +3 -0
- data/CHANGELOG.md +8 -0
- data/Gemfile +17 -3
- data/LICENSE +2 -1
- data/README.md +183 -52
- data/Rakefile +29 -1
- data/config.ru +20 -3
- data/docs/polyfills.md +24 -0
- data/examples/2048/Gemfile +6 -0
- data/examples/2048/README.md +13 -0
- data/examples/2048/app/application.rb +169 -0
- data/examples/2048/config.ru +9 -0
- data/examples/canvas/Gemfile +6 -0
- data/examples/canvas/README.md +9 -0
- data/examples/canvas/app/application.rb +55 -0
- data/examples/canvas/config.ru +9 -0
- data/examples/component/Gemfile +6 -0
- data/examples/component/README.md +10 -0
- data/examples/component/app/application.rb +66 -0
- data/examples/component/config.ru +9 -0
- data/examples/integrations/README.md +24 -0
- data/examples/integrations/dynamic-rack-opal-sprockets-server/Gemfile +6 -0
- data/examples/integrations/dynamic-rack-opal-sprockets-server/README.md +16 -0
- data/examples/integrations/dynamic-rack-opal-sprockets-server/app/application.rb +6 -0
- data/examples/integrations/dynamic-rack-opal-sprockets-server/config.ru +9 -0
- data/examples/integrations/dynamic-roda-roda-sprockets/.gitignore +1 -0
- data/examples/integrations/dynamic-roda-roda-sprockets/Gemfile +7 -0
- data/examples/integrations/dynamic-roda-roda-sprockets/README.md +22 -0
- data/examples/integrations/dynamic-roda-roda-sprockets/Rakefile +4 -0
- data/examples/integrations/dynamic-roda-roda-sprockets/app/application.rb +6 -0
- data/examples/integrations/dynamic-roda-roda-sprockets/app.rb +32 -0
- data/examples/integrations/dynamic-roda-roda-sprockets/config.ru +3 -0
- data/examples/integrations/dynamic-roda-tilt/.gitignore +1 -0
- data/examples/integrations/dynamic-roda-tilt/Gemfile +8 -0
- data/examples/integrations/dynamic-roda-tilt/README.md +17 -0
- data/examples/integrations/dynamic-roda-tilt/Rakefile +6 -0
- data/examples/integrations/dynamic-roda-tilt/app/application.rb +6 -0
- data/examples/integrations/dynamic-roda-tilt/app.rb +50 -0
- data/examples/integrations/dynamic-roda-tilt/config.ru +3 -0
- data/examples/integrations/dynamic-sinatra-opal-sprockets-server/Gemfile +7 -0
- data/examples/integrations/dynamic-sinatra-opal-sprockets-server/README.md +16 -0
- data/examples/integrations/dynamic-sinatra-opal-sprockets-server/app/application.rb +6 -0
- data/examples/integrations/dynamic-sinatra-opal-sprockets-server/config.ru +29 -0
- data/examples/integrations/static-bash/.gitignore +2 -0
- data/examples/integrations/static-bash/Gemfile +3 -0
- data/examples/integrations/static-bash/README.md +8 -0
- data/examples/integrations/static-bash/app/application.rb +6 -0
- data/examples/integrations/static-bash/build.sh +4 -0
- data/examples/integrations/static-bash/index.html +10 -0
- data/examples/integrations/static-bash-opal-parser/.gitignore +3 -0
- data/examples/integrations/static-bash-opal-parser/Gemfile +3 -0
- data/examples/integrations/static-bash-opal-parser/README.md +10 -0
- data/examples/integrations/static-bash-opal-parser/build.sh +4 -0
- data/examples/integrations/static-bash-opal-parser/index.html +19 -0
- data/examples/integrations/static-rake/.gitignore +1 -0
- data/examples/integrations/static-rake/Gemfile +4 -0
- data/examples/integrations/static-rake/README.md +7 -0
- data/examples/integrations/static-rake/Rakefile +10 -0
- data/examples/integrations/static-rake/app/application.rb +6 -0
- data/examples/integrations/static-rake/index.html +9 -0
- data/examples/integrations/static-rake-guard/.gitignore +1 -0
- data/examples/integrations/static-rake-guard/Gemfile +6 -0
- data/examples/integrations/static-rake-guard/Guardfile +3 -0
- data/examples/integrations/static-rake-guard/README.md +10 -0
- data/examples/integrations/static-rake-guard/Rakefile +10 -0
- data/examples/integrations/static-rake-guard/app/application.rb +6 -0
- data/examples/integrations/static-rake-guard/index.html +9 -0
- data/examples/svg/.gitignore +1 -0
- data/examples/svg/Gemfile +4 -0
- data/examples/svg/README.md +7 -0
- data/examples/svg/Rakefile +10 -0
- data/examples/svg/app/application.rb +11 -0
- data/examples/svg/index.html +17 -0
- data/examples/svg/index.svg +6 -0
- data/index.html.erb +8 -6
- data/lib/opal-browser.rb +1 -0
- data/opal/browser/animation_frame.rb +26 -1
- data/opal/browser/audio/node.rb +121 -0
- data/opal/browser/audio/param_schedule.rb +43 -0
- data/opal/browser/audio.rb +66 -0
- data/opal/browser/blob.rb +94 -0
- data/opal/browser/canvas/data.rb +1 -11
- data/opal/browser/canvas/gradient.rb +1 -11
- data/opal/browser/canvas/style.rb +3 -11
- data/opal/browser/canvas/text.rb +1 -11
- data/opal/browser/canvas.rb +17 -13
- data/opal/browser/console.rb +3 -1
- data/opal/browser/cookies.rb +78 -42
- data/opal/browser/crypto.rb +79 -0
- data/opal/browser/css/declaration.rb +1 -1
- data/opal/browser/css/rule.rb +1 -1
- data/opal/browser/css/style_sheet.rb +2 -2
- data/opal/browser/css.rb +23 -7
- data/opal/browser/database/sql.rb +193 -0
- data/opal/browser/delay.rb +41 -7
- data/opal/browser/dom/attribute.rb +13 -12
- data/opal/browser/dom/builder.rb +31 -17
- data/opal/browser/dom/document.rb +174 -42
- data/opal/browser/dom/document_fragment.rb +18 -0
- data/opal/browser/dom/document_or_shadow_root.rb +19 -0
- data/opal/browser/dom/element/attributes.rb +111 -0
- data/opal/browser/dom/element/button.rb +31 -0
- data/opal/browser/dom/element/custom.rb +177 -0
- data/opal/browser/dom/element/data.rb +82 -0
- data/opal/browser/dom/element/editable.rb +47 -0
- data/opal/browser/dom/element/form.rb +38 -0
- data/opal/browser/dom/element/iframe.rb +37 -0
- data/opal/browser/dom/element/image.rb +2 -0
- data/opal/browser/dom/element/input.rb +48 -1
- data/opal/browser/dom/element/media.rb +17 -0
- data/opal/browser/dom/element/offset.rb +5 -0
- data/opal/browser/dom/element/position.rb +11 -2
- data/opal/browser/dom/element/scroll.rb +123 -24
- data/opal/browser/dom/element/select.rb +42 -0
- data/opal/browser/dom/element/size.rb +17 -0
- data/opal/browser/dom/element/template.rb +11 -0
- data/opal/browser/dom/element/textarea.rb +26 -0
- data/opal/browser/dom/element.rb +468 -238
- data/opal/browser/dom/mutation_observer.rb +4 -4
- data/opal/browser/dom/node.rb +142 -60
- data/opal/browser/dom/node_set.rb +73 -44
- data/opal/browser/dom/shadow_root.rb +12 -0
- data/opal/browser/dom/text.rb +2 -2
- data/opal/browser/dom.rb +40 -16
- data/opal/browser/effects.rb +180 -3
- data/opal/browser/event/all.rb +26 -0
- data/opal/browser/{dom/event → event}/animation.rb +4 -2
- data/opal/browser/{dom/event → event}/audio_processing.rb +4 -2
- data/opal/browser/{dom/event → event}/base.rb +98 -9
- data/opal/browser/{dom/event → event}/before_unload.rb +4 -2
- data/opal/browser/{dom/event → event}/clipboard.rb +11 -2
- data/opal/browser/{dom/event → event}/close.rb +4 -2
- data/opal/browser/{dom/event → event}/composition.rb +4 -2
- data/opal/browser/{dom/event → event}/custom.rb +3 -3
- data/opal/browser/event/data_transfer.rb +95 -0
- data/opal/browser/{dom/event → event}/device_light.rb +4 -2
- data/opal/browser/{dom/event → event}/device_motion.rb +4 -2
- data/opal/browser/{dom/event → event}/device_orientation.rb +4 -2
- data/opal/browser/{dom/event → event}/device_proximity.rb +4 -2
- data/opal/browser/{dom/event → event}/drag.rb +11 -7
- data/opal/browser/{dom/event → event}/focus.rb +4 -2
- data/opal/browser/{dom/event → event}/gamepad.rb +5 -3
- data/opal/browser/{dom/event → event}/hash_change.rb +4 -2
- data/opal/browser/{dom/event → event}/keyboard.rb +16 -3
- data/opal/browser/{dom/event → event}/message.rb +4 -2
- data/opal/browser/{dom/event → event}/mouse.rb +12 -8
- data/opal/browser/{dom/event → event}/page_transition.rb +4 -2
- data/opal/browser/{dom/event → event}/pop_state.rb +4 -2
- data/opal/browser/{dom/event → event}/progress.rb +4 -2
- data/opal/browser/{dom/event → event}/sensor.rb +4 -2
- data/opal/browser/{dom/event → event}/storage.rb +4 -2
- data/opal/browser/{dom/event → event}/touch.rb +4 -2
- data/opal/browser/{dom/event → event}/ui.rb +2 -2
- data/opal/browser/{dom/event → event}/wheel.rb +4 -2
- data/opal/browser/event.rb +163 -0
- data/opal/browser/event_source.rb +2 -2
- data/opal/browser/form_data.rb +225 -0
- data/opal/browser/history.rb +4 -8
- data/opal/browser/http/binary.rb +1 -0
- data/opal/browser/http/headers.rb +16 -2
- data/opal/browser/http/request.rb +46 -48
- data/opal/browser/http/response.rb +5 -1
- data/opal/browser/http.rb +25 -2
- data/opal/browser/immediate.rb +9 -5
- data/opal/browser/interval.rb +34 -11
- data/opal/browser/location.rb +7 -1
- data/opal/browser/navigator.rb +127 -7
- data/opal/browser/polyfill/visual_viewport.rb +216 -0
- data/opal/browser/screen.rb +3 -3
- data/opal/browser/setup/base.rb +6 -0
- data/opal/browser/setup/full.rb +13 -0
- data/opal/browser/setup/large.rb +17 -0
- data/opal/browser/setup/mini.rb +8 -0
- data/opal/browser/setup/traditional.rb +10 -0
- data/opal/browser/socket.rb +8 -4
- data/opal/browser/storage.rb +53 -35
- data/opal/browser/support.rb +72 -5
- data/opal/browser/utils.rb +94 -14
- data/opal/browser/version.rb +1 -1
- data/opal/browser/visual_viewport.rb +39 -0
- data/opal/browser/window/size.rb +31 -3
- data/opal/browser/window/view.rb +15 -0
- data/opal/browser/window.rb +46 -25
- data/opal/browser.rb +1 -10
- data/opal/opal-browser.rb +1 -0
- data/opal-browser.gemspec +3 -3
- data/spec/database/sql_spec.rb +139 -0
- data/spec/delay_spec.rb +41 -0
- data/spec/dom/attribute_spec.rb +49 -0
- data/spec/dom/builder_spec.rb +25 -8
- data/spec/dom/document_spec.rb +22 -0
- data/spec/dom/element/attributes_spec.rb +52 -0
- data/spec/dom/element/custom_spec.rb +106 -0
- data/spec/dom/element/subclass_spec.rb +144 -0
- data/spec/dom/element_spec.rb +181 -4
- data/spec/dom/mutation_observer_spec.rb +12 -8
- data/spec/dom/node_set_spec.rb +44 -0
- data/spec/dom/node_spec.rb +48 -0
- data/spec/dom_spec.rb +8 -0
- data/spec/event_source_spec.rb +15 -12
- data/spec/{dom/event_spec.rb → event_spec.rb} +44 -15
- data/spec/history_spec.rb +23 -19
- data/spec/http_spec.rb +19 -31
- data/spec/immediate_spec.rb +5 -4
- data/spec/interval_spec.rb +59 -0
- data/spec/native_cached_wrapper_spec.rb +46 -0
- data/spec/runner.rb +62 -69
- data/spec/socket_spec.rb +16 -12
- data/spec/spec_helper.rb +2 -5
- data/spec/spec_helper_promise.rb.erb +25 -0
- data/spec/storage_spec.rb +1 -1
- metadata +172 -50
- data/.travis.yml +0 -60
- data/opal/browser/dom/event.rb +0 -253
- data/opal/browser/http/parameters.rb +0 -8
- data/opal/browser/window/scroll.rb +0 -59
data/opal/browser/interval.rb
CHANGED
|
@@ -19,14 +19,11 @@ class Interval
|
|
|
19
19
|
@block = block
|
|
20
20
|
|
|
21
21
|
@aborted = false
|
|
22
|
-
@stopped = true
|
|
23
|
-
|
|
24
|
-
start
|
|
25
22
|
end
|
|
26
23
|
|
|
27
24
|
# Check if the interval has been stopped.
|
|
28
25
|
def stopped?
|
|
29
|
-
@
|
|
26
|
+
@id.nil?
|
|
30
27
|
end
|
|
31
28
|
|
|
32
29
|
# Check if the interval has been aborted.
|
|
@@ -44,6 +41,8 @@ class Interval
|
|
|
44
41
|
|
|
45
42
|
# Stop the interval, it will be possible to start it again.
|
|
46
43
|
def stop
|
|
44
|
+
return if stopped?
|
|
45
|
+
|
|
47
46
|
`#@window.clearInterval(#@id)`
|
|
48
47
|
|
|
49
48
|
@stopped = true
|
|
@@ -53,10 +52,14 @@ class Interval
|
|
|
53
52
|
# Start the interval if it has been stopped.
|
|
54
53
|
def start
|
|
55
54
|
raise "the interval has been aborted" if aborted?
|
|
56
|
-
|
|
57
55
|
return unless stopped?
|
|
58
56
|
|
|
59
|
-
@id = `#@window.setInterval(
|
|
57
|
+
@id = `#@window.setInterval(#@block, #@every * 1000)`
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Call the [Interval] block.
|
|
61
|
+
def call
|
|
62
|
+
@block.call
|
|
60
63
|
end
|
|
61
64
|
end
|
|
62
65
|
|
|
@@ -67,22 +70,42 @@ class Window
|
|
|
67
70
|
#
|
|
68
71
|
# @return [Interval] the object representing the interval
|
|
69
72
|
def every(time, &block)
|
|
73
|
+
Interval.new(@native, time, &block).tap(&:start)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Execute the block every given seconds, you have to call [#start] on it
|
|
77
|
+
# yourself.
|
|
78
|
+
#
|
|
79
|
+
# @param time [Float] the seconds between every call
|
|
80
|
+
#
|
|
81
|
+
# @return [Interval] the object representing the interval
|
|
82
|
+
def every!(time, &block)
|
|
70
83
|
Interval.new(@native, time, &block)
|
|
71
84
|
end
|
|
72
85
|
end
|
|
73
86
|
|
|
74
87
|
end
|
|
75
88
|
|
|
89
|
+
module Kernel
|
|
90
|
+
# (see Browser::Window#every)
|
|
91
|
+
def every(time, &block)
|
|
92
|
+
$window.every(time, &block)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# (see Browser::Window#every!)
|
|
96
|
+
def every!(time, &block)
|
|
97
|
+
$window.every!(time, &block)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
76
101
|
class Proc
|
|
77
102
|
# (see Browser::Window#every)
|
|
78
103
|
def every(time)
|
|
79
104
|
$window.every(time, &self)
|
|
80
105
|
end
|
|
81
|
-
end
|
|
82
106
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
$window.every(time, &block)
|
|
107
|
+
# (see Browser::Window#every!)
|
|
108
|
+
def every!(time)
|
|
109
|
+
$window.every!(time, &self)
|
|
87
110
|
end
|
|
88
111
|
end
|
data/opal/browser/location.rb
CHANGED
|
@@ -4,7 +4,7 @@ module Browser
|
|
|
4
4
|
#
|
|
5
5
|
# @see https://developer.mozilla.org/en-US/docs/Web/API/Location
|
|
6
6
|
class Location
|
|
7
|
-
include
|
|
7
|
+
include Browser::NativeCachedWrapper
|
|
8
8
|
|
|
9
9
|
# Change the location.
|
|
10
10
|
#
|
|
@@ -66,6 +66,12 @@ class Location
|
|
|
66
66
|
# @return [String] the query part of the location URI
|
|
67
67
|
alias_native :query, :search
|
|
68
68
|
alias_native :query=, :search=
|
|
69
|
+
|
|
70
|
+
# Returns the full path of the location URI, including
|
|
71
|
+
# the query string and fragment, eg. /site?a=b#c
|
|
72
|
+
def full_path
|
|
73
|
+
path + query + fragment
|
|
74
|
+
end
|
|
69
75
|
end
|
|
70
76
|
|
|
71
77
|
class Window
|
data/opal/browser/navigator.rb
CHANGED
|
@@ -4,7 +4,7 @@ module Browser
|
|
|
4
4
|
#
|
|
5
5
|
# @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator
|
|
6
6
|
class Navigator
|
|
7
|
-
include
|
|
7
|
+
include Browser::NativeCachedWrapper
|
|
8
8
|
|
|
9
9
|
Version = Struct.new(:major, :minor, :build)
|
|
10
10
|
Product = Struct.new(:name, :version)
|
|
@@ -12,7 +12,7 @@ class Navigator
|
|
|
12
12
|
|
|
13
13
|
# Representation of a MIME type.
|
|
14
14
|
class MimeType
|
|
15
|
-
include
|
|
15
|
+
include Browser::NativeCachedWrapper
|
|
16
16
|
|
|
17
17
|
# @!attribute [r] plugin
|
|
18
18
|
# @return [Plugin] the plugin for the MIME type
|
|
@@ -62,6 +62,27 @@ class Navigator
|
|
|
62
62
|
alias_native :version
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
+
# Representation for the array of plugins.
|
|
66
|
+
#
|
|
67
|
+
# @see https://developer.mozilla.org/en-US/docs/Web/API/NavigatorPlugins
|
|
68
|
+
class Plugins < Native::Array
|
|
69
|
+
def initialize(plugins)
|
|
70
|
+
super plugins do |p|
|
|
71
|
+
Plugin.new(p)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Reload all browser plugins.
|
|
76
|
+
def refresh
|
|
77
|
+
`#@native.refresh(false)`
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Reload all browser plugins reloading pages that contain `<embed>`s.
|
|
81
|
+
def refresh!
|
|
82
|
+
`#@native.refresh(true)`
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
65
86
|
# @!attribute [r] code
|
|
66
87
|
# @return [String] the browser code name
|
|
67
88
|
alias_native :code, :appCodeName
|
|
@@ -112,11 +133,9 @@ class Navigator
|
|
|
112
133
|
alias_native :platform
|
|
113
134
|
|
|
114
135
|
# @!attribute [r] plugins
|
|
115
|
-
# @return [
|
|
136
|
+
# @return [Plugins] the enabled plugins
|
|
116
137
|
def plugins
|
|
117
|
-
|
|
118
|
-
Plugin.new(p)
|
|
119
|
-
end
|
|
138
|
+
Plugins.new(`#@native.plugins`)
|
|
120
139
|
end
|
|
121
140
|
|
|
122
141
|
# @!attribute [r] product
|
|
@@ -141,13 +160,114 @@ class Navigator
|
|
|
141
160
|
rescue
|
|
142
161
|
false
|
|
143
162
|
end
|
|
163
|
+
|
|
164
|
+
# Representation of user location based on Geolocation API
|
|
165
|
+
#
|
|
166
|
+
# Example usage:
|
|
167
|
+
# ```
|
|
168
|
+
# $window.navigator.geolocate.then do |pos|
|
|
169
|
+
# puts "#{pos.coords.latitude}, #{pos.coords.longitude}, #{pos.coords.accuracy}"
|
|
170
|
+
# end
|
|
171
|
+
# ```
|
|
172
|
+
#
|
|
173
|
+
# @see https://developer.mozilla.org/en-US/docs/Web/API/Position
|
|
174
|
+
class Position
|
|
175
|
+
include Browser::NativeCachedWrapper
|
|
176
|
+
|
|
177
|
+
class Coords
|
|
178
|
+
include Native::Wrapper
|
|
179
|
+
|
|
180
|
+
# @!attribute [r] latitude
|
|
181
|
+
alias_native :latitude
|
|
182
|
+
# @!attribute [r] longitude
|
|
183
|
+
alias_native :longitude
|
|
184
|
+
# @!attribute [r] altitude
|
|
185
|
+
alias_native :altitude
|
|
186
|
+
# @!attribute [r] accuracy
|
|
187
|
+
alias_native :accuracy
|
|
188
|
+
# @!attribute [r] altitude_accuracy
|
|
189
|
+
alias_native :altitude_accuracy, :altitudeAccuracy
|
|
190
|
+
# @!attribute [r] heading
|
|
191
|
+
alias_native :heading
|
|
192
|
+
# @!attribute [r] speed
|
|
193
|
+
alias_native :speed
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# @!attribute [r] timestamp
|
|
197
|
+
alias_native :timestamp
|
|
198
|
+
|
|
199
|
+
def coords
|
|
200
|
+
@coords ||= Coords.new(`#@native.coords`)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Geolocates the user once
|
|
205
|
+
#
|
|
206
|
+
# @return [Promise] promise that resolves to the {Position} object
|
|
207
|
+
def geolocate(max_age: 0, timeout: Float::INFINITY, high_accuracy: false)
|
|
208
|
+
promise = Promise.new
|
|
209
|
+
succ = proc { |i| promise.resolve(Position.new(i)) }
|
|
210
|
+
fail = proc { |i| promise.reject(Native(i)) }
|
|
211
|
+
opts = {maxAge: max_age, timeout: timeout, enableHighAccuracy: high_accuracy}
|
|
212
|
+
`#@native.geolocation.getCurrentPosition(#{succ.to_n}, #{fail.to_n}, #{opts.to_n})`
|
|
213
|
+
promise
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Geolocates the user multiple times and calls a block with his location
|
|
217
|
+
# until #stop_tracking is called with a returned id. Calls a proc named error
|
|
218
|
+
# if error happens.
|
|
219
|
+
#
|
|
220
|
+
# @return [Integer] an ID that can be used as an argument to #stop_tracking
|
|
221
|
+
def track(max_age: 0, timeout: Float::INFINITY, high_accuracy: false, error: proc{|i|}, &block)
|
|
222
|
+
opts = {maxAge: max_age, timeout: timeout, enableHighAccuracy: high_accuracy}
|
|
223
|
+
succ = proc { |i| block.call(Position.new(i)) }
|
|
224
|
+
fail = proc { |i| error.call(Native(i)) }
|
|
225
|
+
`#@native.geolocation.watchPosition(#{succ.to_n}, #{fail.to_n}, #{opts.to_n})`
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def stop_tracking(id)
|
|
229
|
+
`#@native.geolocation.clearWatch(#{id})`
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Triggers a vibration on a device. A pattern can be either a number of
|
|
233
|
+
# miliseconds for a vibration length, or an array of lengths (in
|
|
234
|
+
# miliseconds) which describes a vibration pattern - first element of said
|
|
235
|
+
# array describes how long the device should vibrate, second - how long to
|
|
236
|
+
# stop for and so on.
|
|
237
|
+
def vibrate(pattern)
|
|
238
|
+
`#@native.vibrate(#{pattern.to_n})`
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Check a battery status of user device. This API is deprecated in the browser
|
|
242
|
+
# context and usable mainly in privileged contexts.
|
|
243
|
+
#
|
|
244
|
+
# @return [Promise] a promise that resolves with a battery status
|
|
245
|
+
#
|
|
246
|
+
# @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/getBattery
|
|
247
|
+
def get_battery
|
|
248
|
+
promise = Promise.new
|
|
249
|
+
yes = proc { |r| promise.resolve(Native(r)) }
|
|
250
|
+
no = proc { |r| promise.reject(Native(r)) }
|
|
251
|
+
`#@native.getBattery().then(#{yes.to_n}).catch(#{no.to_n})`
|
|
252
|
+
promise
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Queue to send a small amount of data to a server.
|
|
256
|
+
#
|
|
257
|
+
# @param url [String] url to trigger
|
|
258
|
+
# @param payload [String, Blob, FormData, Hash] data to send
|
|
259
|
+
#
|
|
260
|
+
# @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon
|
|
261
|
+
def send_beacon(url, payload=nil)
|
|
262
|
+
`#@native.sendBeacon(#{url}, #{payload.to_n})`
|
|
263
|
+
end
|
|
144
264
|
end
|
|
145
265
|
|
|
146
266
|
class Window
|
|
147
267
|
# @!attribute [r] navigator
|
|
148
268
|
# @return [Navigator] the navigator
|
|
149
269
|
def navigator
|
|
150
|
-
Navigator.new(`#@native.navigator`) if `#@native.navigator`
|
|
270
|
+
@navigator ||= Navigator.new(`#@native.navigator`) if `#@native.navigator`
|
|
151
271
|
end
|
|
152
272
|
end
|
|
153
273
|
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# VisualViewport polyfill (mainly for Firefox browsers) taken from:
|
|
2
|
+
# https://github.com/WICG/visual-viewport/blob/gh-pages/polyfill/visualViewport.js
|
|
3
|
+
# Licensed under "W3C 3-clause BSD License".
|
|
4
|
+
|
|
5
|
+
# Let's make sure the functions aren't poluting the global context:
|
|
6
|
+
%x{
|
|
7
|
+
(function(){ "use strict";
|
|
8
|
+
|
|
9
|
+
// This is hacky but necessary in order to get the innerWidth/Height without
|
|
10
|
+
// page scale applied reliably.
|
|
11
|
+
function updateUnscaledDimensions() {
|
|
12
|
+
if (!window.viewPolyfill.iframeDummy) {
|
|
13
|
+
var iframe = document.createElement('iframe');
|
|
14
|
+
iframe.style.position="absolute";
|
|
15
|
+
iframe.style.width="100%";
|
|
16
|
+
iframe.style.height="100%";
|
|
17
|
+
iframe.style.left="0px";
|
|
18
|
+
iframe.style.top="0px";
|
|
19
|
+
iframe.style.border="0";
|
|
20
|
+
iframe.style.visibility="hidden";
|
|
21
|
+
iframe.style.zIndex="-1";
|
|
22
|
+
iframe.srcdoc = "<!DOCTYPE html><html><body style='margin:0px; padding:0px'></body></html>";
|
|
23
|
+
|
|
24
|
+
document.body.appendChild(iframe);
|
|
25
|
+
window.viewPolyfill.iframeDummy = iframe;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
var iframe = window.viewPolyfill.iframeDummy;
|
|
29
|
+
|
|
30
|
+
var documentRect = document.documentElement.getBoundingClientRect();
|
|
31
|
+
var iframeBody = iframe.contentDocument.body;
|
|
32
|
+
iframeBody.style.width = documentRect.width + 'px';
|
|
33
|
+
iframeBody.style.height = documentRect.height + 'px';
|
|
34
|
+
|
|
35
|
+
// Hide overflow temporarily so that the iframe size isn't shrunk by
|
|
36
|
+
// scrollbars.
|
|
37
|
+
var prevDocumentOverflow = document.documentElement.style.overflow;
|
|
38
|
+
document.documentElement.style.overflow = "hidden";
|
|
39
|
+
|
|
40
|
+
var iframeWindow = window.viewPolyfill.iframeDummy.contentWindow;
|
|
41
|
+
window.viewPolyfill.unscaledInnerWidth = iframeWindow.innerWidth;
|
|
42
|
+
window.viewPolyfill.unscaledInnerHeight = iframeWindow.innerHeight;
|
|
43
|
+
|
|
44
|
+
document.documentElement.style.overflow = prevDocumentOverflow;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function fireScrollEvent() {
|
|
48
|
+
var listeners = window.viewPolyfill.scrollEventListeners;
|
|
49
|
+
for (var i = 0; i < listeners.length; i++)
|
|
50
|
+
listeners[i]();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function fireResizeEvent() {
|
|
54
|
+
var listeners = window.viewPolyfill.resizeEventListeners;
|
|
55
|
+
for (var i = 0; i < listeners.length; i++)
|
|
56
|
+
listeners[i]();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function updateViewportChanged() {
|
|
60
|
+
var scrollChanged =
|
|
61
|
+
window.viewPolyfill.offsetLeftSinceLastChange != window.visualViewport.offsetLeft ||
|
|
62
|
+
window.viewPolyfill.offsetTopSinceLastChange != window.visualViewport.offsetTop;
|
|
63
|
+
|
|
64
|
+
var sizeChanged =
|
|
65
|
+
window.viewPolyfill.widthSinceLastChange != window.visualViewport.width ||
|
|
66
|
+
window.viewPolyfill.heightSinceLastChange != window.visualViewport.height ||
|
|
67
|
+
window.viewPolyfill.scaleSinceLastChange != window.visualViewport.scale;
|
|
68
|
+
|
|
69
|
+
window.viewPolyfill.offsetLeftSinceLastChange = window.visualViewport.offsetLeft;
|
|
70
|
+
window.viewPolyfill.offsetTopSinceLastChange = window.visualViewport.offsetTop;
|
|
71
|
+
window.viewPolyfill.widthSinceLastChange = window.visualViewport.width;
|
|
72
|
+
window.viewPolyfill.heightSinceLastChange = window.visualViewport.height;
|
|
73
|
+
window.viewPolyfill.scaleSinceLastChange = window.visualViewport.scale;
|
|
74
|
+
|
|
75
|
+
if (scrollChanged)
|
|
76
|
+
fireScrollEvent();
|
|
77
|
+
|
|
78
|
+
if (sizeChanged)
|
|
79
|
+
fireResizeEvent();
|
|
80
|
+
|
|
81
|
+
setTimeout(updateViewportChanged, 500);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function registerChangeHandlers() {
|
|
85
|
+
window.addEventListener('scroll', updateViewportChanged, {'passive': true});
|
|
86
|
+
window.addEventListener('resize', updateViewportChanged, {'passive': true});
|
|
87
|
+
window.addEventListener('resize', updateUnscaledDimensions, {'passive': true});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
var isChrome = navigator.userAgent.indexOf('Chrome') > -1;
|
|
91
|
+
var isSafari = navigator.userAgent.indexOf("Safari") > -1;
|
|
92
|
+
var isIEEdge = navigator.userAgent.indexOf('Edge') > -1;
|
|
93
|
+
|
|
94
|
+
if ((isChrome)&&(isSafari))
|
|
95
|
+
isSafari=false;
|
|
96
|
+
|
|
97
|
+
if (window.visualViewport) {
|
|
98
|
+
console.log('Using real visual viewport API');
|
|
99
|
+
} else {
|
|
100
|
+
console.log('Polyfilling Viewport API');
|
|
101
|
+
var layoutDummy = document.createElement('div');
|
|
102
|
+
layoutDummy.style.width = "100%";
|
|
103
|
+
layoutDummy.style.height = "100%";
|
|
104
|
+
if (isSafari) {
|
|
105
|
+
layoutDummy.style.position = "fixed";
|
|
106
|
+
} else {
|
|
107
|
+
layoutDummy.style.position = "absolute";
|
|
108
|
+
}
|
|
109
|
+
layoutDummy.style.left = "0px";
|
|
110
|
+
layoutDummy.style.top = "0px";
|
|
111
|
+
layoutDummy.style.visibility = "hidden";
|
|
112
|
+
|
|
113
|
+
window.viewPolyfill = {
|
|
114
|
+
"offsetLeftSinceLastChange": null,
|
|
115
|
+
"offsetTopSinceLastChange": null,
|
|
116
|
+
"widthSinceLastChange": null,
|
|
117
|
+
"heightSinceLastChange": null,
|
|
118
|
+
"scaleSinceLastChange": null,
|
|
119
|
+
"scrollEventListeners": [],
|
|
120
|
+
"resizeEventListeners": [],
|
|
121
|
+
"layoutDummy": layoutDummy,
|
|
122
|
+
"iframeDummy": null,
|
|
123
|
+
"unscaledInnerWidth": 0,
|
|
124
|
+
"unscaledInnerHeight": 0
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
registerChangeHandlers();
|
|
128
|
+
|
|
129
|
+
// TODO: Need to wait for <body> to be loaded but this is probably
|
|
130
|
+
// later than needed.
|
|
131
|
+
window.addEventListener('load', function() {
|
|
132
|
+
updateUnscaledDimensions();
|
|
133
|
+
document.body.appendChild(layoutDummy);
|
|
134
|
+
|
|
135
|
+
var viewport = {
|
|
136
|
+
get offsetLeft() {
|
|
137
|
+
if (isSafari) {
|
|
138
|
+
// Note: Safari's getBoundingClientRect left/top is wrong when pinch-zoomed requiring this "unscaling".
|
|
139
|
+
return window.scrollX - (layoutDummy.getBoundingClientRect().left * this.scale + window.scrollX * this.scale);
|
|
140
|
+
} else {
|
|
141
|
+
return window.scrollX + layoutDummy.getBoundingClientRect().left;
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
get offsetTop() {
|
|
145
|
+
if (isSafari) {
|
|
146
|
+
// Note: Safari's getBoundingClientRect left/top is wrong when pinch-zoomed requiring this "unscaling".
|
|
147
|
+
return window.scrollY - (layoutDummy.getBoundingClientRect().top * this.scale + window.scrollY * this.scale);
|
|
148
|
+
} else {
|
|
149
|
+
return window.scrollY + layoutDummy.getBoundingClientRect().top;
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
get width() {
|
|
153
|
+
var clientWidth = document.documentElement.clientWidth;
|
|
154
|
+
if (isIEEdge) {
|
|
155
|
+
// If there's no scrollbar before pinch-zooming, Edge will add
|
|
156
|
+
// a non-layout-affecting overlay scrollbar. This won't be
|
|
157
|
+
// reflected in documentElement.clientWidth so we need to
|
|
158
|
+
// manually subtract it out.
|
|
159
|
+
if (document.documentElement.clientWidth == window.viewPolyfill.unscaledInnerWidth
|
|
160
|
+
&& this.scale > 1) {
|
|
161
|
+
var oldWidth = document.documentElement.clientWidth;
|
|
162
|
+
var prevHeight = layoutDummy.style.height;
|
|
163
|
+
// Lengthen the dummy to add a layout vertical scrollbar.
|
|
164
|
+
layoutDummy.style.height = "200%";
|
|
165
|
+
var scrollbarWidth = oldWidth - document.documentElement.clientWidth;
|
|
166
|
+
layoutDummy.style.width = prevHeight;
|
|
167
|
+
clientWidth -= scrollbarWidth;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return clientWidth / this.scale;
|
|
171
|
+
},
|
|
172
|
+
get height() {
|
|
173
|
+
var clientHeight = document.documentElement.clientHeight;
|
|
174
|
+
if (isIEEdge) {
|
|
175
|
+
// If there's no scrollbar before pinch-zooming, Edge will add
|
|
176
|
+
// a non-layout-affecting overlay scrollbar. This won't be
|
|
177
|
+
// reflected in documentElement.clientHeight so we need to
|
|
178
|
+
// manually subtract it out.
|
|
179
|
+
if (document.documentElement.clientHeight == window.viewPolyfill.unscaledInnerHeight
|
|
180
|
+
&& this.scale > 1) {
|
|
181
|
+
var oldHeight = document.documentElement.clientHeight;
|
|
182
|
+
var prevWidth = layoutDummy.style.width;
|
|
183
|
+
// Widen the dummy to add a layout horizontal scrollbar.
|
|
184
|
+
layoutDummy.style.width = "200%";
|
|
185
|
+
var scrollbarHeight = oldHeight - document.documentElement.clientHeight;
|
|
186
|
+
layoutDummy.style.width = prevWidth;
|
|
187
|
+
clientHeight -= scrollbarHeight;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return clientHeight / this.scale;
|
|
191
|
+
},
|
|
192
|
+
get scale() {
|
|
193
|
+
return window.viewPolyfill.unscaledInnerWidth / window.innerWidth;
|
|
194
|
+
},
|
|
195
|
+
get pageLeft() {
|
|
196
|
+
return window.scrollX;
|
|
197
|
+
},
|
|
198
|
+
get pageTop() {
|
|
199
|
+
return window.scrollY;
|
|
200
|
+
},
|
|
201
|
+
"addEventListener": function(name, func) {
|
|
202
|
+
// TODO: Match event listener semantics. i.e. can't add the same callback twice.
|
|
203
|
+
if (name === 'scroll')
|
|
204
|
+
window.viewPolyfill.scrollEventListeners.push(func);
|
|
205
|
+
else if (name === 'resize')
|
|
206
|
+
window.viewPolyfill.resizeEventListeners.push(func);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
window.visualViewport = viewport;
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
})();
|
|
215
|
+
|
|
216
|
+
}
|
data/opal/browser/screen.rb
CHANGED
|
@@ -4,8 +4,8 @@ module Browser
|
|
|
4
4
|
#
|
|
5
5
|
# @see https://developer.mozilla.org/en-US/docs/Web/API/Window.screen
|
|
6
6
|
class Screen
|
|
7
|
-
include
|
|
8
|
-
include
|
|
7
|
+
include Browser::NativeCachedWrapper
|
|
8
|
+
include Event::Target
|
|
9
9
|
|
|
10
10
|
target {|value|
|
|
11
11
|
Screen.new(value) if Native.is_a?(value, `window.Screen`)
|
|
@@ -59,7 +59,7 @@ class Window
|
|
|
59
59
|
# @!attribute [r] screen
|
|
60
60
|
# @return [Screen] the screen for the window
|
|
61
61
|
def screen
|
|
62
|
-
Screen.new(`#@native.screen`)
|
|
62
|
+
@screen ||= Screen.new(`#@native.screen`)
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
65
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# browser/setup/full - a full set of requires to provide all features at once
|
|
2
|
+
|
|
3
|
+
require 'paggio'
|
|
4
|
+
|
|
5
|
+
require 'browser/setup/large'
|
|
6
|
+
|
|
7
|
+
require 'browser/event/all'
|
|
8
|
+
|
|
9
|
+
require 'browser/dom/builder'
|
|
10
|
+
require 'browser/dom/mutation_observer'
|
|
11
|
+
require 'browser/dom/element/custom'
|
|
12
|
+
|
|
13
|
+
require 'browser/canvas'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# browser/setup/large - a larger set of requires for more complex applications
|
|
2
|
+
#
|
|
3
|
+
# Note - it doesn't include Paggio (or Paggio support) or many events
|
|
4
|
+
|
|
5
|
+
require 'browser/setup/mini'
|
|
6
|
+
|
|
7
|
+
require 'browser/effects'
|
|
8
|
+
require 'browser/http'
|
|
9
|
+
require 'browser/delay'
|
|
10
|
+
require 'browser/interval'
|
|
11
|
+
require 'browser/immediate'
|
|
12
|
+
require 'browser/storage'
|
|
13
|
+
require 'browser/blob'
|
|
14
|
+
require 'browser/animation_frame'
|
|
15
|
+
require 'browser/socket'
|
|
16
|
+
require 'browser/history'
|
|
17
|
+
require 'browser/navigator'
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# browser/setup/traditional, browser, opal-browser
|
|
2
|
+
# ------------------------------------------------
|
|
3
|
+
# A traditional set of requires.
|
|
4
|
+
|
|
5
|
+
require 'native'
|
|
6
|
+
require 'paggio'
|
|
7
|
+
|
|
8
|
+
require 'browser/setup/mini'
|
|
9
|
+
require 'browser/event/all'
|
|
10
|
+
require 'browser/dom/builder'
|
data/opal/browser/socket.rb
CHANGED
|
@@ -4,14 +4,14 @@ module Browser
|
|
|
4
4
|
# connection.
|
|
5
5
|
#
|
|
6
6
|
# @see https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
|
|
7
|
-
class Socket
|
|
7
|
+
class Socket < IO
|
|
8
8
|
def self.supported?
|
|
9
9
|
Browser.supports? :WebSocket
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
include Native
|
|
13
|
-
include IO::Writable
|
|
14
|
-
include
|
|
12
|
+
include Native::Wrapper
|
|
13
|
+
include IO::Writable if defined? IO::Writable
|
|
14
|
+
include Event::Target
|
|
15
15
|
|
|
16
16
|
target {|value|
|
|
17
17
|
Socket.new(value) if Native.is_a?(value, `window.WebSocket`)
|
|
@@ -107,6 +107,10 @@ class Socket
|
|
|
107
107
|
`#@native.send(#{data.to_n})`
|
|
108
108
|
end
|
|
109
109
|
|
|
110
|
+
alias << write
|
|
111
|
+
|
|
112
|
+
alias send write
|
|
113
|
+
|
|
110
114
|
# Close the socket.
|
|
111
115
|
#
|
|
112
116
|
# @param code [Integer, nil] the error code
|