opal-browser 0.2.0 → 0.3.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 +5 -5
- data/.github/workflows/build.yml +95 -0
- data/.gitignore +2 -0
- data/Gemfile +17 -3
- data/LICENSE +2 -1
- data/README.md +116 -54
- data/Rakefile +29 -1
- data/config.ru +20 -3
- data/docs/polyfills.md +24 -0
- data/examples/2048/Gemfile +7 -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 +7 -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 +7 -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 +7 -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 +8 -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 +9 -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 +8 -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 +4 -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 +4 -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 +5 -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 +7 -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 +5 -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 +2 -3
- 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 -1
- data/opal/browser/canvas/gradient.rb +1 -1
- data/opal/browser/canvas/style.rb +3 -1
- data/opal/browser/canvas/text.rb +1 -1
- data/opal/browser/canvas.rb +17 -3
- data/opal/browser/console.rb +3 -1
- data/opal/browser/cookies.rb +16 -7
- 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 +7 -8
- data/opal/browser/delay.rb +16 -0
- data/opal/browser/dom/attribute.rb +1 -1
- data/opal/browser/dom/builder.rb +29 -10
- data/opal/browser/dom/document.rb +81 -13
- 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 +28 -4
- 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 +17 -2
- 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 +36 -0
- data/opal/browser/dom/element/media.rb +17 -0
- data/opal/browser/dom/element/scroll.rb +106 -74
- data/opal/browser/dom/element/select.rb +6 -0
- data/opal/browser/dom/element/size.rb +12 -0
- data/opal/browser/dom/element/template.rb +2 -0
- data/opal/browser/dom/element/textarea.rb +2 -0
- data/opal/browser/dom/element.rb +193 -48
- data/opal/browser/dom/mutation_observer.rb +2 -2
- data/opal/browser/dom/node.rb +53 -13
- data/opal/browser/dom/node_set.rb +11 -2
- data/opal/browser/dom/shadow_root.rb +12 -0
- data/opal/browser/dom/text.rb +2 -2
- data/opal/browser/dom.rb +38 -5
- data/opal/browser/effects.rb +170 -4
- data/opal/browser/event/all.rb +26 -0
- data/opal/browser/event/animation.rb +2 -0
- data/opal/browser/event/audio_processing.rb +2 -0
- data/opal/browser/event/base.rb +35 -4
- data/opal/browser/event/before_unload.rb +2 -0
- data/opal/browser/event/clipboard.rb +9 -0
- data/opal/browser/event/close.rb +2 -0
- data/opal/browser/event/composition.rb +2 -0
- data/opal/browser/event/custom.rb +1 -1
- data/opal/browser/event/data_transfer.rb +95 -0
- data/opal/browser/event/device_light.rb +2 -0
- data/opal/browser/event/device_motion.rb +2 -0
- data/opal/browser/event/device_orientation.rb +2 -0
- data/opal/browser/event/device_proximity.rb +2 -0
- data/opal/browser/event/drag.rb +9 -5
- data/opal/browser/event/focus.rb +2 -0
- data/opal/browser/event/gamepad.rb +3 -1
- data/opal/browser/event/hash_change.rb +2 -0
- data/opal/browser/event/keyboard.rb +14 -1
- data/opal/browser/event/message.rb +2 -0
- data/opal/browser/event/mouse.rb +10 -6
- data/opal/browser/event/page_transition.rb +2 -0
- data/opal/browser/event/pop_state.rb +2 -0
- data/opal/browser/event/progress.rb +2 -0
- data/opal/browser/event/sensor.rb +2 -0
- data/opal/browser/event/storage.rb +2 -0
- data/opal/browser/event/touch.rb +2 -0
- data/opal/browser/event/wheel.rb +2 -0
- data/opal/browser/event.rb +26 -116
- data/opal/browser/event_source.rb +1 -1
- data/opal/browser/form_data.rb +225 -0
- data/opal/browser/history.rb +4 -8
- data/opal/browser/http/request.rb +32 -10
- data/opal/browser/http/response.rb +5 -1
- data/opal/browser/http.rb +0 -2
- data/opal/browser/immediate.rb +0 -2
- data/opal/browser/location.rb +7 -1
- data/opal/browser/navigator.rb +105 -4
- data/opal/browser/polyfill/visual_viewport.rb +216 -0
- data/opal/browser/screen.rb +2 -2
- 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 +3 -3
- data/opal/browser/storage.rb +2 -2
- data/opal/browser/support.rb +13 -1
- 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 +14 -0
- data/opal/browser/window/view.rb +15 -0
- data/opal/browser/window.rb +29 -16
- data/opal/browser.rb +1 -11
- data/opal-browser.gemspec +3 -3
- data/spec/database/sql_spec.rb +43 -35
- data/spec/delay_spec.rb +15 -12
- data/spec/dom/document_spec.rb +10 -8
- data/spec/dom/element/custom_spec.rb +106 -0
- data/spec/dom/element/subclass_spec.rb +144 -0
- data/spec/dom/element_spec.rb +42 -0
- data/spec/dom/mutation_observer_spec.rb +12 -8
- 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 +18 -9
- data/spec/native_cached_wrapper_spec.rb +46 -0
- data/spec/runner.rb +37 -62
- data/spec/socket_spec.rb +15 -12
- data/spec/spec_helper.rb +2 -1
- data/spec/spec_helper_promise.rb.erb +25 -0
- metadata +119 -16
- data/.travis.yml +0 -74
- data/opal/browser/window/scroll.rb +0 -59
|
@@ -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,7 +4,7 @@ module Browser
|
|
|
4
4
|
#
|
|
5
5
|
# @see https://developer.mozilla.org/en-US/docs/Web/API/Window.screen
|
|
6
6
|
class Screen
|
|
7
|
-
include
|
|
7
|
+
include Browser::NativeCachedWrapper
|
|
8
8
|
include Event::Target
|
|
9
9
|
|
|
10
10
|
target {|value|
|
|
@@ -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,13 +4,13 @@ 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
|
|
12
|
+
include Native::Wrapper
|
|
13
|
+
include IO::Writable if defined? IO::Writable
|
|
14
14
|
include Event::Target
|
|
15
15
|
|
|
16
16
|
target {|value|
|
data/opal/browser/storage.rb
CHANGED
|
@@ -196,12 +196,12 @@ class Storage
|
|
|
196
196
|
#
|
|
197
197
|
# @return [String] the JSON representation
|
|
198
198
|
def to_json
|
|
199
|
-
io = StringIO.new
|
|
199
|
+
io = StringIO.new << "{"
|
|
200
200
|
|
|
201
201
|
io << JSON.create_id.to_json << ":" << self.class.name.to_json << ","
|
|
202
202
|
|
|
203
203
|
@data.each {|key, value|
|
|
204
|
-
io << key.to_json.
|
|
204
|
+
io << key.to_json.to_s << ":" << value.to_json << ","
|
|
205
205
|
}
|
|
206
206
|
|
|
207
207
|
io.seek(-1, IO::SEEK_CUR)
|
data/opal/browser/support.rb
CHANGED
|
@@ -98,6 +98,9 @@ module Browser
|
|
|
98
98
|
when 'Window.scroll'
|
|
99
99
|
defined?(`document.documentElement.scrollLeft`)
|
|
100
100
|
|
|
101
|
+
when 'Window.scrollBy'
|
|
102
|
+
defined?(`document.documentElement.scrollBy`)
|
|
103
|
+
|
|
101
104
|
when 'Window.pageOffset'
|
|
102
105
|
defined?(`window.pageXOffset`)
|
|
103
106
|
|
|
@@ -182,7 +185,7 @@ module Browser
|
|
|
182
185
|
`new MouseEvent("click")`
|
|
183
186
|
|
|
184
187
|
true
|
|
185
|
-
rescue
|
|
188
|
+
rescue StandardError, JS::Error
|
|
186
189
|
false
|
|
187
190
|
end
|
|
188
191
|
|
|
@@ -269,6 +272,15 @@ module Browser
|
|
|
269
272
|
|
|
270
273
|
when 'Animation.cancelRequest (Chrome)', 'Animation.cancelRequest (Safari)'
|
|
271
274
|
defined?(`window.webkitCancelRequestAnimationFrame`)
|
|
275
|
+
|
|
276
|
+
when 'Audio'
|
|
277
|
+
defined?(`window.AudioContext`)
|
|
278
|
+
|
|
279
|
+
when 'Audio (Safari)', 'Audio (Chrome)'
|
|
280
|
+
defined?(`window.webkitAudioContext`)
|
|
281
|
+
|
|
282
|
+
when 'Custom Elements'
|
|
283
|
+
defined?(`window.customElements`)
|
|
272
284
|
end
|
|
273
285
|
|
|
274
286
|
`#@support[#{feature}] = #{support}`
|
data/opal/browser/utils.rb
CHANGED
|
@@ -1,51 +1,133 @@
|
|
|
1
1
|
module Browser
|
|
2
|
+
Promise = defined?(PromiseV2) ? PromiseV2 : ::Promise
|
|
3
|
+
|
|
2
4
|
Size = Struct.new(:width, :height)
|
|
3
5
|
Position = Struct.new(:x, :y)
|
|
6
|
+
|
|
7
|
+
# {Browser::NativeCachedWrapper} is a special case of {Native::Wrapper}.
|
|
8
|
+
#
|
|
9
|
+
# What this module does is it makes sure that your Ruby objects
|
|
10
|
+
# are mapped 1:1 to your Javascript objects. So that for instance
|
|
11
|
+
# your `$document.at_css('body')` is always the same Ruby object.
|
|
12
|
+
#
|
|
13
|
+
# You can only use it if your final `.new` is of the signature
|
|
14
|
+
# `.new(native)` and your native (probably DOM) object persists and
|
|
15
|
+
# doesn't mind arbitrary properties.
|
|
16
|
+
#
|
|
17
|
+
# The rule of thumb is: if it does overload `#initialize`'s signature
|
|
18
|
+
# and not `.new`'s - it won't work. Use {Native::Wrapper} in this case.
|
|
19
|
+
module NativeCachedWrapper
|
|
20
|
+
def self.included(klass)
|
|
21
|
+
klass.include Native::Wrapper
|
|
22
|
+
klass.extend NativeCachedWrapperClassMethods
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def restricted?
|
|
26
|
+
!!@restricted
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Change a native reference and make sure the change is reflected on JS
|
|
30
|
+
# side as well. This method is used by Node#initialize_copy. Please don't
|
|
31
|
+
# use this method outside of the cloning semantic.
|
|
32
|
+
def set_native_reference(native)
|
|
33
|
+
`#{native}.$$opal_native_cached = #{self}`
|
|
34
|
+
@native = native
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
module NativeCachedWrapperClassMethods
|
|
39
|
+
# Check if we don't have access to arbitrary properties of a (presumably)
|
|
40
|
+
# native object.
|
|
41
|
+
private def restricted?(native)
|
|
42
|
+
%x{
|
|
43
|
+
try {
|
|
44
|
+
typeof(#{native}.$$try_restricted_access);
|
|
45
|
+
} catch (e) {
|
|
46
|
+
if (e.name == 'SecurityError') return true;
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def new(native)
|
|
53
|
+
# We can't access arbitrary properties if an element is restricted
|
|
54
|
+
# i.e. the DOM element is an item we can't fully access due to CORS.
|
|
55
|
+
if restricted?(native)
|
|
56
|
+
# Let's try to bypass any further initializers... may be ugly, but
|
|
57
|
+
# works.
|
|
58
|
+
obj = allocate
|
|
59
|
+
obj.instance_variable_set :@native, native
|
|
60
|
+
obj.instance_variable_set :@restricted, true
|
|
61
|
+
return obj
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# It's not a native element? Weird, better throw an exception.
|
|
65
|
+
raise ArgumentError if !native?(native)
|
|
66
|
+
|
|
67
|
+
if defined? `#{native}.$$opal_native_cached`
|
|
68
|
+
`#{native}.$$opal_native_cached`
|
|
69
|
+
else
|
|
70
|
+
`#{native}.$$opal_native_cached = #{super(native)}`
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
4
74
|
end
|
|
5
75
|
|
|
6
76
|
class Object
|
|
7
77
|
# Encode as URI.
|
|
8
78
|
#
|
|
79
|
+
# @deprecated Please use FormData.encode_uri
|
|
9
80
|
# @return [String] the {Object#to_s} encoded for usage as URI
|
|
10
81
|
def encode_uri
|
|
11
|
-
|
|
82
|
+
warn "opal-browser: Object#encode_uri is deprecated. Please use FormData.encode_uri"
|
|
83
|
+
FormData.encode_uri(to_s)
|
|
12
84
|
end
|
|
13
85
|
|
|
14
86
|
# Encode as URI component.
|
|
15
87
|
#
|
|
88
|
+
# @deprecated Please use FormData.encode
|
|
16
89
|
# @return [String] the {Object#to_s} encoded for usage as URI component
|
|
17
90
|
def encode_uri_component
|
|
18
|
-
|
|
91
|
+
warn "opal-browser: Object#encode_uri_component is deprecated. Please use FormData.encode"
|
|
92
|
+
FormData.encode(to_s)
|
|
19
93
|
end
|
|
20
94
|
end
|
|
21
95
|
|
|
22
96
|
class String
|
|
23
97
|
# Encode as URI component.
|
|
24
98
|
#
|
|
99
|
+
# @deprecated Please use FormData.encode
|
|
25
100
|
# @return [String] the string encoded for usage as URI component
|
|
26
101
|
def encode_uri_component
|
|
27
|
-
|
|
102
|
+
warn "opal-browser: String#encode_uri_component is deprecated. Please use FormData.encode"
|
|
103
|
+
FormData.encode(self)
|
|
28
104
|
end
|
|
29
105
|
|
|
30
106
|
# Encode as URI.
|
|
31
107
|
#
|
|
108
|
+
# @deprecated Please use FormData.encode_uri
|
|
32
109
|
# @return [String] the string encoded as URI
|
|
33
110
|
def encode_uri
|
|
34
|
-
|
|
111
|
+
warn "opal-browser: String#encode_uri is deprecated. Please use FormData.encode_uri"
|
|
112
|
+
FormData.encode_uri(self)
|
|
35
113
|
end
|
|
36
114
|
|
|
37
115
|
# Decode as URI component.
|
|
38
116
|
#
|
|
117
|
+
# @deprecated Please use FormData.decode
|
|
39
118
|
# @return [String] the string decoded as URI component
|
|
40
119
|
def decode_uri_component
|
|
41
|
-
|
|
120
|
+
warn "opal-browser: String#decode_uri_component is deprecated. Please use FormData.decode"
|
|
121
|
+
FormData.decode(self)
|
|
42
122
|
end
|
|
43
123
|
|
|
44
124
|
# Decode as URI.
|
|
45
125
|
#
|
|
126
|
+
# @deprecated Please use FormData.decode_uri
|
|
46
127
|
# @return [String] the string decoded as URI
|
|
47
128
|
def decode_uri
|
|
48
|
-
|
|
129
|
+
warn "opal-browser: String#decode_uri is deprecated. Please use FormData.decode_uri"
|
|
130
|
+
FormData.decode_uri(self)
|
|
49
131
|
end
|
|
50
132
|
end
|
|
51
133
|
|
|
@@ -54,21 +136,19 @@ class Hash
|
|
|
54
136
|
#
|
|
55
137
|
# @param string [String] the URL encoded form
|
|
56
138
|
#
|
|
139
|
+
# @deprecated Please use FormData.parse_query
|
|
57
140
|
# @return [Hash]
|
|
58
141
|
def self.decode_uri(string)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
[name.decode_uri_component, value.decode_uri_component]
|
|
63
|
-
}]
|
|
142
|
+
warn "opal-browser: Hash.decode_uri is deprecated. Please use FormData.parse_query"
|
|
143
|
+
FormData.parse_query(string)
|
|
64
144
|
end
|
|
65
145
|
|
|
66
146
|
# Encode the Hash to an URL form.
|
|
67
147
|
#
|
|
148
|
+
# @deprecated Please use FormData.build_query
|
|
68
149
|
# @return [String] the URL encoded form
|
|
69
150
|
def encode_uri
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}.join(?&)
|
|
151
|
+
warn "opal-browser: Hash#encode_uri is deprecated. Please use FormData.build_query"
|
|
152
|
+
FormData.build_query(self)
|
|
73
153
|
end
|
|
74
154
|
end
|
data/opal/browser/version.rb
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Firefox browsers need either a flag or a polyfill
|
|
2
|
+
require "browser/polyfill/visual_viewport"
|
|
3
|
+
|
|
4
|
+
module Browser
|
|
5
|
+
# The mobile web contains two viewports, the Layout and Visual viewport.
|
|
6
|
+
# The Layout viewport is what a page lays out its elements into and the
|
|
7
|
+
# Visual viewport is what is actually visible on the screen. When the user
|
|
8
|
+
# pinch-zooms into the page, the visual viewport shrinks but the layout viewport
|
|
9
|
+
# is unchanged. UI like the on-screen keyboard (OSK) can also shrink the visual
|
|
10
|
+
# viewport without affecting the layout viewport.
|
|
11
|
+
#
|
|
12
|
+
# https://github.com/WICG/visual-viewport
|
|
13
|
+
# https://developer.mozilla.org/en-US/docs/Web/API/VisualViewport
|
|
14
|
+
class VisualViewport
|
|
15
|
+
include Native::Wrapper
|
|
16
|
+
include Event::Target
|
|
17
|
+
|
|
18
|
+
alias_native :offset_left, :offsetLeft
|
|
19
|
+
alias_native :offset_top, :offsetTop
|
|
20
|
+
alias_native :page_left, :pageLeft
|
|
21
|
+
alias_native :page_top, :pageTop
|
|
22
|
+
alias_native :width, :width
|
|
23
|
+
alias_native :height, :height
|
|
24
|
+
alias_native :scale, :scale
|
|
25
|
+
|
|
26
|
+
attr_accessor :native
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class Window
|
|
30
|
+
def visual_viewport
|
|
31
|
+
@visual_viewport ||= VisualViewport.new(`#@native.visualViewport`)
|
|
32
|
+
|
|
33
|
+
# Polyfill can take some time to load
|
|
34
|
+
@visual_viewport.native ||= `#@native.visualViewport`
|
|
35
|
+
|
|
36
|
+
@visual_viewport
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
data/opal/browser/window/size.rb
CHANGED
|
@@ -54,6 +54,20 @@ class Size
|
|
|
54
54
|
def height=(value)
|
|
55
55
|
set(height: value)
|
|
56
56
|
end
|
|
57
|
+
|
|
58
|
+
# @!attribute inner_width
|
|
59
|
+
# @return [Integer] the inner width of the window
|
|
60
|
+
|
|
61
|
+
def inner_width
|
|
62
|
+
`#@native.innerWidth`
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# @!attribute inner_height
|
|
66
|
+
# @return [Integer] the inner height of the window
|
|
67
|
+
|
|
68
|
+
def inner_height
|
|
69
|
+
`#@native.innerHeight`
|
|
70
|
+
end
|
|
57
71
|
end
|
|
58
72
|
|
|
59
73
|
end; end
|
data/opal/browser/window/view.rb
CHANGED
|
@@ -31,6 +31,21 @@ class View
|
|
|
31
31
|
raise NotImplementedError, 'window size unsupported'
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
|
+
|
|
35
|
+
# Get a device pixel ratio. Can be used to handle desktop browser
|
|
36
|
+
# zoom, retina devices and custom screen scale for mobile devices.
|
|
37
|
+
# Use $window.visual_viewport.scale to handle mobile zoom.
|
|
38
|
+
def zoom
|
|
39
|
+
`#@native.devicePixelRatio`
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Handle #pixel_ratio changes. This will trigger a block on zoom.
|
|
43
|
+
def on_zoom &block
|
|
44
|
+
%x{
|
|
45
|
+
var mqString = "(resolution: " + #@native.devicePixelRatio + "dppx)";
|
|
46
|
+
#@native.matchMedia(mqString).addListener(#{block.to_n});
|
|
47
|
+
}
|
|
48
|
+
end
|
|
34
49
|
end
|
|
35
50
|
|
|
36
51
|
end; end
|