dialog-polyfill-rails 0.4.5.1
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 +7 -0
- data/.gitignore +10 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +27 -0
- data/README.md +50 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/dialog-polyfill-rails.gemspec +31 -0
- data/lib/dialog/polyfill/rails.rb +10 -0
- data/lib/dialog/polyfill/rails/version.rb +7 -0
- data/vendor/assets/javascripts/dialog-polyfill.js +523 -0
- data/vendor/assets/stylesheets/dialog-polyfill.css +46 -0
- metadata +85 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 237cb92f58f9c6f5851c6248329122fbbb96757b
|
4
|
+
data.tar.gz: 861985161565dd3b03828c644cac7a9021de9b6e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3f3e9b25bab51a9bdde256744abccc9a71378f6d6fc6b2d35e3b1d199964128adb9bce34de39dd19290518845ec8055d33d8f56fba22765b89d4b8cf42836971
|
7
|
+
data.tar.gz: 4643f1b7ea90709b6b6edc706dbd0e94b6408258bda445846a7461cb870b27c4b0f63aa2611ed88ab348229f24f11d62dfc00293f71c466a90c46cda2b360145
|
data/.gitignore
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, and in the interest of
|
4
|
+
fostering an open and welcoming community, we pledge to respect all people who
|
5
|
+
contribute through reporting issues, posting feature requests, updating
|
6
|
+
documentation, submitting pull requests or patches, and other activities.
|
7
|
+
|
8
|
+
We are committed to making participation in this project a harassment-free
|
9
|
+
experience for everyone, regardless of level of experience, gender, gender
|
10
|
+
identity and expression, sexual orientation, disability, personal appearance,
|
11
|
+
body size, race, ethnicity, age, religion, or nationality.
|
12
|
+
|
13
|
+
Examples of unacceptable behavior by participants include:
|
14
|
+
|
15
|
+
* The use of sexualized language or imagery
|
16
|
+
* Personal attacks
|
17
|
+
* Trolling or insulting/derogatory comments
|
18
|
+
* Public or private harassment
|
19
|
+
* Publishing other's private information, such as physical or electronic
|
20
|
+
addresses, without explicit permission
|
21
|
+
* Other unethical or unprofessional conduct
|
22
|
+
|
23
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
24
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
25
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
26
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
27
|
+
threatening, offensive, or harmful.
|
28
|
+
|
29
|
+
By adopting this Code of Conduct, project maintainers commit themselves to
|
30
|
+
fairly and consistently applying these principles to every aspect of managing
|
31
|
+
this project. Project maintainers who do not follow or enforce the Code of
|
32
|
+
Conduct may be permanently removed from the project team.
|
33
|
+
|
34
|
+
This code of conduct applies both within project spaces and in public spaces
|
35
|
+
when an individual is representing the project or its community.
|
36
|
+
|
37
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
38
|
+
reported by contacting a project maintainer at mjmaix@gmail.com. All
|
39
|
+
complaints will be reviewed and investigated and will result in a response that
|
40
|
+
is deemed necessary and appropriate to the circumstances. Maintainers are
|
41
|
+
obligated to maintain confidentiality with regard to the reporter of an
|
42
|
+
incident.
|
43
|
+
|
44
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
45
|
+
version 1.3.0, available at
|
46
|
+
[http://contributor-covenant.org/version/1/3/0/][version]
|
47
|
+
|
48
|
+
[homepage]: http://contributor-covenant.org
|
49
|
+
[version]: http://contributor-covenant.org/version/1/3/0/
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
2
|
+
//
|
3
|
+
// Redistribution and use in source and binary forms, with or without
|
4
|
+
// modification, are permitted provided that the following conditions are
|
5
|
+
// met:
|
6
|
+
//
|
7
|
+
// * Redistributions of source code must retain the above copyright
|
8
|
+
// notice, this list of conditions and the following disclaimer.
|
9
|
+
// * Redistributions in binary form must reproduce the above
|
10
|
+
// copyright notice, this list of conditions and the following disclaimer
|
11
|
+
// in the documentation and/or other materials provided with the
|
12
|
+
// distribution.
|
13
|
+
// * Neither the name of Google Inc. nor the names of its
|
14
|
+
// contributors may be used to endorse or promote products derived from
|
15
|
+
// this software without specific prior written permission.
|
16
|
+
//
|
17
|
+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
18
|
+
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
19
|
+
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
20
|
+
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
21
|
+
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
22
|
+
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
23
|
+
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
24
|
+
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
25
|
+
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
26
|
+
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
27
|
+
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Dialog::Polyfill::Rails
|
2
|
+
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/dialog/polyfill/rails`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
+
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'dialog-polyfill-rails'
|
13
|
+
```
|
14
|
+
|
15
|
+
Add this to your js manifest file
|
16
|
+
|
17
|
+
```js
|
18
|
+
//= require dialog-polyfill
|
19
|
+
```
|
20
|
+
|
21
|
+
Add this to your css manifest file
|
22
|
+
|
23
|
+
```css
|
24
|
+
*= require dialog-polyfill
|
25
|
+
```
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
And then execute:
|
30
|
+
|
31
|
+
$ bundle
|
32
|
+
|
33
|
+
Or install it yourself as:
|
34
|
+
|
35
|
+
$ gem install dialog-polyfill-rails
|
36
|
+
|
37
|
+
## Usage
|
38
|
+
|
39
|
+
TODO: Write usage instructions here
|
40
|
+
|
41
|
+
## Development
|
42
|
+
|
43
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
44
|
+
|
45
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
46
|
+
|
47
|
+
## Contributing
|
48
|
+
|
49
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/dialog-polyfill-rails. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
50
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "dialog/polyfill/rails"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'dialog/polyfill/rails/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "dialog-polyfill-rails"
|
8
|
+
spec.version = Dialog::Polyfill::Rails::VERSION
|
9
|
+
spec.authors = ["MJ Abadilla"]
|
10
|
+
spec.email = ["mjmaix@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Gemified version of https://github.com/GoogleChrome/dialog-polyfill.}
|
13
|
+
spec.description = %q{Gemified version of https://github.com/GoogleChrome/dialog-polyfill.}
|
14
|
+
spec.homepage = "https://github.com/mjmaix/dialog-polyfill-rails"
|
15
|
+
|
16
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
17
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
18
|
+
if spec.respond_to?(:metadata)
|
19
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
20
|
+
else
|
21
|
+
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
22
|
+
end
|
23
|
+
|
24
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
25
|
+
spec.bindir = "exe"
|
26
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
|
+
spec.require_paths = ["lib"]
|
28
|
+
|
29
|
+
spec.add_development_dependency "bundler", "~> 1.13"
|
30
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
31
|
+
end
|
@@ -0,0 +1,523 @@
|
|
1
|
+
(function() {
|
2
|
+
|
3
|
+
var supportCustomEvent = window.CustomEvent;
|
4
|
+
if (!supportCustomEvent || typeof supportCustomEvent == 'object') {
|
5
|
+
supportCustomEvent = function CustomEvent(event, x) {
|
6
|
+
x = x || {};
|
7
|
+
var ev = document.createEvent('CustomEvent');
|
8
|
+
ev.initCustomEvent(event, !!x.bubbles, !!x.cancelable, x.detail || null);
|
9
|
+
return ev;
|
10
|
+
};
|
11
|
+
supportCustomEvent.prototype = window.Event.prototype;
|
12
|
+
}
|
13
|
+
|
14
|
+
/**
|
15
|
+
* Finds the nearest <dialog> from the passed element.
|
16
|
+
*
|
17
|
+
* @param {Element} el to search from
|
18
|
+
* @return {HTMLDialogElement} dialog found
|
19
|
+
*/
|
20
|
+
function findNearestDialog(el) {
|
21
|
+
while (el) {
|
22
|
+
if (el.nodeName.toUpperCase() == 'DIALOG') {
|
23
|
+
return /** @type {HTMLDialogElement} */ (el);
|
24
|
+
}
|
25
|
+
el = el.parentElement;
|
26
|
+
}
|
27
|
+
return null;
|
28
|
+
}
|
29
|
+
|
30
|
+
/**
|
31
|
+
* Blur the specified element, as long as it's not the HTML body element.
|
32
|
+
* This works around an IE9/10 bug - blurring the body causes Windows to
|
33
|
+
* blur the whole application.
|
34
|
+
*
|
35
|
+
* @param {Element} el to blur
|
36
|
+
*/
|
37
|
+
function safeBlur(el) {
|
38
|
+
if (el && el.blur && el != document.body) {
|
39
|
+
el.blur();
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
/**
|
44
|
+
* @param {!NodeList} nodeList to search
|
45
|
+
* @param {Node} node to find
|
46
|
+
* @return {boolean} whether node is inside nodeList
|
47
|
+
*/
|
48
|
+
function inNodeList(nodeList, node) {
|
49
|
+
for (var i = 0; i < nodeList.length; ++i) {
|
50
|
+
if (nodeList[i] == node) {
|
51
|
+
return true;
|
52
|
+
}
|
53
|
+
}
|
54
|
+
return false;
|
55
|
+
}
|
56
|
+
|
57
|
+
/**
|
58
|
+
* @param {!HTMLDialogElement} dialog to upgrade
|
59
|
+
* @constructor
|
60
|
+
*/
|
61
|
+
function dialogPolyfillInfo(dialog) {
|
62
|
+
this.dialog_ = dialog;
|
63
|
+
this.replacedStyleTop_ = false;
|
64
|
+
this.openAsModal_ = false;
|
65
|
+
|
66
|
+
// Set a11y role. Browsers that support dialog implicitly know this already.
|
67
|
+
if (!dialog.hasAttribute('role')) {
|
68
|
+
dialog.setAttribute('role', 'dialog');
|
69
|
+
}
|
70
|
+
|
71
|
+
dialog.show = this.show.bind(this);
|
72
|
+
dialog.showModal = this.showModal.bind(this);
|
73
|
+
dialog.close = this.close.bind(this);
|
74
|
+
|
75
|
+
if (!('returnValue' in dialog)) {
|
76
|
+
dialog.returnValue = '';
|
77
|
+
}
|
78
|
+
|
79
|
+
this.maybeHideModal = this.maybeHideModal.bind(this);
|
80
|
+
if ('MutationObserver' in window) {
|
81
|
+
// IE11+, most other browsers.
|
82
|
+
var mo = new MutationObserver(this.maybeHideModal);
|
83
|
+
mo.observe(dialog, { attributes: true, attributeFilter: ['open'] });
|
84
|
+
} else {
|
85
|
+
dialog.addEventListener('DOMAttrModified', this.maybeHideModal);
|
86
|
+
}
|
87
|
+
// Note that the DOM is observed inside DialogManager while any dialog
|
88
|
+
// is being displayed as a modal, to catch modal removal from the DOM.
|
89
|
+
|
90
|
+
Object.defineProperty(dialog, 'open', {
|
91
|
+
set: this.setOpen.bind(this),
|
92
|
+
get: dialog.hasAttribute.bind(dialog, 'open')
|
93
|
+
});
|
94
|
+
|
95
|
+
this.backdrop_ = document.createElement('div');
|
96
|
+
this.backdrop_.className = 'backdrop';
|
97
|
+
this.backdropClick_ = this.backdropClick_.bind(this);
|
98
|
+
}
|
99
|
+
|
100
|
+
dialogPolyfillInfo.prototype = {
|
101
|
+
|
102
|
+
get dialog() {
|
103
|
+
return this.dialog_;
|
104
|
+
},
|
105
|
+
|
106
|
+
/**
|
107
|
+
* Maybe remove this dialog from the modal top layer. This is called when
|
108
|
+
* a modal dialog may no longer be tenable, e.g., when the dialog is no
|
109
|
+
* longer open or is no longer part of the DOM.
|
110
|
+
*/
|
111
|
+
maybeHideModal: function() {
|
112
|
+
if (!this.openAsModal_) { return; }
|
113
|
+
if (this.dialog_.hasAttribute('open') &&
|
114
|
+
document.body.contains(this.dialog_)) { return; }
|
115
|
+
|
116
|
+
this.openAsModal_ = false;
|
117
|
+
this.dialog_.style.zIndex = '';
|
118
|
+
|
119
|
+
// This won't match the native <dialog> exactly because if the user set
|
120
|
+
// top on a centered polyfill dialog, that top gets thrown away when the
|
121
|
+
// dialog is closed. Not sure it's possible to polyfill this perfectly.
|
122
|
+
if (this.replacedStyleTop_) {
|
123
|
+
this.dialog_.style.top = '';
|
124
|
+
this.replacedStyleTop_ = false;
|
125
|
+
}
|
126
|
+
|
127
|
+
// Optimistically clear the modal part of this <dialog>.
|
128
|
+
this.backdrop_.removeEventListener('click', this.backdropClick_);
|
129
|
+
if (this.backdrop_.parentElement) {
|
130
|
+
this.backdrop_.parentElement.removeChild(this.backdrop_);
|
131
|
+
}
|
132
|
+
dialogPolyfill.dm.removeDialog(this);
|
133
|
+
},
|
134
|
+
|
135
|
+
/**
|
136
|
+
* @param {boolean} value whether to open or close this dialog
|
137
|
+
*/
|
138
|
+
setOpen: function(value) {
|
139
|
+
if (value) {
|
140
|
+
this.dialog_.hasAttribute('open') || this.dialog_.setAttribute('open', '');
|
141
|
+
} else {
|
142
|
+
this.dialog_.removeAttribute('open');
|
143
|
+
this.maybeHideModal(); // nb. redundant with MutationObserver
|
144
|
+
}
|
145
|
+
},
|
146
|
+
|
147
|
+
/**
|
148
|
+
* Handles clicks on the fake .backdrop element, redirecting them as if
|
149
|
+
* they were on the dialog itself.
|
150
|
+
*
|
151
|
+
* @param {!Event} e to redirect
|
152
|
+
*/
|
153
|
+
backdropClick_: function(e) {
|
154
|
+
var redirectedEvent = document.createEvent('MouseEvents');
|
155
|
+
redirectedEvent.initMouseEvent(e.type, e.bubbles, e.cancelable, window,
|
156
|
+
e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey,
|
157
|
+
e.altKey, e.shiftKey, e.metaKey, e.button, e.relatedTarget);
|
158
|
+
this.dialog_.dispatchEvent(redirectedEvent);
|
159
|
+
e.stopPropagation();
|
160
|
+
},
|
161
|
+
|
162
|
+
/**
|
163
|
+
* Focuses on the first focusable element within the dialog. This will always blur the current
|
164
|
+
* focus, even if nothing within the dialog is found.
|
165
|
+
*/
|
166
|
+
focus_: function() {
|
167
|
+
// Find element with `autofocus` attribute, or fall back to the first form/tabindex control.
|
168
|
+
var target = this.dialog_.querySelector('[autofocus]:not([disabled])');
|
169
|
+
if (!target) {
|
170
|
+
// Note that this is 'any focusable area'. This list is probably not exhaustive, but the
|
171
|
+
// alternative involves stepping through and trying to focus everything.
|
172
|
+
var opts = ['button', 'input', 'keygen', 'select', 'textarea'];
|
173
|
+
var query = opts.map(function(el) {
|
174
|
+
return el + ':not([disabled])';
|
175
|
+
});
|
176
|
+
// TODO(samthor): tabindex values that are not numeric are not focusable.
|
177
|
+
query.push('[tabindex]:not([disabled]):not([tabindex=""])'); // tabindex != "", not disabled
|
178
|
+
target = this.dialog_.querySelector(query.join(', '));
|
179
|
+
}
|
180
|
+
safeBlur(document.activeElement);
|
181
|
+
target && target.focus();
|
182
|
+
},
|
183
|
+
|
184
|
+
/**
|
185
|
+
* Sets the zIndex for the backdrop and dialog.
|
186
|
+
*
|
187
|
+
* @param {number} dialogZ
|
188
|
+
* @param {number} backdropZ
|
189
|
+
*/
|
190
|
+
updateZIndex: function(dialogZ, backdropZ) {
|
191
|
+
if (dialogZ < backdropZ) {
|
192
|
+
throw new Error('dialogZ should never be < backdropZ');
|
193
|
+
}
|
194
|
+
this.dialog_.style.zIndex = dialogZ;
|
195
|
+
this.backdrop_.style.zIndex = backdropZ;
|
196
|
+
},
|
197
|
+
|
198
|
+
/**
|
199
|
+
* Shows the dialog. If the dialog is already open, this does nothing.
|
200
|
+
*/
|
201
|
+
show: function() {
|
202
|
+
if (!this.dialog_.open) {
|
203
|
+
this.setOpen(true);
|
204
|
+
this.focus_();
|
205
|
+
}
|
206
|
+
},
|
207
|
+
|
208
|
+
/**
|
209
|
+
* Show this dialog modally.
|
210
|
+
*/
|
211
|
+
showModal: function() {
|
212
|
+
if (this.dialog_.hasAttribute('open')) {
|
213
|
+
throw new Error('Failed to execute \'showModal\' on dialog: The element is already open, and therefore cannot be opened modally.');
|
214
|
+
}
|
215
|
+
if (!document.body.contains(this.dialog_)) {
|
216
|
+
throw new Error('Failed to execute \'showModal\' on dialog: The element is not in a Document.');
|
217
|
+
}
|
218
|
+
if (!dialogPolyfill.dm.pushDialog(this)) {
|
219
|
+
throw new Error('Failed to execute \'showModal\' on dialog: There are too many open modal dialogs.');
|
220
|
+
}
|
221
|
+
this.show();
|
222
|
+
this.openAsModal_ = true;
|
223
|
+
|
224
|
+
// Optionally center vertically, relative to the current viewport.
|
225
|
+
if (dialogPolyfill.needsCentering(this.dialog_)) {
|
226
|
+
dialogPolyfill.reposition(this.dialog_);
|
227
|
+
this.replacedStyleTop_ = true;
|
228
|
+
} else {
|
229
|
+
this.replacedStyleTop_ = false;
|
230
|
+
}
|
231
|
+
|
232
|
+
// Insert backdrop.
|
233
|
+
this.backdrop_.addEventListener('click', this.backdropClick_);
|
234
|
+
this.dialog_.parentNode.insertBefore(this.backdrop_,
|
235
|
+
this.dialog_.nextSibling);
|
236
|
+
},
|
237
|
+
|
238
|
+
/**
|
239
|
+
* Closes this HTMLDialogElement. This is optional vs clearing the open
|
240
|
+
* attribute, however this fires a 'close' event.
|
241
|
+
*
|
242
|
+
* @param {string=} opt_returnValue to use as the returnValue
|
243
|
+
*/
|
244
|
+
close: function(opt_returnValue) {
|
245
|
+
if (!this.dialog_.hasAttribute('open')) {
|
246
|
+
throw new Error('Failed to execute \'close\' on dialog: The element does not have an \'open\' attribute, and therefore cannot be closed.');
|
247
|
+
}
|
248
|
+
this.setOpen(false);
|
249
|
+
|
250
|
+
// Leave returnValue untouched in case it was set directly on the element
|
251
|
+
if (opt_returnValue !== undefined) {
|
252
|
+
this.dialog_.returnValue = opt_returnValue;
|
253
|
+
}
|
254
|
+
|
255
|
+
// Triggering "close" event for any attached listeners on the <dialog>.
|
256
|
+
var closeEvent = new supportCustomEvent('close', {
|
257
|
+
bubbles: false,
|
258
|
+
cancelable: false
|
259
|
+
});
|
260
|
+
this.dialog_.dispatchEvent(closeEvent);
|
261
|
+
}
|
262
|
+
|
263
|
+
};
|
264
|
+
|
265
|
+
var dialogPolyfill = {};
|
266
|
+
|
267
|
+
dialogPolyfill.reposition = function(element) {
|
268
|
+
var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
|
269
|
+
var topValue = scrollTop + (window.innerHeight - element.offsetHeight) / 2;
|
270
|
+
element.style.top = Math.max(scrollTop, topValue) + 'px';
|
271
|
+
};
|
272
|
+
|
273
|
+
dialogPolyfill.isInlinePositionSetByStylesheet = function(element) {
|
274
|
+
for (var i = 0; i < document.styleSheets.length; ++i) {
|
275
|
+
var styleSheet = document.styleSheets[i];
|
276
|
+
var cssRules = null;
|
277
|
+
// Some browsers throw on cssRules.
|
278
|
+
try {
|
279
|
+
cssRules = styleSheet.cssRules;
|
280
|
+
} catch (e) {}
|
281
|
+
if (!cssRules) { continue; }
|
282
|
+
for (var j = 0; j < cssRules.length; ++j) {
|
283
|
+
var rule = cssRules[j];
|
284
|
+
var selectedNodes = null;
|
285
|
+
// Ignore errors on invalid selector texts.
|
286
|
+
try {
|
287
|
+
selectedNodes = document.querySelectorAll(rule.selectorText);
|
288
|
+
} catch(e) {}
|
289
|
+
if (!selectedNodes || !inNodeList(selectedNodes, element)) {
|
290
|
+
continue;
|
291
|
+
}
|
292
|
+
var cssTop = rule.style.getPropertyValue('top');
|
293
|
+
var cssBottom = rule.style.getPropertyValue('bottom');
|
294
|
+
if ((cssTop && cssTop != 'auto') || (cssBottom && cssBottom != 'auto')) {
|
295
|
+
return true;
|
296
|
+
}
|
297
|
+
}
|
298
|
+
}
|
299
|
+
return false;
|
300
|
+
};
|
301
|
+
|
302
|
+
dialogPolyfill.needsCentering = function(dialog) {
|
303
|
+
var computedStyle = window.getComputedStyle(dialog);
|
304
|
+
if (computedStyle.position != 'absolute') {
|
305
|
+
return false;
|
306
|
+
}
|
307
|
+
|
308
|
+
// We must determine whether the top/bottom specified value is non-auto. In
|
309
|
+
// WebKit/Blink, checking computedStyle.top == 'auto' is sufficient, but
|
310
|
+
// Firefox returns the used value. So we do this crazy thing instead: check
|
311
|
+
// the inline style and then go through CSS rules.
|
312
|
+
if ((dialog.style.top != 'auto' && dialog.style.top != '') ||
|
313
|
+
(dialog.style.bottom != 'auto' && dialog.style.bottom != ''))
|
314
|
+
return false;
|
315
|
+
return !dialogPolyfill.isInlinePositionSetByStylesheet(dialog);
|
316
|
+
};
|
317
|
+
|
318
|
+
/**
|
319
|
+
* @param {!Element} element to force upgrade
|
320
|
+
*/
|
321
|
+
dialogPolyfill.forceRegisterDialog = function(element) {
|
322
|
+
if (element.showModal) {
|
323
|
+
console.warn('This browser already supports <dialog>, the polyfill ' +
|
324
|
+
'may not work correctly', element);
|
325
|
+
}
|
326
|
+
if (element.nodeName.toUpperCase() != 'DIALOG') {
|
327
|
+
throw new Error('Failed to register dialog: The element is not a dialog.');
|
328
|
+
}
|
329
|
+
new dialogPolyfillInfo(/** @type {!HTMLDialogElement} */ (element));
|
330
|
+
};
|
331
|
+
|
332
|
+
/**
|
333
|
+
* @param {!Element} element to upgrade, if necessary
|
334
|
+
*/
|
335
|
+
dialogPolyfill.registerDialog = function(element) {
|
336
|
+
if (!element.showModal) {
|
337
|
+
dialogPolyfill.forceRegisterDialog(element);
|
338
|
+
}
|
339
|
+
};
|
340
|
+
|
341
|
+
/**
|
342
|
+
* @constructor
|
343
|
+
*/
|
344
|
+
dialogPolyfill.DialogManager = function() {
|
345
|
+
/** @type {!Array<!dialogPolyfillInfo>} */
|
346
|
+
this.pendingDialogStack = [];
|
347
|
+
|
348
|
+
// The overlay is used to simulate how a modal dialog blocks the document.
|
349
|
+
// The blocking dialog is positioned on top of the overlay, and the rest of
|
350
|
+
// the dialogs on the pending dialog stack are positioned below it. In the
|
351
|
+
// actual implementation, the modal dialog stacking is controlled by the
|
352
|
+
// top layer, where z-index has no effect.
|
353
|
+
this.overlay = document.createElement('div');
|
354
|
+
this.overlay.className = '_dialog_overlay';
|
355
|
+
this.overlay.addEventListener('click', function(e) {
|
356
|
+
e.stopPropagation();
|
357
|
+
this.checkDOM_(); // sanity-check DOM
|
358
|
+
}.bind(this));
|
359
|
+
|
360
|
+
this.handleKey_ = this.handleKey_.bind(this);
|
361
|
+
this.handleFocus_ = this.handleFocus_.bind(this);
|
362
|
+
this.handleRemove_ = this.handleRemove_.bind(this);
|
363
|
+
|
364
|
+
this.zIndexLow_ = 100000;
|
365
|
+
this.zIndexHigh_ = 100000 + 150;
|
366
|
+
};
|
367
|
+
|
368
|
+
/**
|
369
|
+
* @return {Element} the top HTML dialog element, if any
|
370
|
+
*/
|
371
|
+
dialogPolyfill.DialogManager.prototype.topDialogElement = function() {
|
372
|
+
var dpi = this.pendingDialogStack[0];
|
373
|
+
return dpi ? dpi.dialog : null;
|
374
|
+
};
|
375
|
+
|
376
|
+
/**
|
377
|
+
* Called on the first modal dialog being shown. Adds the overlay and related
|
378
|
+
* handlers.
|
379
|
+
*/
|
380
|
+
dialogPolyfill.DialogManager.prototype.blockDocument = function() {
|
381
|
+
document.body.appendChild(this.overlay);
|
382
|
+
document.body.addEventListener('focus', this.handleFocus_, true);
|
383
|
+
document.addEventListener('keydown', this.handleKey_);
|
384
|
+
document.addEventListener('DOMNodeRemoved', this.handleRemove_);
|
385
|
+
};
|
386
|
+
|
387
|
+
/**
|
388
|
+
* Called on the first modal dialog being removed, i.e., when no more modal
|
389
|
+
* dialogs are visible.
|
390
|
+
*/
|
391
|
+
dialogPolyfill.DialogManager.prototype.unblockDocument = function() {
|
392
|
+
document.body.removeChild(this.overlay);
|
393
|
+
document.body.removeEventListener('focus', this.handleFocus_, true);
|
394
|
+
document.removeEventListener('keydown', this.handleKey_);
|
395
|
+
document.removeEventListener('DOMNodeRemoved', this.handleRemove_);
|
396
|
+
};
|
397
|
+
|
398
|
+
dialogPolyfill.DialogManager.prototype.updateStacking = function() {
|
399
|
+
var zIndex = this.zIndexHigh_;
|
400
|
+
|
401
|
+
for (var i = 0, dpi; dpi = this.pendingDialogStack[i]; ++i) {
|
402
|
+
dpi.updateZIndex(--zIndex, --zIndex);
|
403
|
+
if (i === 0) {
|
404
|
+
this.overlay.style.zIndex = --zIndex;
|
405
|
+
}
|
406
|
+
}
|
407
|
+
};
|
408
|
+
|
409
|
+
dialogPolyfill.DialogManager.prototype.handleFocus_ = function(event) {
|
410
|
+
var candidate = findNearestDialog(/** @type {Element} */ (event.target));
|
411
|
+
if (candidate != this.topDialogElement()) {
|
412
|
+
event.preventDefault();
|
413
|
+
event.stopPropagation();
|
414
|
+
safeBlur(/** @type {Element} */ (event.target));
|
415
|
+
// TODO: Focus on the browser chrome (aka document) or the dialog itself
|
416
|
+
// depending on the tab direction.
|
417
|
+
return false;
|
418
|
+
}
|
419
|
+
};
|
420
|
+
|
421
|
+
dialogPolyfill.DialogManager.prototype.handleKey_ = function(event) {
|
422
|
+
if (event.keyCode == 27) {
|
423
|
+
event.preventDefault();
|
424
|
+
event.stopPropagation();
|
425
|
+
var cancelEvent = new supportCustomEvent('cancel', {
|
426
|
+
bubbles: false,
|
427
|
+
cancelable: true
|
428
|
+
});
|
429
|
+
var dialog = this.topDialogElement();
|
430
|
+
if (dialog.dispatchEvent(cancelEvent)) {
|
431
|
+
dialog.close();
|
432
|
+
}
|
433
|
+
}
|
434
|
+
};
|
435
|
+
|
436
|
+
/**
|
437
|
+
* Finds and removes any modal dialogs that are no longer on the page.
|
438
|
+
*/
|
439
|
+
dialogPolyfill.DialogManager.prototype.checkDOM_ = function() {
|
440
|
+
var clone = this.pendingDialogStack.slice();
|
441
|
+
clone.forEach(function(dpi) {
|
442
|
+
dpi.maybeHideModal();
|
443
|
+
});
|
444
|
+
};
|
445
|
+
|
446
|
+
dialogPolyfill.DialogManager.prototype.handleRemove_ = function(event) {
|
447
|
+
if (event.target.nodeName.toUpperCase() !== 'DIALOG') { return; }
|
448
|
+
this.checkDOM_();
|
449
|
+
};
|
450
|
+
|
451
|
+
/**
|
452
|
+
* @param {!dialogPolyfillInfo} dpi
|
453
|
+
* @return {boolean} whether the dialog was allowed
|
454
|
+
*/
|
455
|
+
dialogPolyfill.DialogManager.prototype.pushDialog = function(dpi) {
|
456
|
+
var allowed = (this.zIndexHigh_ - this.zIndexLow_) / 2 - 1;
|
457
|
+
if (this.pendingDialogStack.length >= allowed) {
|
458
|
+
return false;
|
459
|
+
}
|
460
|
+
if (this.pendingDialogStack.unshift(dpi) === 1) {
|
461
|
+
this.blockDocument();
|
462
|
+
}
|
463
|
+
this.updateStacking();
|
464
|
+
return true;
|
465
|
+
};
|
466
|
+
|
467
|
+
/**
|
468
|
+
* @param {!dialogPolyfillInfo} dpi
|
469
|
+
*/
|
470
|
+
dialogPolyfill.DialogManager.prototype.removeDialog = function(dpi) {
|
471
|
+
var index = this.pendingDialogStack.indexOf(dpi);
|
472
|
+
if (index == -1) { return; }
|
473
|
+
|
474
|
+
this.pendingDialogStack.splice(index, 1);
|
475
|
+
this.updateStacking();
|
476
|
+
if (this.pendingDialogStack.length === 0) {
|
477
|
+
this.unblockDocument();
|
478
|
+
}
|
479
|
+
};
|
480
|
+
|
481
|
+
dialogPolyfill.dm = new dialogPolyfill.DialogManager();
|
482
|
+
|
483
|
+
/**
|
484
|
+
* Global form 'dialog' method handler. Closes a dialog correctly on submit
|
485
|
+
* and possibly sets its return value.
|
486
|
+
*/
|
487
|
+
document.addEventListener('submit', function(ev) {
|
488
|
+
var target = ev.target;
|
489
|
+
if (!target || !target.hasAttribute('method')) { return; }
|
490
|
+
if (target.getAttribute('method').toLowerCase() != 'dialog') { return; }
|
491
|
+
ev.preventDefault();
|
492
|
+
|
493
|
+
var dialog = findNearestDialog(/** @type {Element} */ (ev.target));
|
494
|
+
if (!dialog) { return; }
|
495
|
+
|
496
|
+
// FIXME: The original event doesn't contain the element used to submit the
|
497
|
+
// form (if any). Look in some possible places.
|
498
|
+
var returnValue;
|
499
|
+
var cands = [document.activeElement, ev.explicitOriginalTarget];
|
500
|
+
var els = ['BUTTON', 'INPUT'];
|
501
|
+
cands.some(function(cand) {
|
502
|
+
if (cand && cand.form == ev.target && els.indexOf(cand.nodeName.toUpperCase()) != -1) {
|
503
|
+
returnValue = cand.value;
|
504
|
+
return true;
|
505
|
+
}
|
506
|
+
});
|
507
|
+
dialog.close(returnValue);
|
508
|
+
}, true);
|
509
|
+
|
510
|
+
dialogPolyfill['forceRegisterDialog'] = dialogPolyfill.forceRegisterDialog;
|
511
|
+
dialogPolyfill['registerDialog'] = dialogPolyfill.registerDialog;
|
512
|
+
|
513
|
+
if (typeof define === 'function' && 'amd' in define) {
|
514
|
+
// AMD support
|
515
|
+
define(function() { return dialogPolyfill; });
|
516
|
+
} else if (typeof module === 'object' && typeof module['exports'] === 'object') {
|
517
|
+
// CommonJS support
|
518
|
+
module['exports'] = dialogPolyfill;
|
519
|
+
} else {
|
520
|
+
// all others
|
521
|
+
window['dialogPolyfill'] = dialogPolyfill;
|
522
|
+
}
|
523
|
+
})();
|
@@ -0,0 +1,46 @@
|
|
1
|
+
dialog {
|
2
|
+
position: absolute;
|
3
|
+
left: 0; right: 0;
|
4
|
+
width: -moz-fit-content;
|
5
|
+
width: -webkit-fit-content;
|
6
|
+
width: fit-content;
|
7
|
+
height: -moz-fit-content;
|
8
|
+
height: -webkit-fit-content;
|
9
|
+
height: fit-content;
|
10
|
+
margin: auto;
|
11
|
+
border: solid;
|
12
|
+
padding: 1em;
|
13
|
+
background: white;
|
14
|
+
color: black;
|
15
|
+
display: none;
|
16
|
+
}
|
17
|
+
|
18
|
+
dialog[open] {
|
19
|
+
display: block;
|
20
|
+
}
|
21
|
+
|
22
|
+
dialog + .backdrop {
|
23
|
+
position: fixed;
|
24
|
+
top: 0; right: 0; bottom: 0; left: 0;
|
25
|
+
background: rgba(0,0,0,0.1);
|
26
|
+
}
|
27
|
+
|
28
|
+
/* for small devices, modal dialogs go full-screen */
|
29
|
+
@media screen and (max-width: 540px) {
|
30
|
+
dialog[_polyfill_modal] { /* TODO: implement */
|
31
|
+
top: 0;
|
32
|
+
width: auto;
|
33
|
+
margin: 1em;
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
._dialog_overlay {
|
38
|
+
position: fixed;
|
39
|
+
top: 0; right: 0; bottom: 0; left: 0;
|
40
|
+
}
|
41
|
+
|
42
|
+
dialog.fixed {
|
43
|
+
position: fixed;
|
44
|
+
top: 50%;
|
45
|
+
transform: translate(0, -50%);
|
46
|
+
}
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dialog-polyfill-rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.5.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- MJ Abadilla
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-01-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.13'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.13'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
description: Gemified version of https://github.com/GoogleChrome/dialog-polyfill.
|
42
|
+
email:
|
43
|
+
- mjmaix@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- ".gitignore"
|
49
|
+
- CODE_OF_CONDUCT.md
|
50
|
+
- Gemfile
|
51
|
+
- LICENSE.txt
|
52
|
+
- README.md
|
53
|
+
- Rakefile
|
54
|
+
- bin/console
|
55
|
+
- bin/setup
|
56
|
+
- dialog-polyfill-rails.gemspec
|
57
|
+
- lib/dialog/polyfill/rails.rb
|
58
|
+
- lib/dialog/polyfill/rails/version.rb
|
59
|
+
- vendor/assets/javascripts/dialog-polyfill.js
|
60
|
+
- vendor/assets/stylesheets/dialog-polyfill.css
|
61
|
+
homepage: https://github.com/mjmaix/dialog-polyfill-rails
|
62
|
+
licenses: []
|
63
|
+
metadata:
|
64
|
+
allowed_push_host: https://rubygems.org
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options: []
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
requirements: []
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 2.6.8
|
82
|
+
signing_key:
|
83
|
+
specification_version: 4
|
84
|
+
summary: Gemified version of https://github.com/GoogleChrome/dialog-polyfill.
|
85
|
+
test_files: []
|