opal-browser 0.2.0.beta1 → 0.2.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/.travis.yml +22 -8
- data/Gemfile +1 -1
- data/README.md +59 -5
- data/index.html.erb +7 -4
- data/lib/opal-browser.rb +1 -0
- data/opal-browser.gemspec +1 -1
- data/opal/browser.rb +1 -0
- data/opal/browser/animation_frame.rb +26 -1
- data/opal/browser/canvas.rb +0 -10
- data/opal/browser/canvas/data.rb +0 -10
- data/opal/browser/canvas/gradient.rb +0 -10
- data/opal/browser/canvas/style.rb +0 -10
- data/opal/browser/canvas/text.rb +0 -10
- data/opal/browser/cookies.rb +6 -8
- data/opal/browser/database/sql.rb +194 -0
- data/opal/browser/delay.rb +25 -7
- data/opal/browser/dom.rb +2 -11
- data/opal/browser/dom/attribute.rb +12 -11
- data/opal/browser/dom/builder.rb +4 -9
- data/opal/browser/dom/document.rb +105 -41
- data/opal/browser/dom/element.rb +317 -231
- data/opal/browser/dom/element/attributes.rb +87 -0
- data/opal/browser/dom/element/data.rb +67 -0
- data/opal/browser/dom/element/input.rb +12 -1
- 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 +77 -10
- data/opal/browser/dom/element/select.rb +36 -0
- data/opal/browser/dom/element/size.rb +5 -0
- data/opal/browser/dom/element/template.rb +9 -0
- data/opal/browser/dom/element/textarea.rb +24 -0
- data/opal/browser/dom/mutation_observer.rb +2 -2
- data/opal/browser/dom/node.rb +93 -51
- data/opal/browser/dom/node_set.rb +66 -48
- data/opal/browser/effects.rb +11 -0
- data/opal/browser/{dom/event.rb → event.rb} +32 -32
- data/opal/browser/{dom/event → event}/animation.rb +2 -2
- data/opal/browser/{dom/event → event}/audio_processing.rb +2 -2
- data/opal/browser/{dom/event → event}/base.rb +65 -7
- data/opal/browser/{dom/event → event}/before_unload.rb +2 -2
- data/opal/browser/{dom/event → event}/clipboard.rb +2 -2
- data/opal/browser/{dom/event → event}/close.rb +2 -2
- data/opal/browser/{dom/event → event}/composition.rb +2 -2
- data/opal/browser/{dom/event → event}/custom.rb +2 -2
- data/opal/browser/{dom/event → event}/device_light.rb +2 -2
- data/opal/browser/{dom/event → event}/device_motion.rb +2 -2
- data/opal/browser/{dom/event → event}/device_orientation.rb +2 -2
- data/opal/browser/{dom/event → event}/device_proximity.rb +2 -2
- data/opal/browser/{dom/event → event}/drag.rb +2 -2
- data/opal/browser/{dom/event → event}/focus.rb +2 -2
- data/opal/browser/{dom/event → event}/gamepad.rb +2 -2
- data/opal/browser/{dom/event → event}/hash_change.rb +2 -2
- data/opal/browser/{dom/event → event}/keyboard.rb +2 -2
- data/opal/browser/{dom/event → event}/message.rb +2 -2
- data/opal/browser/{dom/event → event}/mouse.rb +2 -2
- data/opal/browser/{dom/event → event}/page_transition.rb +2 -2
- data/opal/browser/{dom/event → event}/pop_state.rb +2 -2
- data/opal/browser/{dom/event → event}/progress.rb +2 -2
- data/opal/browser/{dom/event → event}/sensor.rb +2 -2
- data/opal/browser/{dom/event → event}/storage.rb +2 -2
- data/opal/browser/{dom/event → event}/touch.rb +2 -2
- data/opal/browser/{dom/event → event}/ui.rb +2 -2
- data/opal/browser/{dom/event → event}/wheel.rb +2 -2
- data/opal/browser/event_source.rb +1 -1
- data/opal/browser/http.rb +25 -0
- data/opal/browser/http/binary.rb +1 -0
- data/opal/browser/http/headers.rb +16 -2
- data/opal/browser/http/request.rb +14 -38
- data/opal/browser/immediate.rb +9 -3
- data/opal/browser/interval.rb +34 -11
- data/opal/browser/navigator.rb +23 -4
- data/opal/browser/screen.rb +1 -1
- data/opal/browser/socket.rb +5 -1
- data/opal/browser/storage.rb +51 -33
- data/opal/browser/support.rb +59 -4
- data/opal/browser/version.rb +1 -1
- data/opal/browser/window.rb +17 -9
- data/opal/browser/window/size.rb +17 -3
- data/opal/opal-browser.rb +1 -0
- data/spec/database/sql_spec.rb +131 -0
- data/spec/delay_spec.rb +38 -0
- data/spec/dom/attribute_spec.rb +49 -0
- data/spec/dom/builder_spec.rb +25 -8
- data/spec/dom/document_spec.rb +20 -0
- data/spec/dom/element/attributes_spec.rb +52 -0
- data/spec/dom/element_spec.rb +139 -4
- data/spec/dom/node_set_spec.rb +44 -0
- data/spec/interval_spec.rb +50 -0
- data/spec/runner.rb +46 -28
- data/spec/socket_spec.rb +1 -0
- data/spec/spec_helper.rb +0 -4
- data/spec/storage_spec.rb +1 -1
- metadata +57 -39
- data/opal/browser/http/parameters.rb +0 -8
data/opal/browser/immediate.rb
CHANGED
@@ -37,7 +37,6 @@ class Immediate
|
|
37
37
|
|
38
38
|
# @!method prevent
|
39
39
|
# Prevent the immediate from being called once scheduled.
|
40
|
-
|
41
40
|
if Browser.supports? 'Immediate'
|
42
41
|
def dispatch
|
43
42
|
@id = `window.setImmediate(function() {
|
@@ -58,7 +57,7 @@ class Immediate
|
|
58
57
|
def prevent
|
59
58
|
`window.msClearImmediate(#@id)`
|
60
59
|
end
|
61
|
-
elsif Browser.supports? 'Window.send'
|
60
|
+
elsif Browser.supports? 'Window.send (Asynchronous)'
|
62
61
|
# @private
|
63
62
|
@@tasks = {}
|
64
63
|
|
@@ -77,7 +76,7 @@ class Immediate
|
|
77
76
|
@id = rand(1_000_000).to_s
|
78
77
|
@@tasks[@id] = [@function, @arguments, @block]
|
79
78
|
|
80
|
-
$window.send
|
79
|
+
$window.send "#{@@prefix}#{@id}"
|
81
80
|
end
|
82
81
|
|
83
82
|
def prevent
|
@@ -132,6 +131,13 @@ end
|
|
132
131
|
|
133
132
|
end
|
134
133
|
|
134
|
+
module Kernel
|
135
|
+
# (see Immediate.new)
|
136
|
+
def defer(*args, &block)
|
137
|
+
Browser::Immediate.new(block, args).tap(&:dispatch)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
135
141
|
class Proc
|
136
142
|
# (see Immediate.new)
|
137
143
|
def defer(*args, &block)
|
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/navigator.rb
CHANGED
@@ -62,6 +62,27 @@ class Navigator
|
|
62
62
|
alias_native :version
|
63
63
|
end
|
64
64
|
|
65
|
+
# Representation for the arary 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
|
data/opal/browser/screen.rb
CHANGED
data/opal/browser/socket.rb
CHANGED
@@ -11,7 +11,7 @@ class Socket
|
|
11
11
|
|
12
12
|
include Native
|
13
13
|
include IO::Writable
|
14
|
-
include
|
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
|
data/opal/browser/storage.rb
CHANGED
@@ -27,8 +27,6 @@ class Storage
|
|
27
27
|
}]
|
28
28
|
end
|
29
29
|
|
30
|
-
include Enumerable
|
31
|
-
|
32
30
|
# @!attribute [r] name
|
33
31
|
# @return [String] the name of the storage
|
34
32
|
attr_reader :name
|
@@ -45,13 +43,7 @@ class Storage
|
|
45
43
|
@data = {}
|
46
44
|
|
47
45
|
autosave!
|
48
|
-
|
49
|
-
end
|
50
|
-
|
51
|
-
# @!attribute [r] encoded_name
|
52
|
-
# @return [String] the generated name
|
53
|
-
def encoded_name
|
54
|
-
"$opal.storage.#@name"
|
46
|
+
reload
|
55
47
|
end
|
56
48
|
|
57
49
|
# Check if autosaving is enabled.
|
@@ -72,6 +64,8 @@ class Storage
|
|
72
64
|
@autosave = false
|
73
65
|
end
|
74
66
|
|
67
|
+
include Enumerable
|
68
|
+
|
75
69
|
# Iterate over the (key, value) pairs in the storage.
|
76
70
|
#
|
77
71
|
# @yield [key, value]
|
@@ -83,9 +77,8 @@ class Storage
|
|
83
77
|
self
|
84
78
|
end
|
85
79
|
|
86
|
-
|
87
|
-
|
88
|
-
@data[key]
|
80
|
+
def method_missing(*args, &block)
|
81
|
+
@data.__send__(*args, &block)
|
89
82
|
end
|
90
83
|
|
91
84
|
# Set a value in the storage.
|
@@ -98,7 +91,7 @@ class Storage
|
|
98
91
|
# Delete a value from the storage.
|
99
92
|
def delete(key)
|
100
93
|
@data.delete(key).tap {
|
101
|
-
save if autosave
|
94
|
+
save if autosave?
|
102
95
|
}
|
103
96
|
end
|
104
97
|
|
@@ -120,57 +113,82 @@ class Storage
|
|
120
113
|
end
|
121
114
|
end
|
122
115
|
|
123
|
-
#
|
124
|
-
|
116
|
+
# Call the block between a [#reload] and [#save].
|
117
|
+
def commit(&block)
|
118
|
+
autosave = @autosave
|
119
|
+
@autosave = false
|
120
|
+
result = nil
|
121
|
+
|
122
|
+
reload
|
123
|
+
|
124
|
+
begin
|
125
|
+
result = block.call
|
126
|
+
save
|
127
|
+
rescue
|
128
|
+
reload
|
129
|
+
raise
|
130
|
+
ensure
|
131
|
+
@autosave = autosave
|
132
|
+
end
|
133
|
+
|
134
|
+
result
|
135
|
+
end
|
136
|
+
|
137
|
+
def to_h
|
138
|
+
@data
|
139
|
+
end
|
140
|
+
|
141
|
+
# @!method reload
|
142
|
+
# Load the storage.
|
125
143
|
|
126
144
|
# @!method save
|
127
145
|
# Persist the current state to the storage.
|
128
146
|
|
129
147
|
if Browser.supports? 'Storage.local'
|
130
|
-
def
|
131
|
-
replace `#@window.localStorage[
|
148
|
+
def reload
|
149
|
+
replace `#@window.localStorage[#@name] || '{}'`
|
132
150
|
end
|
133
151
|
|
134
152
|
def save
|
135
|
-
`#@window.localStorage[
|
153
|
+
`#@window.localStorage[#@name] = #{JSON.dump(self)}`
|
136
154
|
end
|
137
155
|
elsif Browser.supports? 'Storage.global'
|
138
|
-
def
|
139
|
-
replace `#@window.globalStorage[#@window.location.hostname][
|
156
|
+
def reload
|
157
|
+
replace `#@window.globalStorage[#@window.location.hostname][#@name] || '{}'`
|
140
158
|
end
|
141
159
|
|
142
160
|
def save
|
143
|
-
`#@window.globalStorage[#@window.location.hostname][
|
161
|
+
`#@window.globalStorage[#@window.location.hostname][#@name] = #{JSON.dump(self)}`
|
144
162
|
end
|
145
163
|
elsif Browser.supports? 'Element.addBehavior'
|
146
|
-
def
|
164
|
+
def reload
|
147
165
|
%x{
|
148
166
|
#@element = #@window.document.createElement('link');
|
149
167
|
#@element.addBehavior('#default#userData');
|
150
168
|
|
151
169
|
#@window.document.getElementsByTagName('head')[0].appendChild(#@element);
|
152
170
|
|
153
|
-
#@element.load(
|
171
|
+
#@element.load(#@name);
|
154
172
|
}
|
155
173
|
|
156
|
-
replace `#@element.getAttribute(
|
174
|
+
replace `#@element.getAttribute(#@name) || '{}'`
|
157
175
|
end
|
158
176
|
|
159
177
|
def save
|
160
178
|
%x{
|
161
|
-
#@element.setAttribute(
|
162
|
-
#@element.save(
|
179
|
+
#@element.setAttribute(#@name, #{JSON.dump(self)});
|
180
|
+
#@element.save(#@name);
|
163
181
|
}
|
164
182
|
end
|
165
183
|
else
|
166
|
-
def
|
184
|
+
def reload
|
167
185
|
$document.cookies.options expires: 60 * 60 * 24 * 365
|
168
186
|
|
169
|
-
replace $document.cookies[
|
187
|
+
replace $document.cookies[@name]
|
170
188
|
end
|
171
189
|
|
172
190
|
def save
|
173
|
-
$document.cookies[
|
191
|
+
$document.cookies[@name] = JSON.dump(self)
|
174
192
|
end
|
175
193
|
end
|
176
194
|
|
@@ -182,7 +200,7 @@ class Storage
|
|
182
200
|
|
183
201
|
io << JSON.create_id.to_json << ":" << self.class.name.to_json << ","
|
184
202
|
|
185
|
-
each {|key, value|
|
203
|
+
@data.each {|key, value|
|
186
204
|
io << key.to_json.to_json << ":" << value.to_json << ","
|
187
205
|
}
|
188
206
|
|
@@ -202,12 +220,12 @@ class SessionStorage < Storage
|
|
202
220
|
Browser.supports? 'Storage.session'
|
203
221
|
end
|
204
222
|
|
205
|
-
def
|
206
|
-
replace `#@window.sessionStorage[
|
223
|
+
def reload
|
224
|
+
replace `#@window.sessionStorage[#@name] || '{}'`
|
207
225
|
end
|
208
226
|
|
209
227
|
def save
|
210
|
-
`#@window.sessionStorage[
|
228
|
+
`#@window.sessionStorage[#@name] = #{JSON.dump(self)}`
|
211
229
|
end
|
212
230
|
end
|
213
231
|
|
data/opal/browser/support.rb
CHANGED
@@ -1,9 +1,14 @@
|
|
1
|
+
# The engine the browser is running on.
|
2
|
+
#
|
3
|
+
# Keep in mind it uses the user agent to know, so it's not reliable in case of
|
4
|
+
# spoofing.
|
1
5
|
BROWSER_ENGINE = `/MSIE|WebKit|Presto|Gecko/.exec(navigator.userAgent)[0]`.downcase rescue :unknown
|
2
6
|
|
3
7
|
module Browser
|
4
8
|
# @private
|
5
9
|
@support = `{}`
|
6
10
|
|
11
|
+
# Check if the browser supports the given feature.
|
7
12
|
def self.supports?(feature)
|
8
13
|
if defined?(`#@support[#{feature}]`)
|
9
14
|
return `#@support[#{feature}]`
|
@@ -25,8 +30,11 @@ module Browser
|
|
25
30
|
when 'ActiveX'
|
26
31
|
defined?(`window.ActiveXObject`)
|
27
32
|
|
33
|
+
when 'WebSQL'
|
34
|
+
defined?(`window.openDatabase`)
|
35
|
+
|
28
36
|
when 'Query.css'
|
29
|
-
defined?(`
|
37
|
+
defined?(`document.querySelectorAll`)
|
30
38
|
|
31
39
|
when 'Query.xpath'
|
32
40
|
defined?(`document.evaluate`)
|
@@ -62,6 +70,9 @@ module Browser
|
|
62
70
|
defined?(`document.documentElement.currentStyle`)
|
63
71
|
|
64
72
|
when 'Window.send'
|
73
|
+
defined?(`window.postMessage`)
|
74
|
+
|
75
|
+
when 'Window.send (Asynchronous)'
|
65
76
|
if defined?(`window.postMessage`) && !defined?(`window.importScripts`)
|
66
77
|
%x{
|
67
78
|
var ok = true,
|
@@ -70,11 +81,14 @@ module Browser
|
|
70
81
|
window.onmessage = function() { ok = false; };
|
71
82
|
window.postMessage("", "*")
|
72
83
|
window.onmessage = old;
|
73
|
-
}
|
74
84
|
|
75
|
-
|
85
|
+
return ok;
|
86
|
+
}
|
76
87
|
end
|
77
88
|
|
89
|
+
when 'Window.send (Synchronous)'
|
90
|
+
!supports?('Window.send (Asynchronous)')
|
91
|
+
|
78
92
|
when 'Window.innerSize'
|
79
93
|
defined?(`window.innerHeight`)
|
80
94
|
|
@@ -87,8 +101,48 @@ module Browser
|
|
87
101
|
when 'Window.pageOffset'
|
88
102
|
defined?(`window.pageXOffset`)
|
89
103
|
|
104
|
+
when 'Attr.isId'
|
105
|
+
%x{
|
106
|
+
var div = document.createElement('div');
|
107
|
+
div.setAttribute('id', 'xxxxxxxxxxxxx');
|
108
|
+
|
109
|
+
return typeof(div.attributes['id'].isId) !== "undefined";
|
110
|
+
}
|
111
|
+
|
90
112
|
when 'Element.addBehavior'
|
91
|
-
defined?(`document.
|
113
|
+
defined?(`document.documentElement.addBehavior`)
|
114
|
+
|
115
|
+
when 'Element.className'
|
116
|
+
%x{
|
117
|
+
var div = document.createElement("div");
|
118
|
+
div.setAttribute('className', 'x');
|
119
|
+
|
120
|
+
return div.className === 'x';
|
121
|
+
}
|
122
|
+
|
123
|
+
when 'Element.class'
|
124
|
+
%x{
|
125
|
+
var div = document.createElement("div");
|
126
|
+
div.setAttribute('class', 'x');
|
127
|
+
|
128
|
+
return div.className === 'x';
|
129
|
+
}
|
130
|
+
|
131
|
+
when 'Element.for'
|
132
|
+
%x{
|
133
|
+
var label = document.createElement("label");
|
134
|
+
label.setAttribute('for', 'x');
|
135
|
+
|
136
|
+
return label.htmlFor === 'x';
|
137
|
+
}
|
138
|
+
|
139
|
+
when 'Element.htmlFor'
|
140
|
+
%x{
|
141
|
+
var label = document.createElement("label");
|
142
|
+
label.setAttribute('htmlFor', 'x');
|
143
|
+
|
144
|
+
return label.htmlFor === 'x';
|
145
|
+
}
|
92
146
|
|
93
147
|
when 'Element.clientSize'
|
94
148
|
defined?(`document.documentElement.clientHeight`)
|
@@ -220,6 +274,7 @@ module Browser
|
|
220
274
|
`#@support[#{feature}] = #{support}`
|
221
275
|
end
|
222
276
|
|
277
|
+
# Check if the given polyfill is loaded.
|
223
278
|
def self.loaded?(name)
|
224
279
|
case name
|
225
280
|
when 'Sizzle'
|