opal-browser 0.1.0.beta1 → 0.2.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +60 -0
- data/.yardopts +1 -1
- data/Gemfile +7 -2
- data/LICENSE +19 -0
- data/README.md +74 -10
- data/config.ru +2 -1
- data/index.html.erb +22 -0
- data/opal-browser.gemspec +9 -11
- data/opal/browser.rb +1 -1
- data/opal/browser/animation_frame.rb +66 -9
- data/opal/browser/canvas.rb +72 -18
- data/opal/browser/canvas/data.rb +1 -1
- data/opal/browser/console.rb +3 -37
- data/opal/browser/cookies.rb +80 -24
- data/opal/browser/css/declaration.rb +0 -5
- data/opal/browser/{timeout.rb → delay.rb} +13 -13
- data/opal/browser/dom.rb +0 -2
- data/opal/browser/dom/attribute.rb +6 -0
- data/opal/browser/dom/builder.rb +4 -8
- data/opal/browser/dom/character_data.rb +43 -7
- data/opal/browser/dom/document.rb +13 -11
- data/opal/browser/dom/element.rb +127 -29
- data/opal/browser/dom/element/image.rb +23 -0
- data/opal/browser/dom/element/offset.rb +27 -10
- data/opal/browser/dom/element/scroll.rb +32 -12
- data/opal/browser/dom/element/size.rb +29 -0
- data/opal/browser/dom/event.rb +88 -75
- data/opal/browser/dom/event/animation.rb +16 -4
- data/opal/browser/dom/event/audio_processing.rb +6 -4
- data/opal/browser/dom/event/base.rb +229 -64
- data/opal/browser/dom/event/before_unload.rb +6 -4
- data/opal/browser/dom/event/clipboard.rb +6 -4
- data/opal/browser/dom/event/close.rb +16 -4
- data/opal/browser/dom/event/composition.rb +16 -4
- data/opal/browser/dom/event/custom.rb +43 -8
- data/opal/browser/dom/event/device_light.rb +6 -4
- data/opal/browser/dom/event/device_motion.rb +17 -4
- data/opal/browser/dom/event/device_orientation.rb +16 -4
- data/opal/browser/dom/event/device_proximity.rb +6 -4
- data/opal/browser/dom/event/drag.rb +34 -28
- data/opal/browser/dom/event/focus.rb +21 -5
- data/opal/browser/dom/event/gamepad.rb +33 -20
- data/opal/browser/dom/event/hash_change.rb +6 -4
- data/opal/browser/dom/event/keyboard.rb +45 -23
- data/opal/browser/dom/event/message.rb +28 -8
- data/opal/browser/dom/event/mouse.rb +26 -25
- data/opal/browser/dom/event/page_transition.rb +6 -4
- data/opal/browser/dom/event/pop_state.rb +16 -4
- data/opal/browser/dom/event/progress.rb +16 -4
- data/opal/browser/dom/event/sensor.rb +6 -4
- data/opal/browser/dom/event/storage.rb +6 -4
- data/opal/browser/dom/event/touch.rb +10 -19
- data/opal/browser/dom/event/ui.rb +19 -3
- data/opal/browser/dom/event/wheel.rb +2 -2
- data/opal/browser/dom/mutation_observer.rb +65 -5
- data/opal/browser/dom/node.rb +164 -59
- data/opal/browser/dom/node_set.rb +4 -0
- data/opal/browser/dom/text.rb +16 -1
- data/opal/browser/event_source.rb +5 -2
- data/opal/browser/history.rb +51 -15
- data/opal/browser/http.rb +22 -7
- data/opal/browser/http/headers.rb +5 -0
- data/opal/browser/http/request.rb +40 -10
- data/opal/browser/immediate.rb +123 -9
- data/opal/browser/interval.rb +8 -13
- data/opal/browser/location.rb +13 -3
- data/opal/browser/navigator.rb +9 -6
- data/opal/browser/screen.rb +31 -5
- data/opal/browser/socket.rb +8 -4
- data/opal/browser/storage.rb +118 -33
- data/opal/browser/support.rb +232 -0
- data/opal/browser/utils.rb +24 -6
- data/opal/browser/version.rb +1 -1
- data/opal/browser/window.rb +1 -2
- data/opal/browser/window/scroll.rb +21 -11
- data/opal/browser/window/size.rb +16 -6
- data/opal/browser/window/view.rb +23 -5
- data/spec/dom/builder_spec.rb +19 -19
- data/spec/dom/document_spec.rb +6 -6
- data/spec/dom/element_spec.rb +5 -5
- data/spec/dom/event_spec.rb +20 -20
- data/spec/dom/mutation_observer_spec.rb +5 -5
- data/spec/dom/node_spec.rb +39 -27
- data/spec/dom_spec.rb +10 -8
- data/spec/event_source_spec.rb +12 -12
- data/spec/history_spec.rb +24 -15
- data/spec/http_spec.rb +18 -17
- data/spec/immediate_spec.rb +9 -7
- data/spec/runner.rb +114 -0
- data/spec/socket_spec.rb +8 -8
- data/spec/spec_helper.rb +1 -0
- data/spec/storage_spec.rb +6 -6
- data/spec/wgxpath.install.js +49 -0
- data/spec/window_spec.rb +2 -2
- metadata +21 -54
- data/opal/browser/compatibility.rb +0 -59
- data/opal/browser/compatibility/animation_frame.rb +0 -93
- data/opal/browser/compatibility/dom/document/window.rb +0 -15
- data/opal/browser/compatibility/dom/element/css.rb +0 -15
- data/opal/browser/compatibility/dom/element/matches.rb +0 -31
- data/opal/browser/compatibility/dom/element/offset.rb +0 -20
- data/opal/browser/compatibility/dom/element/scroll.rb +0 -25
- data/opal/browser/compatibility/dom/element/style.rb +0 -15
- data/opal/browser/compatibility/dom/mutation_observer.rb +0 -47
- data/opal/browser/compatibility/http/request.rb +0 -15
- data/opal/browser/compatibility/immediate.rb +0 -107
- data/opal/browser/compatibility/window/scroll.rb +0 -27
- data/opal/browser/compatibility/window/size.rb +0 -13
- data/opal/browser/compatibility/window/view.rb +0 -13
- data/opal/browser/dom/compatibility.rb +0 -8
- data/opal/browser/http/compatibility.rb +0 -1
- data/opal/browser/window/compatibility.rb +0 -3
data/opal/browser/dom/text.rb
CHANGED
@@ -1,16 +1,31 @@
|
|
1
1
|
module Browser; module DOM
|
2
2
|
|
3
|
+
# Encapsulates a text node.
|
4
|
+
#
|
5
|
+
# @see https://developer.mozilla.org/en-US/docs/Web/API/Text
|
3
6
|
class Text < CharacterData
|
7
|
+
# (see Document#create_text)
|
4
8
|
def self.create(*args)
|
5
9
|
$document.create_text(*args)
|
6
10
|
end
|
7
11
|
|
12
|
+
# @!attribute [r] whole
|
13
|
+
# @return [String] the whole text
|
14
|
+
#
|
15
|
+
# @see https://developer.mozilla.org/en-US/docs/Web/API/Text.wholeText
|
8
16
|
def whole
|
9
17
|
`#@native.wholeText`
|
10
18
|
end
|
11
19
|
|
20
|
+
# Split the text node at a given offset.
|
21
|
+
#
|
22
|
+
# @param offset [Integer] the offset where to split the text node
|
23
|
+
#
|
24
|
+
# @return [Text] the newly created text node
|
25
|
+
#
|
26
|
+
# @see https://developer.mozilla.org/en-US/docs/Web/API/Text.splitText
|
12
27
|
def split(offset)
|
13
|
-
`#@native.splitText(offset)`
|
28
|
+
DOM(`#@native.splitText(offset)`)
|
14
29
|
end
|
15
30
|
|
16
31
|
def inspect
|
@@ -1,9 +1,12 @@
|
|
1
1
|
module Browser
|
2
2
|
|
3
|
-
#
|
3
|
+
# An {EventSource} allows you to receive events from a server in real-time,
|
4
|
+
# similar to long-polling but not exactly.
|
5
|
+
#
|
6
|
+
# @see https://developer.mozilla.org/en-US/docs/Web/API/EventSource
|
4
7
|
class EventSource
|
5
8
|
def self.supported?
|
6
|
-
|
9
|
+
Browser.supports? :EventSource
|
7
10
|
end
|
8
11
|
|
9
12
|
include Native
|
data/opal/browser/history.rb
CHANGED
@@ -2,50 +2,86 @@ require 'browser/location'
|
|
2
2
|
|
3
3
|
module Browser
|
4
4
|
|
5
|
+
# {History} allows manipulation of the session history.
|
6
|
+
#
|
7
|
+
# @see https://developer.mozilla.org/en-US/docs/Web/API/History
|
5
8
|
class History
|
9
|
+
# Check if HTML5 history is supported.
|
10
|
+
def self.supported?
|
11
|
+
Browser.supports? 'History'
|
12
|
+
end
|
13
|
+
|
6
14
|
include Native
|
7
15
|
|
16
|
+
# @!attribute [r] length
|
17
|
+
# @return [Integer] how many items are in the history
|
8
18
|
alias_native :length
|
9
19
|
|
20
|
+
# Go back in the history.
|
21
|
+
#
|
22
|
+
# @param number [Integer] how many items to go back
|
10
23
|
def back(number = 1)
|
11
24
|
`#@native.go(-number)`
|
12
|
-
|
13
|
-
self
|
14
25
|
end
|
15
26
|
|
27
|
+
# Go forward in the history.
|
28
|
+
#
|
29
|
+
# @param number [Integer] how many items to go forward
|
16
30
|
def forward(number = 1)
|
17
31
|
`#@native.go(number)`
|
18
|
-
|
19
|
-
self
|
20
32
|
end
|
21
33
|
|
22
|
-
|
34
|
+
# Push an item in the history.
|
35
|
+
#
|
36
|
+
# @param item [String] the item to push in the history
|
37
|
+
# @param data [Object] additional state to push
|
38
|
+
def push(item, data = nil)
|
23
39
|
data = `null` if data.nil?
|
24
40
|
|
25
|
-
`#@native.pushState(data, null,
|
26
|
-
|
27
|
-
self
|
41
|
+
`#@native.pushState(data, null, item)`
|
28
42
|
end
|
29
43
|
|
30
|
-
|
44
|
+
# Replace the current history item with another.
|
45
|
+
#
|
46
|
+
# @param item [String] the item to replace with
|
47
|
+
# @param data [Object] additional state to replace
|
48
|
+
def replace(item, data = nil)
|
31
49
|
data = `null` if data.nil?
|
32
50
|
|
33
|
-
`#@native.replaceState(data, null,
|
51
|
+
`#@native.replaceState(data, null, item)`
|
34
52
|
end
|
35
53
|
|
54
|
+
# @!attribute [r] current
|
55
|
+
# @return [String] the current item
|
36
56
|
def current
|
37
57
|
$window.location.path
|
38
58
|
end
|
39
59
|
|
40
|
-
|
41
|
-
|
60
|
+
# @!attribute [r] state
|
61
|
+
# @return [Object] the current state
|
62
|
+
if Browser.supports? 'History.state'
|
63
|
+
def state
|
64
|
+
%x{
|
65
|
+
var state = #@native.state;
|
66
|
+
|
67
|
+
if (state == null) {
|
68
|
+
return nil;
|
69
|
+
}
|
70
|
+
else {
|
71
|
+
return state;
|
72
|
+
}
|
73
|
+
}
|
74
|
+
end
|
75
|
+
else
|
76
|
+
def state
|
77
|
+
raise NotImplementedError, 'history state unsupported'
|
78
|
+
end
|
42
79
|
end
|
43
80
|
end
|
44
81
|
|
45
82
|
class Window
|
46
|
-
#
|
47
|
-
#
|
48
|
-
# @return [History]
|
83
|
+
# @!attribute [r] history
|
84
|
+
# @return [History] the history for this window
|
49
85
|
def history
|
50
86
|
History.new(`#@native.history`) if `#@native.history`
|
51
87
|
end
|
data/opal/browser/http.rb
CHANGED
@@ -4,17 +4,21 @@ require 'browser/http/binary'
|
|
4
4
|
require 'browser/http/headers'
|
5
5
|
require 'browser/http/request'
|
6
6
|
require 'browser/http/response'
|
7
|
-
require 'browser/http/compatibility'
|
8
7
|
|
9
8
|
module Browser
|
10
9
|
|
11
10
|
module HTTP
|
11
|
+
def self.supported?
|
12
|
+
Browser.supports?('XHR') || Browser.supports?('ActiveXObject')
|
13
|
+
end
|
14
|
+
|
12
15
|
# Send an asynchronous request.
|
13
16
|
#
|
14
17
|
# @param method [Symbol] the HTTP method to use
|
15
18
|
# @param url [String] the URL to request
|
16
19
|
# @param data [String, Hash] the data to send
|
17
|
-
#
|
20
|
+
#
|
21
|
+
# @return [Promise] a promise that will be resolved with the response
|
18
22
|
def self.send(method, url, data = nil, &block)
|
19
23
|
Promise.new.tap {|promise|
|
20
24
|
Request.new(&block).tap {|req|
|
@@ -32,7 +36,8 @@ module HTTP
|
|
32
36
|
# Send an asynchronous GET request.
|
33
37
|
#
|
34
38
|
# @param url [String] the URL to request
|
35
|
-
#
|
39
|
+
#
|
40
|
+
# @return [Promise] a promise that will be resolved with the response
|
36
41
|
def self.get(url, &block)
|
37
42
|
send(:get, url, &block)
|
38
43
|
end
|
@@ -40,7 +45,8 @@ module HTTP
|
|
40
45
|
# Send an asynchronous HEAD request.
|
41
46
|
#
|
42
47
|
# @param url [String] the URL to request
|
43
|
-
#
|
48
|
+
#
|
49
|
+
# @return [Promise] a promise that will be resolved with the response
|
44
50
|
def self.head(url, &block)
|
45
51
|
send(:head, url, &block)
|
46
52
|
end
|
@@ -49,7 +55,8 @@ module HTTP
|
|
49
55
|
#
|
50
56
|
# @param url [String] the URL to request
|
51
57
|
# @param data [String, Hash] the data to send
|
52
|
-
#
|
58
|
+
#
|
59
|
+
# @return [Promise] a promise that will be resolved with the response
|
53
60
|
def self.post(url, data = nil, &block)
|
54
61
|
send(:post, url, data, &block)
|
55
62
|
end
|
@@ -58,7 +65,8 @@ module HTTP
|
|
58
65
|
#
|
59
66
|
# @param url [String] the URL to request
|
60
67
|
# @param data [String, Hash] the data to send
|
61
|
-
#
|
68
|
+
#
|
69
|
+
# @return [Promise] a promise that will be resolved with the response
|
62
70
|
def self.put(url, data = nil, &block)
|
63
71
|
send(:put, url, data, &block)
|
64
72
|
end
|
@@ -67,7 +75,8 @@ module HTTP
|
|
67
75
|
#
|
68
76
|
# @param url [String] the URL to request
|
69
77
|
# @param data [String, Hash] the data to send
|
70
|
-
#
|
78
|
+
#
|
79
|
+
# @return [Promise] a promise that will be resolved with the response
|
71
80
|
def self.delete(url, data = nil, &block)
|
72
81
|
send(:delete, url, data, &block)
|
73
82
|
end
|
@@ -77,6 +86,7 @@ module HTTP
|
|
77
86
|
# @param method [Symbol] the HTTP method to use
|
78
87
|
# @param url [String] the URL to request
|
79
88
|
# @param data [String, Hash] the data to send
|
89
|
+
#
|
80
90
|
# @return [Response] the response
|
81
91
|
def self.send!(method, url, data = nil, &block)
|
82
92
|
Request.new(&block).open(method, url, false).send(data)
|
@@ -85,6 +95,7 @@ module HTTP
|
|
85
95
|
# Send a synchronous GET request.
|
86
96
|
#
|
87
97
|
# @param url [String] the URL to request
|
98
|
+
#
|
88
99
|
# @return [Response] the response
|
89
100
|
def self.get!(url, &block)
|
90
101
|
send!(:get, url, &block)
|
@@ -93,6 +104,7 @@ module HTTP
|
|
93
104
|
# Send a synchronous HEAD request.
|
94
105
|
#
|
95
106
|
# @param url [String] the URL to request
|
107
|
+
#
|
96
108
|
# @return [Response] the response
|
97
109
|
def self.head!(url, &block)
|
98
110
|
send!(:head, url, &block)
|
@@ -102,6 +114,7 @@ module HTTP
|
|
102
114
|
#
|
103
115
|
# @param url [String] the URL to request
|
104
116
|
# @param data [String, Hash] the data to send
|
117
|
+
#
|
105
118
|
# @return [Response] the response
|
106
119
|
def self.post!(url, data = nil, &block)
|
107
120
|
send!(:post, url, data, &block)
|
@@ -111,6 +124,7 @@ module HTTP
|
|
111
124
|
#
|
112
125
|
# @param url [String] the URL to request
|
113
126
|
# @param data [String, Hash] the data to send
|
127
|
+
#
|
114
128
|
# @return [Response] the response
|
115
129
|
def self.put!(url, data = nil, &block)
|
116
130
|
send!(:put, url, data, &block)
|
@@ -120,6 +134,7 @@ module HTTP
|
|
120
134
|
#
|
121
135
|
# @param url [String] the URL to request
|
122
136
|
# @param data [String, Hash] the data to send
|
137
|
+
#
|
123
138
|
# @return [Response] the response
|
124
139
|
def self.delete!(url, data = nil, &block)
|
125
140
|
send!(:delete, url, data, &block)
|
@@ -8,7 +8,6 @@ class Request
|
|
8
8
|
# @param method [Symbol] the HTTP method to use
|
9
9
|
# @param url [String, #to_s] the URL to request
|
10
10
|
# @param parameters [String, Hash] the parameters to send
|
11
|
-
#
|
12
11
|
def self.open(method, url, parameters = nil, &block)
|
13
12
|
request = new(&block)
|
14
13
|
request.open(method, url)
|
@@ -44,11 +43,13 @@ class Request
|
|
44
43
|
def initialize(&block)
|
45
44
|
super(transport)
|
46
45
|
|
46
|
+
@parameters = {}
|
47
|
+
@query = {}
|
47
48
|
@headers = Headers[DEFAULT_HEADERS]
|
48
49
|
@method = :get
|
49
50
|
@asynchronous = true
|
50
51
|
@binary = false
|
51
|
-
@cacheable
|
52
|
+
@cacheable = true
|
52
53
|
@opened = false
|
53
54
|
@sent = false
|
54
55
|
@completed = false
|
@@ -61,8 +62,20 @@ class Request
|
|
61
62
|
end if block
|
62
63
|
end
|
63
64
|
|
64
|
-
|
65
|
-
|
65
|
+
# @!method transport
|
66
|
+
# @private
|
67
|
+
if Browser.supports? :XHR
|
68
|
+
def transport
|
69
|
+
`new XMLHttpRequest()`
|
70
|
+
end
|
71
|
+
elsif Browser.supports? :ActiveX
|
72
|
+
def transport
|
73
|
+
`new ActiveXObject("MSXML2.XMLHTTP.3.0")`
|
74
|
+
end
|
75
|
+
else
|
76
|
+
def transport
|
77
|
+
raise NotImplementedError
|
78
|
+
end
|
66
79
|
end
|
67
80
|
|
68
81
|
# Check if the request has been opened.
|
@@ -183,11 +196,18 @@ class Request
|
|
183
196
|
#
|
184
197
|
# @param hash [Hash] the parameters
|
185
198
|
#
|
186
|
-
# @return [
|
187
|
-
def parameters(hash)
|
188
|
-
@parameters = hash
|
199
|
+
# @return [Hash]
|
200
|
+
def parameters(hash = nil)
|
201
|
+
hash ? @parameters = hash : @parameters
|
202
|
+
end
|
189
203
|
|
190
|
-
|
204
|
+
# Set the URI query.
|
205
|
+
#
|
206
|
+
# @param hash [Hash] the query
|
207
|
+
#
|
208
|
+
# @return [Hash]
|
209
|
+
def query(hash = nil)
|
210
|
+
hash ? @query = hash : @query
|
191
211
|
end
|
192
212
|
|
193
213
|
# Register an event on the request.
|
@@ -223,7 +243,17 @@ class Request
|
|
223
243
|
url = @url
|
224
244
|
|
225
245
|
unless cacheable?
|
226
|
-
|
246
|
+
@query[:_] = rand
|
247
|
+
end
|
248
|
+
|
249
|
+
unless @query.empty?
|
250
|
+
if url.include???
|
251
|
+
url += ?&
|
252
|
+
else
|
253
|
+
url += ??
|
254
|
+
end
|
255
|
+
|
256
|
+
url += @query.encode_uri
|
227
257
|
end
|
228
258
|
|
229
259
|
`#@native.open(#{@method.to_s.upcase}, #{url.to_s}, #{@asynchronous}, #{@user.to_n}, #{@password.to_n})`
|
@@ -279,7 +309,7 @@ class Request
|
|
279
309
|
|
280
310
|
if String === parameters
|
281
311
|
data = parameters
|
282
|
-
elsif Hash === parameters
|
312
|
+
elsif Hash === parameters && !parameters.empty?
|
283
313
|
data = parameters.map {|vals|
|
284
314
|
vals.map(&:encode_uri_component).join(?=)
|
285
315
|
}.join(?&)
|
data/opal/browser/immediate.rb
CHANGED
@@ -1,9 +1,30 @@
|
|
1
|
-
require '
|
1
|
+
require 'promise'
|
2
2
|
|
3
3
|
module Browser
|
4
4
|
|
5
|
-
#
|
5
|
+
# Class to easily create and dispatch an immediate call.
|
6
|
+
#
|
7
|
+
# Immediate calls are deferred function calls that happen as soon as they can
|
8
|
+
# be scheduled.
|
9
|
+
#
|
10
|
+
# Compatibility
|
11
|
+
# -------------
|
12
|
+
# The compatibility layer will try various implementations in the following
|
13
|
+
# order.
|
14
|
+
#
|
15
|
+
# + [setImmediate](https://developer.mozilla.org/en-US/docs/Web/API/Window.setImmediate)
|
16
|
+
# + [postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Window.postMessage)
|
17
|
+
# + [readystatechange](https://developer.mozilla.org/en-US/docs/Web/Reference/Events/readystatechange)
|
18
|
+
# + [setTimeout](https://developer.mozilla.org/en/docs/Web/API/window.setTimeout)
|
19
|
+
#
|
20
|
+
# The order has been chosen from best to worst for both performance and
|
21
|
+
# preemptiveness.
|
6
22
|
class Immediate
|
23
|
+
# Create an immediate for the given function which will be called with the
|
24
|
+
# arguments and block.
|
25
|
+
#
|
26
|
+
# @param func [Proc] the function to call
|
27
|
+
# @param args [Array] the arguments to call it with
|
7
28
|
def initialize(func, args, &block)
|
8
29
|
@aborted = false
|
9
30
|
@function = func
|
@@ -11,14 +32,89 @@ class Immediate
|
|
11
32
|
@block = block
|
12
33
|
end
|
13
34
|
|
14
|
-
|
15
|
-
|
16
|
-
end unless method_defined? :dispatch
|
35
|
+
# @!method dispatch
|
36
|
+
# Dispatch the immediate.
|
17
37
|
|
18
|
-
|
19
|
-
|
20
|
-
end unless method_defined? :prevent
|
38
|
+
# @!method prevent
|
39
|
+
# Prevent the immediate from being called once scheduled.
|
21
40
|
|
41
|
+
if Browser.supports? 'Immediate'
|
42
|
+
def dispatch
|
43
|
+
@id = `window.setImmediate(function() {
|
44
|
+
#{@function.call(*@arguments, &@block)};
|
45
|
+
})`
|
46
|
+
end
|
47
|
+
|
48
|
+
def prevent
|
49
|
+
`window.clearImmediate(#@id)`
|
50
|
+
end
|
51
|
+
elsif Browser.supports? 'Immediate (Internet Explorer)'
|
52
|
+
def dispatch
|
53
|
+
@id = `window.msSetImmediate(function() {
|
54
|
+
#{@function.call(*@arguments, &@block)};
|
55
|
+
})`
|
56
|
+
end
|
57
|
+
|
58
|
+
def prevent
|
59
|
+
`window.msClearImmediate(#@id)`
|
60
|
+
end
|
61
|
+
elsif Browser.supports? 'Window.send'
|
62
|
+
# @private
|
63
|
+
@@tasks = {}
|
64
|
+
|
65
|
+
# @private
|
66
|
+
@@prefix = "opal.browser.immediate.#{rand(1_000_000)}."
|
67
|
+
|
68
|
+
$window.on :message do |e|
|
69
|
+
if String === e.data && e.data.start_with?(@@prefix)
|
70
|
+
if task = @@tasks.delete(e.data[@@prefix.length .. -1])
|
71
|
+
task[0].call(*task[1], &task[2])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def dispatch
|
77
|
+
@id = rand(1_000_000).to_s
|
78
|
+
@@tasks[@id] = [@function, @arguments, @block]
|
79
|
+
|
80
|
+
$window.send! "#{@@prefix}#{@id}"
|
81
|
+
end
|
82
|
+
|
83
|
+
def prevent
|
84
|
+
@@tasks.delete(@id)
|
85
|
+
end
|
86
|
+
elsif Browser.supports? 'Event.readystatechange'
|
87
|
+
def dispatch
|
88
|
+
%x{
|
89
|
+
var script = document.createElement("script");
|
90
|
+
|
91
|
+
script.onreadystatechange = function() {
|
92
|
+
if (!#{aborted?}) {
|
93
|
+
#{@function.call(*@arguments, &@block)};
|
94
|
+
}
|
95
|
+
|
96
|
+
script.onreadystatechange = null;
|
97
|
+
script.parentNode.removeChild(script);
|
98
|
+
};
|
99
|
+
|
100
|
+
document.documentElement.appendChild(script);
|
101
|
+
}
|
102
|
+
end
|
103
|
+
|
104
|
+
def prevent; end
|
105
|
+
else
|
106
|
+
def dispatch
|
107
|
+
@id = `window.setTimeout(function() {
|
108
|
+
#{@function.call(*@arguments, &@block)};
|
109
|
+
}, 0)`
|
110
|
+
end
|
111
|
+
|
112
|
+
def prevent
|
113
|
+
`window.clearTimeout(#@id)`
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Abort the immediate.
|
22
118
|
def abort
|
23
119
|
return if aborted?
|
24
120
|
|
@@ -28,6 +124,7 @@ class Immediate
|
|
28
124
|
self
|
29
125
|
end
|
30
126
|
|
127
|
+
# Check if the immediate has been aborted.
|
31
128
|
def aborted?
|
32
129
|
@aborted
|
33
130
|
end
|
@@ -36,8 +133,25 @@ end
|
|
36
133
|
end
|
37
134
|
|
38
135
|
class Proc
|
39
|
-
#
|
136
|
+
# (see Immediate.new)
|
40
137
|
def defer(*args, &block)
|
41
138
|
Browser::Immediate.new(self, args, &block).tap(&:dispatch)
|
42
139
|
end
|
43
140
|
end
|
141
|
+
|
142
|
+
class Promise
|
143
|
+
# Create a promise which will be resolved with the result of the immediate.
|
144
|
+
#
|
145
|
+
# @param args [Array] the arguments the block will be called with
|
146
|
+
def self.defer(*args, &block)
|
147
|
+
new.tap {|promise|
|
148
|
+
proc {
|
149
|
+
begin
|
150
|
+
promise.resolve(block.call(*args))
|
151
|
+
rescue Exception => e
|
152
|
+
promise.reject(e)
|
153
|
+
end
|
154
|
+
}.defer
|
155
|
+
}
|
156
|
+
end
|
157
|
+
end
|