bowser 0.4.3 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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
@@ -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