opal-browser 0.2.0 → 0.3.3
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 +78 -0
- data/.gitignore +3 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +17 -3
- data/LICENSE +2 -1
- data/README.md +131 -54
- data/Rakefile +29 -1
- data/config.ru +20 -3
- data/docs/polyfills.md +24 -0
- data/examples/2048/Gemfile +6 -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 +6 -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 +6 -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 +6 -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 +7 -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 +8 -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 +7 -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 +3 -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 +3 -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 +4 -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 +6 -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 +4 -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 +72 -34
- 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 +194 -50
- data/opal/browser/dom/mutation_observer.rb +2 -2
- data/opal/browser/dom/node.rb +53 -13
- data/opal/browser/dom/node_set.rb +13 -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 +46 -22
- 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 +120 -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
|
@@ -75,14 +75,16 @@ module Browser
|
|
|
75
75
|
when 'Window.send (Asynchronous)'
|
|
76
76
|
if defined?(`window.postMessage`) && !defined?(`window.importScripts`)
|
|
77
77
|
%x{
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
(function() {
|
|
79
|
+
var ok = true,
|
|
80
|
+
old = window.onmessage;
|
|
80
81
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
window.onmessage = function() { ok = false; };
|
|
83
|
+
window.postMessage("", "*")
|
|
84
|
+
window.onmessage = old;
|
|
84
85
|
|
|
85
|
-
|
|
86
|
+
return ok;
|
|
87
|
+
})()
|
|
86
88
|
}
|
|
87
89
|
end
|
|
88
90
|
|
|
@@ -98,15 +100,20 @@ module Browser
|
|
|
98
100
|
when 'Window.scroll'
|
|
99
101
|
defined?(`document.documentElement.scrollLeft`)
|
|
100
102
|
|
|
103
|
+
when 'Window.scrollBy'
|
|
104
|
+
defined?(`document.documentElement.scrollBy`)
|
|
105
|
+
|
|
101
106
|
when 'Window.pageOffset'
|
|
102
107
|
defined?(`window.pageXOffset`)
|
|
103
108
|
|
|
104
109
|
when 'Attr.isId'
|
|
105
110
|
%x{
|
|
106
|
-
|
|
107
|
-
|
|
111
|
+
(function() {
|
|
112
|
+
var div = document.createElement('div');
|
|
113
|
+
div.setAttribute('id', 'xxxxxxxxxxxxx');
|
|
108
114
|
|
|
109
|
-
|
|
115
|
+
return typeof(div.attributes['id'].isId) !== "undefined";
|
|
116
|
+
})()
|
|
110
117
|
}
|
|
111
118
|
|
|
112
119
|
when 'Element.addBehavior'
|
|
@@ -114,34 +121,42 @@ module Browser
|
|
|
114
121
|
|
|
115
122
|
when 'Element.className'
|
|
116
123
|
%x{
|
|
117
|
-
|
|
118
|
-
|
|
124
|
+
(function() {
|
|
125
|
+
var div = document.createElement("div");
|
|
126
|
+
div.setAttribute('className', 'x');
|
|
119
127
|
|
|
120
|
-
|
|
128
|
+
return div.className === 'x';
|
|
129
|
+
})()
|
|
121
130
|
}
|
|
122
131
|
|
|
123
132
|
when 'Element.class'
|
|
124
133
|
%x{
|
|
125
|
-
|
|
126
|
-
|
|
134
|
+
(function() {
|
|
135
|
+
var div = document.createElement("div");
|
|
136
|
+
div.setAttribute('class', 'x');
|
|
127
137
|
|
|
128
|
-
|
|
138
|
+
return div.className === 'x';
|
|
139
|
+
})()
|
|
129
140
|
}
|
|
130
141
|
|
|
131
142
|
when 'Element.for'
|
|
132
143
|
%x{
|
|
133
|
-
|
|
134
|
-
|
|
144
|
+
(function() {
|
|
145
|
+
var label = document.createElement("label");
|
|
146
|
+
label.setAttribute('for', 'x');
|
|
135
147
|
|
|
136
|
-
|
|
148
|
+
return label.htmlFor === 'x';
|
|
149
|
+
})()
|
|
137
150
|
}
|
|
138
151
|
|
|
139
152
|
when 'Element.htmlFor'
|
|
140
153
|
%x{
|
|
141
|
-
|
|
142
|
-
|
|
154
|
+
(function() {
|
|
155
|
+
var label = document.createElement("label");
|
|
156
|
+
label.setAttribute('htmlFor', 'x');
|
|
143
157
|
|
|
144
|
-
|
|
158
|
+
return label.htmlFor === 'x';
|
|
159
|
+
})()
|
|
145
160
|
}
|
|
146
161
|
|
|
147
162
|
when 'Element.clientSize'
|
|
@@ -182,7 +197,7 @@ module Browser
|
|
|
182
197
|
`new MouseEvent("click")`
|
|
183
198
|
|
|
184
199
|
true
|
|
185
|
-
rescue
|
|
200
|
+
rescue StandardError, JS::Error
|
|
186
201
|
false
|
|
187
202
|
end
|
|
188
203
|
|
|
@@ -269,6 +284,15 @@ module Browser
|
|
|
269
284
|
|
|
270
285
|
when 'Animation.cancelRequest (Chrome)', 'Animation.cancelRequest (Safari)'
|
|
271
286
|
defined?(`window.webkitCancelRequestAnimationFrame`)
|
|
287
|
+
|
|
288
|
+
when 'Audio'
|
|
289
|
+
defined?(`window.AudioContext`)
|
|
290
|
+
|
|
291
|
+
when 'Audio (Safari)', 'Audio (Chrome)'
|
|
292
|
+
defined?(`window.webkitAudioContext`)
|
|
293
|
+
|
|
294
|
+
when 'Custom Elements'
|
|
295
|
+
defined?(`window.customElements`)
|
|
272
296
|
end
|
|
273
297
|
|
|
274
298
|
`#@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
|