bowser 0.4.3 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +4 -9
- data/CHANGELOG.md +7 -0
- data/lib/bowser/version.rb +1 -1
- data/opal/bowser.rb +0 -3
- data/opal/bowser/delegate_native.rb +9 -1
- data/opal/bowser/document.rb +8 -0
- data/opal/bowser/element.rb +57 -4
- data/opal/bowser/event.rb +30 -0
- data/opal/bowser/event_target.rb +10 -0
- data/opal/bowser/file_list.rb +25 -0
- data/opal/bowser/http.rb +30 -0
- data/opal/bowser/http/form_data.rb +4 -0
- data/opal/bowser/http/request.rb +19 -0
- data/opal/bowser/http/response.rb +8 -0
- data/opal/bowser/indexed_db.rb +244 -0
- data/opal/bowser/service_worker.rb +43 -0
- data/opal/bowser/service_worker/cache_storage.rb +49 -0
- data/opal/bowser/service_worker/clients.rb +19 -0
- data/opal/bowser/service_worker/context.rb +71 -0
- data/opal/bowser/service_worker/extendable_event.rb +19 -0
- data/opal/bowser/service_worker/fetch_event.rb +28 -0
- data/opal/bowser/service_worker/promise.rb +101 -0
- data/opal/bowser/service_worker/request.rb +33 -0
- data/opal/bowser/service_worker/response.rb +41 -0
- data/opal/bowser/websocket.rb +18 -0
- data/opal/bowser/window.rb +28 -0
- metadata +13 -3
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'bowser/service_worker/response'
|
2
|
+
require 'bowser/service_worker/promise'
|
3
|
+
|
4
|
+
module Bowser
|
5
|
+
module ServiceWorker
|
6
|
+
class CacheStorage
|
7
|
+
def initialize
|
8
|
+
@native = `caches`
|
9
|
+
end
|
10
|
+
|
11
|
+
def match request, options={}
|
12
|
+
Promise.from_native(`
|
13
|
+
#@native.match(#{request.to_n}, #{options.to_n})
|
14
|
+
.then(#{proc { |value| value && Response.from_native(value) }})
|
15
|
+
`)
|
16
|
+
end
|
17
|
+
|
18
|
+
def has name
|
19
|
+
Promise.from_native(`#@native.has(name)`)
|
20
|
+
end
|
21
|
+
|
22
|
+
def open name
|
23
|
+
Promise.from_native(`
|
24
|
+
#@native.open(name)
|
25
|
+
.then(#{proc { |native| Cache.new(native) }})
|
26
|
+
.catch(#{proc { |native| `console.error(native)` }})
|
27
|
+
`)
|
28
|
+
end
|
29
|
+
|
30
|
+
class Cache
|
31
|
+
def initialize native
|
32
|
+
@native = native
|
33
|
+
end
|
34
|
+
|
35
|
+
def add_all requests
|
36
|
+
Promise.from_native(`#@native.addAll(#{requests.map(&:to_n)})`)
|
37
|
+
end
|
38
|
+
|
39
|
+
def put request, response
|
40
|
+
Promise.from_native(`#@native.put(#{request.to_n}, #{response.to_n})`)
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_n
|
44
|
+
@native
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'bowser/service_worker/promise'
|
2
|
+
|
3
|
+
module Bowser
|
4
|
+
module ServiceWorker
|
5
|
+
class Clients
|
6
|
+
def initialize native
|
7
|
+
@native = native
|
8
|
+
end
|
9
|
+
|
10
|
+
def claim
|
11
|
+
Promise.from_native(`#@native.claim()`)
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_n
|
15
|
+
@native
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'opal'
|
2
|
+
require 'native'
|
3
|
+
require 'bowser/service_worker/extendable_event'
|
4
|
+
require 'bowser/service_worker/fetch_event'
|
5
|
+
require 'bowser/service_worker/cache_storage'
|
6
|
+
require 'bowser/service_worker/request'
|
7
|
+
require 'bowser/service_worker/response'
|
8
|
+
require 'bowser/service_worker/promise'
|
9
|
+
require 'bowser/service_worker/clients'
|
10
|
+
|
11
|
+
# Opal uses `self` internally, but we can use `this` to get the worker reference
|
12
|
+
%x{ var worker = this; }
|
13
|
+
|
14
|
+
module Bowser
|
15
|
+
module ServiceWorker
|
16
|
+
class Context
|
17
|
+
def initialize native
|
18
|
+
@native = native
|
19
|
+
end
|
20
|
+
|
21
|
+
def on event_name, &block
|
22
|
+
event_type = EVENT_TYPES.fetch(event_name) { ExtendableEvent }
|
23
|
+
handler = proc { |event| block.call(event_type.new(event)) }
|
24
|
+
|
25
|
+
%x{#@native.addEventListener(event_name, handler);}
|
26
|
+
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_n
|
31
|
+
@native
|
32
|
+
end
|
33
|
+
|
34
|
+
def caches
|
35
|
+
@caches ||= CacheStorage.new
|
36
|
+
end
|
37
|
+
|
38
|
+
def fetch url
|
39
|
+
Promise.new do |promise|
|
40
|
+
%x{
|
41
|
+
fetch(#{url.to_n})
|
42
|
+
.then(#{proc { |response|
|
43
|
+
promise.resolve Response.from_native(response)
|
44
|
+
}})
|
45
|
+
.catch(#{proc { |error| promise.reject error }})
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def skip_waiting
|
51
|
+
Promise.from_native(`#{to_n}.skipWaiting()`)
|
52
|
+
end
|
53
|
+
|
54
|
+
def clients
|
55
|
+
@clients ||= Clients.new(`#{to_n}.clients`)
|
56
|
+
end
|
57
|
+
|
58
|
+
EVENT_TYPES = {
|
59
|
+
fetch: FetchEvent,
|
60
|
+
}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.worker
|
66
|
+
Bowser::ServiceWorker::Context.new(`worker`)
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.method_missing(*args, &block)
|
70
|
+
worker.send(*args, &block)
|
71
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Bowser
|
2
|
+
module ServiceWorker
|
3
|
+
class ExtendableEvent
|
4
|
+
def initialize native
|
5
|
+
@native = native
|
6
|
+
end
|
7
|
+
|
8
|
+
def wait_until promise
|
9
|
+
`#@native.waitUntil(#{promise.to_n})`
|
10
|
+
|
11
|
+
promise
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_n
|
15
|
+
@native
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'bowser/service_worker/request'
|
2
|
+
require 'bowser/service_worker/extendable_event'
|
3
|
+
|
4
|
+
module Bowser
|
5
|
+
module ServiceWorker
|
6
|
+
class FetchEvent < ExtendableEvent
|
7
|
+
def url
|
8
|
+
`#@native.request.url`
|
9
|
+
end
|
10
|
+
|
11
|
+
def reload?
|
12
|
+
`#@native.isReload`
|
13
|
+
end
|
14
|
+
|
15
|
+
def client_id
|
16
|
+
`#@native.clientId`
|
17
|
+
end
|
18
|
+
|
19
|
+
def request
|
20
|
+
@request ||= Request.from_native(`#@native.request`)
|
21
|
+
end
|
22
|
+
|
23
|
+
def respond_with promise
|
24
|
+
`#@native.respondWith(#{promise.then(&:to_n).to_n})`
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Bowser
|
2
|
+
module ServiceWorker
|
3
|
+
class Promise
|
4
|
+
attr_reader :value, :failure
|
5
|
+
|
6
|
+
def initialize &block
|
7
|
+
@native = `new Promise(function(resolve, reject) {
|
8
|
+
#{@resolve = `resolve`};
|
9
|
+
#{@reject = `reject`};
|
10
|
+
#{block.call(self) if block_given?};
|
11
|
+
})`
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.from_native promise
|
15
|
+
p = allocate
|
16
|
+
p.instance_exec { @native = promise }
|
17
|
+
p
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.all promises
|
21
|
+
from_native `Promise.all(#{promises.map(&:to_n)})`
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.race promises
|
25
|
+
from_native `Promise.race(#{promises.map(&:to_n)})`
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.reject reason
|
29
|
+
new.reject reason
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.resolve value
|
33
|
+
new.resolve value
|
34
|
+
end
|
35
|
+
|
36
|
+
def then &block
|
37
|
+
Promise.from_native `#@native.then(block)`
|
38
|
+
end
|
39
|
+
|
40
|
+
def fail &block
|
41
|
+
Promise.from_native `#@native.catch(block)`
|
42
|
+
end
|
43
|
+
|
44
|
+
def always &block
|
45
|
+
Promise.from_native `#@native.then(block).fail(block)`
|
46
|
+
end
|
47
|
+
|
48
|
+
def resolve value
|
49
|
+
return self if resolved?
|
50
|
+
if rejected?
|
51
|
+
`console.warn(#{self}, #{"tried to resolve, already #{resolved? ? 'resolved' : 'rejected'} with"}, #{@value || @failure})`
|
52
|
+
end
|
53
|
+
|
54
|
+
@value = value
|
55
|
+
@resolve.call value
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
def reject failure
|
60
|
+
return self if rejected?
|
61
|
+
if resolved?
|
62
|
+
`console.warn(#{self}, #{"tried to reject, already #{resolved? ? 'resolved' : 'rejected'} with"}, #{@value || @failure})`
|
63
|
+
end
|
64
|
+
|
65
|
+
@failure = failure
|
66
|
+
@reject.call failure
|
67
|
+
self
|
68
|
+
end
|
69
|
+
|
70
|
+
def realized?
|
71
|
+
resolved? || rejected?
|
72
|
+
end
|
73
|
+
|
74
|
+
def resolved?
|
75
|
+
!value.nil?
|
76
|
+
end
|
77
|
+
|
78
|
+
def rejected?
|
79
|
+
!failure.nil?
|
80
|
+
end
|
81
|
+
|
82
|
+
def to_n
|
83
|
+
@native
|
84
|
+
end
|
85
|
+
|
86
|
+
%x{
|
87
|
+
Opal.defn(self, 'then', function(callback) {
|
88
|
+
var self = this;
|
89
|
+
|
90
|
+
#{self.then(&`callback`)};
|
91
|
+
});
|
92
|
+
|
93
|
+
Opal.defn(self, 'catch', function(callback) {
|
94
|
+
var self = this;
|
95
|
+
|
96
|
+
#{self.fail(&`callback`)};
|
97
|
+
});
|
98
|
+
}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Bowser
|
2
|
+
module ServiceWorker
|
3
|
+
class Request
|
4
|
+
def initialize url, options={}
|
5
|
+
@url = url
|
6
|
+
@options = options
|
7
|
+
@native = `new Request(url, #{options.to_n})`
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.from_native native
|
11
|
+
request = allocate
|
12
|
+
request.instance_exec { @native = native }
|
13
|
+
request
|
14
|
+
end
|
15
|
+
|
16
|
+
def inspect
|
17
|
+
"#<#{self.class.name}:0x#{object_id.to_s(16)} @url=#{url.inspect}>"
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
inspect
|
22
|
+
end
|
23
|
+
|
24
|
+
def url
|
25
|
+
@url || `#@native.url`
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_n
|
29
|
+
@native
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Bowser
|
2
|
+
module ServiceWorker
|
3
|
+
class Response
|
4
|
+
def self.from_native native
|
5
|
+
new(native) if `#{native} != null`
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize native
|
9
|
+
@native = native
|
10
|
+
end
|
11
|
+
|
12
|
+
def json
|
13
|
+
@json ||= `#@native.json()`
|
14
|
+
end
|
15
|
+
|
16
|
+
def text
|
17
|
+
@text ||= `#@native.text()`
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_n
|
21
|
+
@native
|
22
|
+
end
|
23
|
+
|
24
|
+
def url
|
25
|
+
`#@native.url`
|
26
|
+
end
|
27
|
+
|
28
|
+
def status
|
29
|
+
`#@native.status`
|
30
|
+
end
|
31
|
+
|
32
|
+
def inspect
|
33
|
+
"#<#{self.class.name}:0x#{(object_id * 2).to_s(16)} @url=#{url.inspect} @status=#{status.inspect}>"
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
inspect
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/opal/bowser/websocket.rb
CHANGED
@@ -2,6 +2,7 @@ require 'bowser/window'
|
|
2
2
|
require 'json'
|
3
3
|
|
4
4
|
module Bowser
|
5
|
+
# A Ruby WebSocket class
|
5
6
|
class WebSocket
|
6
7
|
EVENT_NAMES = %w(
|
7
8
|
open
|
@@ -10,6 +11,8 @@ module Bowser
|
|
10
11
|
close
|
11
12
|
)
|
12
13
|
|
14
|
+
# param url [String] the URL to connect to
|
15
|
+
# keywordparam native [JS] the native WebSocket connection
|
13
16
|
def initialize url, native: `new WebSocket(url)`
|
14
17
|
@url = url
|
15
18
|
@native = native
|
@@ -20,6 +23,9 @@ module Bowser
|
|
20
23
|
on(:close) { @connected = false }
|
21
24
|
end
|
22
25
|
|
26
|
+
# Attach the given block as a handler for the specified event
|
27
|
+
#
|
28
|
+
# @param event_target [String] the name of the event to handle
|
23
29
|
def on event_name, &block
|
24
30
|
if EVENT_NAMES.include? event_name
|
25
31
|
@handlers[event_name] << block
|
@@ -28,16 +34,23 @@ module Bowser
|
|
28
34
|
end
|
29
35
|
end
|
30
36
|
|
37
|
+
# Send the given message across the connection
|
38
|
+
#
|
39
|
+
# @param msg [Hash, Array] the message to send. Should be a JSON-serializable structure.
|
31
40
|
def send_message msg
|
32
41
|
`#@native.send(#{JSON.dump(msg)})`
|
33
42
|
self
|
34
43
|
end
|
35
44
|
|
45
|
+
# @return [Boolean] true if this socket is connected, false otherwise
|
36
46
|
def connected?
|
37
47
|
@connected
|
38
48
|
end
|
39
49
|
|
40
50
|
# Reconnect the websocket after a short delay if it is interrupted
|
51
|
+
#
|
52
|
+
# @keywordparam delay [Numeric] the number of seconds to wait before
|
53
|
+
# reconnecting. Defaults to 1 second.
|
41
54
|
def autoreconnect!(delay: 1)
|
42
55
|
return if @autoreconnect
|
43
56
|
@autoreconnect = true
|
@@ -47,6 +60,9 @@ module Bowser
|
|
47
60
|
end
|
48
61
|
end
|
49
62
|
|
63
|
+
# Close the current connection
|
64
|
+
#
|
65
|
+
# @param reason [String, nil] the reason this socket is being closed
|
50
66
|
def close reason=`undefined`
|
51
67
|
`#@native.close(reason)`
|
52
68
|
end
|
@@ -113,6 +129,8 @@ module Bowser
|
|
113
129
|
|
114
130
|
module_function
|
115
131
|
|
132
|
+
# @param url [String] the URL to connect to
|
133
|
+
# @return [Bowser::WebSocket] a websocket connection to the given URL
|
116
134
|
def websocket *args
|
117
135
|
WebSocket.new(*args)
|
118
136
|
end
|
data/opal/bowser/window.rb
CHANGED
@@ -11,6 +11,9 @@ module Bowser
|
|
11
11
|
module_function
|
12
12
|
|
13
13
|
if `#@native.requestAnimationFrame !== undefined`
|
14
|
+
# Add the given block to the current iteration of the event loop. If this
|
15
|
+
# is called from another `animation_frame` call, the block is run in the
|
16
|
+
# following iteration of the event loop.
|
14
17
|
def animation_frame &block
|
15
18
|
`requestAnimationFrame(function() { #{block.call} })`
|
16
19
|
self
|
@@ -22,60 +25,84 @@ module Bowser
|
|
22
25
|
end
|
23
26
|
end
|
24
27
|
|
28
|
+
# Run the given block after the specified number of seconds has passed.
|
29
|
+
#
|
30
|
+
# @param duration [Numeric] the number of seconds to wait
|
25
31
|
def delay duration, &block
|
26
32
|
`setTimeout(function() { #{block.call} }, duration * 1000)`
|
27
33
|
self
|
28
34
|
end
|
29
35
|
|
36
|
+
# Run the given block every `duration` seconds
|
37
|
+
#
|
38
|
+
# @param duration [Numeric] the number of seconds between runs
|
30
39
|
def interval duration, &block
|
31
40
|
`setInterval(function() { #{block.call} }, duration * 1000)`
|
32
41
|
self
|
33
42
|
end
|
34
43
|
|
44
|
+
# @return [Location] the browser's Location object
|
35
45
|
def location
|
36
46
|
Location
|
37
47
|
end
|
38
48
|
|
49
|
+
# Scroll to the specified (x,y) coordinates
|
39
50
|
def scroll x, y
|
40
51
|
`window.scrollTo(x, y)`
|
41
52
|
end
|
42
53
|
|
54
|
+
# Wrapper for the browser's Location object
|
43
55
|
module Location
|
44
56
|
module_function
|
45
57
|
|
58
|
+
# The "hash" of the current URL
|
46
59
|
def hash
|
47
60
|
`window.location.hash`
|
48
61
|
end
|
49
62
|
|
63
|
+
# Set the hash of the URL
|
64
|
+
#
|
65
|
+
# @param hash [String] the new value of the URL hash
|
50
66
|
def hash= hash
|
51
67
|
`window.location.hash = hash`
|
52
68
|
end
|
53
69
|
|
70
|
+
# The path of the current URL
|
54
71
|
def path
|
55
72
|
`window.location.pathname`
|
56
73
|
end
|
57
74
|
|
75
|
+
# The full current URL
|
58
76
|
def href
|
59
77
|
`window.location.href`
|
60
78
|
end
|
61
79
|
|
80
|
+
# Set the current URL
|
81
|
+
#
|
82
|
+
# @param href [String] the URL to navigate to
|
62
83
|
def href= href
|
63
84
|
`window.location.href = href`
|
64
85
|
end
|
65
86
|
end
|
66
87
|
|
88
|
+
# The browser's history object
|
67
89
|
module History
|
68
90
|
module_function
|
69
91
|
|
92
|
+
# @return [Boolean] true if the browser supports pushState, false otherwise
|
70
93
|
def has_push_state?
|
71
94
|
`!!window.history.pushState`
|
72
95
|
end
|
73
96
|
|
97
|
+
# Navigate to the specified path without triggering a full page reload
|
98
|
+
#
|
99
|
+
# @param path [String] the path to navigate to
|
74
100
|
def push path
|
75
101
|
`window.history.pushState({}, '', path)`
|
76
102
|
end
|
77
103
|
end
|
78
104
|
|
105
|
+
# return [History] the browser's History object
|
79
106
|
def history
|
80
107
|
History
|
81
108
|
end
|
@@ -83,6 +110,7 @@ module Bowser
|
|
83
110
|
|
84
111
|
module_function
|
85
112
|
|
113
|
+
# return [Window] the browser's Window object
|
86
114
|
def window
|
87
115
|
Window
|
88
116
|
end
|