mirror of https://github.com/rails/rails
Merge pull request #45546 from skipkayhil/decaffeinate-ujs
Convert rails-ujs to ES2015 modules
This commit is contained in:
commit
3d1f38fa30
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"extends": "eslint:recommended",
|
||||
"rules": {
|
||||
"semi": ["error", "never"],
|
||||
"quotes": ["error", "double"],
|
||||
"no-unused-vars": ["error", { "vars": "all", "args": "none" }]
|
||||
},
|
||||
"plugins": [
|
||||
"import"
|
||||
],
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"jquery": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module"
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ require "open3"
|
|||
desc "Default Task"
|
||||
task default: :test
|
||||
|
||||
task package: %w( assets:compile assets:verify )
|
||||
task :package
|
||||
|
||||
# Run the unit tests
|
||||
|
||||
|
@ -105,52 +105,6 @@ namespace :ujs do
|
|||
end
|
||||
end
|
||||
|
||||
namespace :assets do
|
||||
desc "Compile Action View assets"
|
||||
task :compile do
|
||||
require "blade"
|
||||
require "sprockets"
|
||||
require "sprockets/export"
|
||||
Blade.build
|
||||
end
|
||||
|
||||
desc "Verify compiled Action View assets"
|
||||
task :verify do
|
||||
file = "lib/assets/compiled/rails-ujs.js"
|
||||
pathname = Pathname.new("#{__dir__}/#{file}")
|
||||
|
||||
print "[verify] #{file} exists "
|
||||
if pathname.exist?
|
||||
puts "[OK]"
|
||||
else
|
||||
$stderr.puts "[FAIL]"
|
||||
fail
|
||||
end
|
||||
|
||||
print "[verify] #{file} is a UMD module "
|
||||
if /module\.exports.*define\.amd/m.match?(pathname.read)
|
||||
puts "[OK]"
|
||||
else
|
||||
$stderr.puts "[FAIL]"
|
||||
fail
|
||||
end
|
||||
|
||||
print "[verify] #{__dir__} can be required as a module "
|
||||
js = <<-JS
|
||||
window = { Event: class {} }
|
||||
class Element {}
|
||||
require('#{__dir__}')
|
||||
JS
|
||||
_, stderr, status = Open3.capture3("node", "--print", js)
|
||||
if status.success?
|
||||
puts "[OK]"
|
||||
else
|
||||
$stderr.puts "[FAIL]\n#{stderr}"
|
||||
fail
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
task :lines do
|
||||
load File.expand_path("../tools/line_statistics", __dir__)
|
||||
files = FileList["lib/**/*.rb"]
|
||||
|
|
|
@ -17,7 +17,7 @@ Gem::Specification.new do |s|
|
|||
s.email = "david@loudthinking.com"
|
||||
s.homepage = "https://rubyonrails.org"
|
||||
|
||||
s.files = Dir["CHANGELOG.md", "README.rdoc", "MIT-LICENSE", "lib/**/*"]
|
||||
s.files = Dir["CHANGELOG.md", "README.rdoc", "MIT-LICENSE", "lib/**/*", "app/assets/javascripts/*.js"]
|
||||
s.require_path = "lib"
|
||||
s.requirements << "none"
|
||||
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
#= require ./rails-ujs/BANNER
|
||||
#= export Rails
|
||||
#= require_self
|
||||
#= require_tree ./rails-ujs/utils
|
||||
#= require_tree ./rails-ujs/features
|
||||
#= require ./rails-ujs/start
|
||||
|
||||
@Rails =
|
||||
# Link elements bound by rails-ujs
|
||||
linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote]:not([disabled]), a[data-disable-with], a[data-disable]'
|
||||
|
||||
# Button elements bound by rails-ujs
|
||||
buttonClickSelector:
|
||||
selector: 'button[data-remote]:not([form]), button[data-confirm]:not([form])'
|
||||
exclude: 'form button'
|
||||
|
||||
# Select elements bound by rails-ujs
|
||||
inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]'
|
||||
|
||||
# Form elements bound by rails-ujs
|
||||
formSubmitSelector: 'form:not([data-turbo=true])',
|
||||
|
||||
# Form input elements bound by rails-ujs
|
||||
formInputClickSelector: 'form:not([data-turbo=true]) input[type=submit], form:not([data-turbo=true]) input[type=image], form:not([data-turbo=true]) button[type=submit], form:not([data-turbo=true]) button:not([type]), input[type=submit][form], input[type=image][form], button[type=submit][form], button[form]:not([type])',
|
||||
|
||||
# Form input elements disabled during form submission
|
||||
formDisableSelector: 'input[data-disable-with]:enabled, button[data-disable-with]:enabled, textarea[data-disable-with]:enabled, input[data-disable]:enabled, button[data-disable]:enabled, textarea[data-disable]:enabled'
|
||||
|
||||
# Form input elements re-enabled after form submission
|
||||
formEnableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled, input[data-disable]:disabled, button[data-disable]:disabled, textarea[data-disable]:disabled'
|
||||
|
||||
# Form file input elements
|
||||
fileInputSelector: 'input[name][type=file]:not([disabled])'
|
||||
|
||||
# Link onClick disable selector with possible re-enable after remote submission
|
||||
linkDisableSelector: 'a[data-disable-with], a[data-disable]'
|
||||
|
||||
# Button onClick disable selector with possible re-enable after remote submission
|
||||
buttonDisableSelector: 'button[data-remote][data-disable-with], button[data-remote][data-disable]'
|
|
@ -0,0 +1,668 @@
|
|||
/*
|
||||
Unobtrusive JavaScript
|
||||
https://github.com/rails/rails/blob/main/actionview/app/javascript
|
||||
Released under the MIT license
|
||||
*/
|
||||
const linkClickSelector = "a[data-confirm], a[data-method], a[data-remote]:not([disabled]), a[data-disable-with], a[data-disable]";
|
||||
|
||||
const buttonClickSelector = {
|
||||
selector: "button[data-remote]:not([form]), button[data-confirm]:not([form])",
|
||||
exclude: "form button"
|
||||
};
|
||||
|
||||
const inputChangeSelector = "select[data-remote], input[data-remote], textarea[data-remote]";
|
||||
|
||||
const formSubmitSelector = "form:not([data-turbo=true])";
|
||||
|
||||
const formInputClickSelector = "form:not([data-turbo=true]) input[type=submit], form:not([data-turbo=true]) input[type=image], form:not([data-turbo=true]) button[type=submit], form:not([data-turbo=true]) button:not([type]), input[type=submit][form], input[type=image][form], button[type=submit][form], button[form]:not([type])";
|
||||
|
||||
const formDisableSelector = "input[data-disable-with]:enabled, button[data-disable-with]:enabled, textarea[data-disable-with]:enabled, input[data-disable]:enabled, button[data-disable]:enabled, textarea[data-disable]:enabled";
|
||||
|
||||
const formEnableSelector = "input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled, input[data-disable]:disabled, button[data-disable]:disabled, textarea[data-disable]:disabled";
|
||||
|
||||
const fileInputSelector = "input[name][type=file]:not([disabled])";
|
||||
|
||||
const linkDisableSelector = "a[data-disable-with], a[data-disable]";
|
||||
|
||||
const buttonDisableSelector = "button[data-remote][data-disable-with], button[data-remote][data-disable]";
|
||||
|
||||
let nonce = null;
|
||||
|
||||
const loadCSPNonce = () => {
|
||||
const metaTag = document.querySelector("meta[name=csp-nonce]");
|
||||
return nonce = metaTag && metaTag.content;
|
||||
};
|
||||
|
||||
const cspNonce = () => nonce || loadCSPNonce();
|
||||
|
||||
const m = Element.prototype.matches || Element.prototype.matchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.webkitMatchesSelector;
|
||||
|
||||
const matches = function(element, selector) {
|
||||
if (selector.exclude) {
|
||||
return m.call(element, selector.selector) && !m.call(element, selector.exclude);
|
||||
} else {
|
||||
return m.call(element, selector);
|
||||
}
|
||||
};
|
||||
|
||||
const EXPANDO = "_ujsData";
|
||||
|
||||
const getData = (element, key) => element[EXPANDO] ? element[EXPANDO][key] : undefined;
|
||||
|
||||
const setData = function(element, key, value) {
|
||||
if (!element[EXPANDO]) {
|
||||
element[EXPANDO] = {};
|
||||
}
|
||||
return element[EXPANDO][key] = value;
|
||||
};
|
||||
|
||||
const $ = selector => Array.prototype.slice.call(document.querySelectorAll(selector));
|
||||
|
||||
const csrfToken = () => {
|
||||
const meta = document.querySelector("meta[name=csrf-token]");
|
||||
return meta && meta.content;
|
||||
};
|
||||
|
||||
const csrfParam = () => {
|
||||
const meta = document.querySelector("meta[name=csrf-param]");
|
||||
return meta && meta.content;
|
||||
};
|
||||
|
||||
const CSRFProtection = xhr => {
|
||||
const token = csrfToken();
|
||||
if (token) {
|
||||
return xhr.setRequestHeader("X-CSRF-Token", token);
|
||||
}
|
||||
};
|
||||
|
||||
const refreshCSRFTokens = () => {
|
||||
const token = csrfToken();
|
||||
const param = csrfParam();
|
||||
if (token && param) {
|
||||
return $('form input[name="' + param + '"]').forEach((input => input.value = token));
|
||||
}
|
||||
};
|
||||
|
||||
const AcceptHeaders = {
|
||||
"*": "*/*",
|
||||
text: "text/plain",
|
||||
html: "text/html",
|
||||
xml: "application/xml, text/xml",
|
||||
json: "application/json, text/javascript",
|
||||
script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
|
||||
};
|
||||
|
||||
const ajax = options => {
|
||||
options = prepareOptions(options);
|
||||
var xhr = createXHR(options, (function() {
|
||||
const response = processResponse(xhr.response != null ? xhr.response : xhr.responseText, xhr.getResponseHeader("Content-Type"));
|
||||
if (Math.floor(xhr.status / 100) === 2) {
|
||||
if (typeof options.success === "function") {
|
||||
options.success(response, xhr.statusText, xhr);
|
||||
}
|
||||
} else {
|
||||
if (typeof options.error === "function") {
|
||||
options.error(response, xhr.statusText, xhr);
|
||||
}
|
||||
}
|
||||
return typeof options.complete === "function" ? options.complete(xhr, xhr.statusText) : undefined;
|
||||
}));
|
||||
if (options.beforeSend && !options.beforeSend(xhr, options)) {
|
||||
return false;
|
||||
}
|
||||
if (xhr.readyState === XMLHttpRequest.OPENED) {
|
||||
return xhr.send(options.data);
|
||||
}
|
||||
};
|
||||
|
||||
var prepareOptions = function(options) {
|
||||
options.url = options.url || location.href;
|
||||
options.type = options.type.toUpperCase();
|
||||
if (options.type === "GET" && options.data) {
|
||||
if (options.url.indexOf("?") < 0) {
|
||||
options.url += "?" + options.data;
|
||||
} else {
|
||||
options.url += "&" + options.data;
|
||||
}
|
||||
}
|
||||
if (!(options.dataType in AcceptHeaders)) {
|
||||
options.dataType = "*";
|
||||
}
|
||||
options.accept = AcceptHeaders[options.dataType];
|
||||
if (options.dataType !== "*") {
|
||||
options.accept += ", */*; q=0.01";
|
||||
}
|
||||
return options;
|
||||
};
|
||||
|
||||
var createXHR = function(options, done) {
|
||||
const xhr = new XMLHttpRequest;
|
||||
xhr.open(options.type, options.url, true);
|
||||
xhr.setRequestHeader("Accept", options.accept);
|
||||
if (typeof options.data === "string") {
|
||||
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
|
||||
}
|
||||
if (!options.crossDomain) {
|
||||
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
||||
CSRFProtection(xhr);
|
||||
}
|
||||
xhr.withCredentials = !!options.withCredentials;
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||
return done(xhr);
|
||||
}
|
||||
};
|
||||
return xhr;
|
||||
};
|
||||
|
||||
var processResponse = function(response, type) {
|
||||
if (typeof response === "string" && typeof type === "string") {
|
||||
if (type.match(/\bjson\b/)) {
|
||||
try {
|
||||
response = JSON.parse(response);
|
||||
} catch (error) {}
|
||||
} else if (type.match(/\b(?:java|ecma)script\b/)) {
|
||||
const script = document.createElement("script");
|
||||
script.setAttribute("nonce", cspNonce());
|
||||
script.text = response;
|
||||
document.head.appendChild(script).parentNode.removeChild(script);
|
||||
} else if (type.match(/\b(xml|html|svg)\b/)) {
|
||||
const parser = new DOMParser;
|
||||
type = type.replace(/;.+/, "");
|
||||
try {
|
||||
response = parser.parseFromString(response, type);
|
||||
} catch (error1) {}
|
||||
}
|
||||
}
|
||||
return response;
|
||||
};
|
||||
|
||||
const href = element => element.href;
|
||||
|
||||
const isCrossDomain = function(url) {
|
||||
const originAnchor = document.createElement("a");
|
||||
originAnchor.href = location.href;
|
||||
const urlAnchor = document.createElement("a");
|
||||
try {
|
||||
urlAnchor.href = url;
|
||||
return !((!urlAnchor.protocol || urlAnchor.protocol === ":") && !urlAnchor.host || originAnchor.protocol + "//" + originAnchor.host === urlAnchor.protocol + "//" + urlAnchor.host);
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
let preventDefault;
|
||||
|
||||
let {CustomEvent: CustomEvent} = window;
|
||||
|
||||
if (typeof CustomEvent !== "function") {
|
||||
CustomEvent = function(event, params) {
|
||||
const evt = document.createEvent("CustomEvent");
|
||||
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
|
||||
return evt;
|
||||
};
|
||||
CustomEvent.prototype = window.Event.prototype;
|
||||
({preventDefault: preventDefault} = CustomEvent.prototype);
|
||||
CustomEvent.prototype.preventDefault = function() {
|
||||
const result = preventDefault.call(this);
|
||||
if (this.cancelable && !this.defaultPrevented) {
|
||||
Object.defineProperty(this, "defaultPrevented", {
|
||||
get() {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
const fire = (obj, name, data) => {
|
||||
const event = new CustomEvent(name, {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
detail: data
|
||||
});
|
||||
obj.dispatchEvent(event);
|
||||
return !event.defaultPrevented;
|
||||
};
|
||||
|
||||
const stopEverything = e => {
|
||||
fire(e.target, "ujs:everythingStopped");
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
};
|
||||
|
||||
const delegate = (element, selector, eventType, handler) => element.addEventListener(eventType, (function(e) {
|
||||
let {target: target} = e;
|
||||
while (!!(target instanceof Element) && !matches(target, selector)) {
|
||||
target = target.parentNode;
|
||||
}
|
||||
if (target instanceof Element && handler.call(target, e) === false) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}));
|
||||
|
||||
const toArray = e => Array.prototype.slice.call(e);
|
||||
|
||||
const serializeElement = (element, additionalParam) => {
|
||||
let inputs = [ element ];
|
||||
if (matches(element, "form")) {
|
||||
inputs = toArray(element.elements);
|
||||
}
|
||||
const params = [];
|
||||
inputs.forEach((function(input) {
|
||||
if (!input.name || input.disabled) {
|
||||
return;
|
||||
}
|
||||
if (matches(input, "fieldset[disabled] *")) {
|
||||
return;
|
||||
}
|
||||
if (matches(input, "select")) {
|
||||
toArray(input.options).forEach((function(option) {
|
||||
if (option.selected) {
|
||||
params.push({
|
||||
name: input.name,
|
||||
value: option.value
|
||||
});
|
||||
}
|
||||
}));
|
||||
} else if (input.checked || [ "radio", "checkbox", "submit" ].indexOf(input.type) === -1) {
|
||||
params.push({
|
||||
name: input.name,
|
||||
value: input.value
|
||||
});
|
||||
}
|
||||
}));
|
||||
if (additionalParam) {
|
||||
params.push(additionalParam);
|
||||
}
|
||||
return params.map((function(param) {
|
||||
if (param.name) {
|
||||
return `${encodeURIComponent(param.name)}=${encodeURIComponent(param.value)}`;
|
||||
} else {
|
||||
return param;
|
||||
}
|
||||
})).join("&");
|
||||
};
|
||||
|
||||
const formElements = (form, selector) => {
|
||||
if (matches(form, "form")) {
|
||||
return toArray(form.elements).filter((el => matches(el, selector)));
|
||||
} else {
|
||||
return toArray(form.querySelectorAll(selector));
|
||||
}
|
||||
};
|
||||
|
||||
const handleConfirmWithRails = rails => function(e) {
|
||||
if (!allowAction(this, rails)) {
|
||||
stopEverything(e);
|
||||
}
|
||||
};
|
||||
|
||||
const confirm = (message, element) => window.confirm(message);
|
||||
|
||||
var allowAction = function(element, rails) {
|
||||
let callback;
|
||||
const message = element.getAttribute("data-confirm");
|
||||
if (!message) {
|
||||
return true;
|
||||
}
|
||||
let answer = false;
|
||||
if (fire(element, "confirm")) {
|
||||
try {
|
||||
answer = rails.confirm(message, element);
|
||||
} catch (error) {}
|
||||
callback = fire(element, "confirm:complete", [ answer ]);
|
||||
}
|
||||
return answer && callback;
|
||||
};
|
||||
|
||||
const handleDisabledElement = function(e) {
|
||||
const element = this;
|
||||
if (element.disabled) {
|
||||
stopEverything(e);
|
||||
}
|
||||
};
|
||||
|
||||
const enableElement = e => {
|
||||
let element;
|
||||
if (e instanceof Event) {
|
||||
if (isXhrRedirect(e)) {
|
||||
return;
|
||||
}
|
||||
element = e.target;
|
||||
} else {
|
||||
element = e;
|
||||
}
|
||||
if (matches(element, linkDisableSelector)) {
|
||||
return enableLinkElement(element);
|
||||
} else if (matches(element, buttonDisableSelector) || matches(element, formEnableSelector)) {
|
||||
return enableFormElement(element);
|
||||
} else if (matches(element, formSubmitSelector)) {
|
||||
return enableFormElements(element);
|
||||
}
|
||||
};
|
||||
|
||||
const disableElement = e => {
|
||||
const element = e instanceof Event ? e.target : e;
|
||||
if (matches(element, linkDisableSelector)) {
|
||||
return disableLinkElement(element);
|
||||
} else if (matches(element, buttonDisableSelector) || matches(element, formDisableSelector)) {
|
||||
return disableFormElement(element);
|
||||
} else if (matches(element, formSubmitSelector)) {
|
||||
return disableFormElements(element);
|
||||
}
|
||||
};
|
||||
|
||||
var disableLinkElement = function(element) {
|
||||
if (getData(element, "ujs:disabled")) {
|
||||
return;
|
||||
}
|
||||
const replacement = element.getAttribute("data-disable-with");
|
||||
if (replacement != null) {
|
||||
setData(element, "ujs:enable-with", element.innerHTML);
|
||||
element.innerHTML = replacement;
|
||||
}
|
||||
element.addEventListener("click", stopEverything);
|
||||
return setData(element, "ujs:disabled", true);
|
||||
};
|
||||
|
||||
var enableLinkElement = function(element) {
|
||||
const originalText = getData(element, "ujs:enable-with");
|
||||
if (originalText != null) {
|
||||
element.innerHTML = originalText;
|
||||
setData(element, "ujs:enable-with", null);
|
||||
}
|
||||
element.removeEventListener("click", stopEverything);
|
||||
return setData(element, "ujs:disabled", null);
|
||||
};
|
||||
|
||||
var disableFormElements = form => formElements(form, formDisableSelector).forEach(disableFormElement);
|
||||
|
||||
var disableFormElement = function(element) {
|
||||
if (getData(element, "ujs:disabled")) {
|
||||
return;
|
||||
}
|
||||
const replacement = element.getAttribute("data-disable-with");
|
||||
if (replacement != null) {
|
||||
if (matches(element, "button")) {
|
||||
setData(element, "ujs:enable-with", element.innerHTML);
|
||||
element.innerHTML = replacement;
|
||||
} else {
|
||||
setData(element, "ujs:enable-with", element.value);
|
||||
element.value = replacement;
|
||||
}
|
||||
}
|
||||
element.disabled = true;
|
||||
return setData(element, "ujs:disabled", true);
|
||||
};
|
||||
|
||||
var enableFormElements = form => formElements(form, formEnableSelector).forEach((element => enableFormElement(element)));
|
||||
|
||||
var enableFormElement = function(element) {
|
||||
const originalText = getData(element, "ujs:enable-with");
|
||||
if (originalText != null) {
|
||||
if (matches(element, "button")) {
|
||||
element.innerHTML = originalText;
|
||||
} else {
|
||||
element.value = originalText;
|
||||
}
|
||||
setData(element, "ujs:enable-with", null);
|
||||
}
|
||||
element.disabled = false;
|
||||
return setData(element, "ujs:disabled", null);
|
||||
};
|
||||
|
||||
var isXhrRedirect = function(event) {
|
||||
const xhr = event.detail ? event.detail[0] : undefined;
|
||||
return xhr && xhr.getResponseHeader("X-Xhr-Redirect");
|
||||
};
|
||||
|
||||
const handleMethodWithRails = rails => function(e) {
|
||||
const link = this;
|
||||
const method = link.getAttribute("data-method");
|
||||
if (!method) {
|
||||
return;
|
||||
}
|
||||
const href = rails.href(link);
|
||||
const csrfToken$1 = csrfToken();
|
||||
const csrfParam$1 = csrfParam();
|
||||
const form = document.createElement("form");
|
||||
let formContent = `<input name='_method' value='${method}' type='hidden' />`;
|
||||
if (csrfParam$1 && csrfToken$1 && !isCrossDomain(href)) {
|
||||
formContent += `<input name='${csrfParam$1}' value='${csrfToken$1}' type='hidden' />`;
|
||||
}
|
||||
formContent += '<input type="submit" />';
|
||||
form.method = "post";
|
||||
form.action = href;
|
||||
form.target = link.target;
|
||||
form.innerHTML = formContent;
|
||||
form.style.display = "none";
|
||||
document.body.appendChild(form);
|
||||
form.querySelector('[type="submit"]').click();
|
||||
stopEverything(e);
|
||||
};
|
||||
|
||||
const isRemote = function(element) {
|
||||
const value = element.getAttribute("data-remote");
|
||||
return value != null && value !== "false";
|
||||
};
|
||||
|
||||
const handleRemoteWithRails = rails => function(e) {
|
||||
let data, method, url;
|
||||
const element = this;
|
||||
if (!isRemote(element)) {
|
||||
return true;
|
||||
}
|
||||
if (!fire(element, "ajax:before")) {
|
||||
fire(element, "ajax:stopped");
|
||||
return false;
|
||||
}
|
||||
const withCredentials = element.getAttribute("data-with-credentials");
|
||||
const dataType = element.getAttribute("data-type") || "script";
|
||||
if (matches(element, formSubmitSelector)) {
|
||||
const button = getData(element, "ujs:submit-button");
|
||||
method = getData(element, "ujs:submit-button-formmethod") || element.getAttribute("method") || "get";
|
||||
url = getData(element, "ujs:submit-button-formaction") || element.getAttribute("action") || location.href;
|
||||
if (method.toUpperCase() === "GET") {
|
||||
url = url.replace(/\?.*$/, "");
|
||||
}
|
||||
if (element.enctype === "multipart/form-data") {
|
||||
data = new FormData(element);
|
||||
if (button != null) {
|
||||
data.append(button.name, button.value);
|
||||
}
|
||||
} else {
|
||||
data = serializeElement(element, button);
|
||||
}
|
||||
setData(element, "ujs:submit-button", null);
|
||||
setData(element, "ujs:submit-button-formmethod", null);
|
||||
setData(element, "ujs:submit-button-formaction", null);
|
||||
} else if (matches(element, buttonClickSelector) || matches(element, inputChangeSelector)) {
|
||||
method = element.getAttribute("data-method");
|
||||
url = element.getAttribute("data-url");
|
||||
data = serializeElement(element, element.getAttribute("data-params"));
|
||||
} else {
|
||||
method = element.getAttribute("data-method");
|
||||
url = rails.href(element);
|
||||
data = element.getAttribute("data-params");
|
||||
}
|
||||
ajax({
|
||||
type: method || "GET",
|
||||
url: url,
|
||||
data: data,
|
||||
dataType: dataType,
|
||||
beforeSend(xhr, options) {
|
||||
if (fire(element, "ajax:beforeSend", [ xhr, options ])) {
|
||||
return fire(element, "ajax:send", [ xhr ]);
|
||||
} else {
|
||||
fire(element, "ajax:stopped");
|
||||
return false;
|
||||
}
|
||||
},
|
||||
success(...args) {
|
||||
return fire(element, "ajax:success", args);
|
||||
},
|
||||
error(...args) {
|
||||
return fire(element, "ajax:error", args);
|
||||
},
|
||||
complete(...args) {
|
||||
return fire(element, "ajax:complete", args);
|
||||
},
|
||||
crossDomain: isCrossDomain(url),
|
||||
withCredentials: withCredentials != null && withCredentials !== "false"
|
||||
});
|
||||
stopEverything(e);
|
||||
};
|
||||
|
||||
const formSubmitButtonClick = function(e) {
|
||||
const button = this;
|
||||
const {form: form} = button;
|
||||
if (!form) {
|
||||
return;
|
||||
}
|
||||
if (button.name) {
|
||||
setData(form, "ujs:submit-button", {
|
||||
name: button.name,
|
||||
value: button.value
|
||||
});
|
||||
}
|
||||
setData(form, "ujs:formnovalidate-button", button.formNoValidate);
|
||||
setData(form, "ujs:submit-button-formaction", button.getAttribute("formaction"));
|
||||
return setData(form, "ujs:submit-button-formmethod", button.getAttribute("formmethod"));
|
||||
};
|
||||
|
||||
const preventInsignificantClick = function(e) {
|
||||
const link = this;
|
||||
const method = (link.getAttribute("data-method") || "GET").toUpperCase();
|
||||
const data = link.getAttribute("data-params");
|
||||
const metaClick = e.metaKey || e.ctrlKey;
|
||||
const insignificantMetaClick = metaClick && method === "GET" && !data;
|
||||
const nonPrimaryMouseClick = e.button != null && e.button !== 0;
|
||||
if (nonPrimaryMouseClick || insignificantMetaClick) {
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
};
|
||||
|
||||
const Rails = {
|
||||
$: $,
|
||||
ajax: ajax,
|
||||
buttonClickSelector: buttonClickSelector,
|
||||
buttonDisableSelector: buttonDisableSelector,
|
||||
confirm: confirm,
|
||||
cspNonce: cspNonce,
|
||||
csrfToken: csrfToken,
|
||||
csrfParam: csrfParam,
|
||||
CSRFProtection: CSRFProtection,
|
||||
delegate: delegate,
|
||||
disableElement: disableElement,
|
||||
enableElement: enableElement,
|
||||
fileInputSelector: fileInputSelector,
|
||||
fire: fire,
|
||||
formElements: formElements,
|
||||
formEnableSelector: formEnableSelector,
|
||||
formDisableSelector: formDisableSelector,
|
||||
formInputClickSelector: formInputClickSelector,
|
||||
formSubmitButtonClick: formSubmitButtonClick,
|
||||
formSubmitSelector: formSubmitSelector,
|
||||
getData: getData,
|
||||
handleDisabledElement: handleDisabledElement,
|
||||
href: href,
|
||||
inputChangeSelector: inputChangeSelector,
|
||||
isCrossDomain: isCrossDomain,
|
||||
linkClickSelector: linkClickSelector,
|
||||
linkDisableSelector: linkDisableSelector,
|
||||
loadCSPNonce: loadCSPNonce,
|
||||
matches: matches,
|
||||
preventInsignificantClick: preventInsignificantClick,
|
||||
refreshCSRFTokens: refreshCSRFTokens,
|
||||
serializeElement: serializeElement,
|
||||
setData: setData,
|
||||
stopEverything: stopEverything
|
||||
};
|
||||
|
||||
const handleConfirm = handleConfirmWithRails(Rails);
|
||||
|
||||
Rails.handleConfirm = handleConfirm;
|
||||
|
||||
const handleMethod = handleMethodWithRails(Rails);
|
||||
|
||||
Rails.handleMethod = handleMethod;
|
||||
|
||||
const handleRemote = handleRemoteWithRails(Rails);
|
||||
|
||||
Rails.handleRemote = handleRemote;
|
||||
|
||||
const start = function() {
|
||||
if (window._rails_loaded) {
|
||||
throw new Error("rails-ujs has already been loaded!");
|
||||
}
|
||||
window.addEventListener("pageshow", (function() {
|
||||
$(formEnableSelector).forEach((function(el) {
|
||||
if (getData(el, "ujs:disabled")) {
|
||||
enableElement(el);
|
||||
}
|
||||
}));
|
||||
$(linkDisableSelector).forEach((function(el) {
|
||||
if (getData(el, "ujs:disabled")) {
|
||||
enableElement(el);
|
||||
}
|
||||
}));
|
||||
}));
|
||||
delegate(document, linkDisableSelector, "ajax:complete", enableElement);
|
||||
delegate(document, linkDisableSelector, "ajax:stopped", enableElement);
|
||||
delegate(document, buttonDisableSelector, "ajax:complete", enableElement);
|
||||
delegate(document, buttonDisableSelector, "ajax:stopped", enableElement);
|
||||
delegate(document, linkClickSelector, "click", preventInsignificantClick);
|
||||
delegate(document, linkClickSelector, "click", handleDisabledElement);
|
||||
delegate(document, linkClickSelector, "click", handleConfirm);
|
||||
delegate(document, linkClickSelector, "click", disableElement);
|
||||
delegate(document, linkClickSelector, "click", handleRemote);
|
||||
delegate(document, linkClickSelector, "click", handleMethod);
|
||||
delegate(document, buttonClickSelector, "click", preventInsignificantClick);
|
||||
delegate(document, buttonClickSelector, "click", handleDisabledElement);
|
||||
delegate(document, buttonClickSelector, "click", handleConfirm);
|
||||
delegate(document, buttonClickSelector, "click", disableElement);
|
||||
delegate(document, buttonClickSelector, "click", handleRemote);
|
||||
delegate(document, inputChangeSelector, "change", handleDisabledElement);
|
||||
delegate(document, inputChangeSelector, "change", handleConfirm);
|
||||
delegate(document, inputChangeSelector, "change", handleRemote);
|
||||
delegate(document, formSubmitSelector, "submit", handleDisabledElement);
|
||||
delegate(document, formSubmitSelector, "submit", handleConfirm);
|
||||
delegate(document, formSubmitSelector, "submit", handleRemote);
|
||||
delegate(document, formSubmitSelector, "submit", (e => setTimeout((() => disableElement(e)), 13)));
|
||||
delegate(document, formSubmitSelector, "ajax:send", disableElement);
|
||||
delegate(document, formSubmitSelector, "ajax:complete", enableElement);
|
||||
delegate(document, formInputClickSelector, "click", preventInsignificantClick);
|
||||
delegate(document, formInputClickSelector, "click", handleDisabledElement);
|
||||
delegate(document, formInputClickSelector, "click", handleConfirm);
|
||||
delegate(document, formInputClickSelector, "click", formSubmitButtonClick);
|
||||
document.addEventListener("DOMContentLoaded", refreshCSRFTokens);
|
||||
document.addEventListener("DOMContentLoaded", loadCSPNonce);
|
||||
return window._rails_loaded = true;
|
||||
};
|
||||
|
||||
Rails.start = start;
|
||||
|
||||
if (typeof jQuery !== "undefined" && jQuery && jQuery.ajax) {
|
||||
if (jQuery.rails) {
|
||||
throw new Error("If you load both jquery_ujs and rails-ujs, use rails-ujs only.");
|
||||
}
|
||||
jQuery.rails = Rails;
|
||||
jQuery.ajaxPrefilter((function(options, originalOptions, xhr) {
|
||||
if (!options.crossDomain) {
|
||||
return CSRFProtection(xhr);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
if (typeof exports !== "object" && typeof module === "undefined") {
|
||||
window.Rails = Rails;
|
||||
if (fire(document, "rails:attachBindings")) {
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
export default Rails;
|
|
@ -0,0 +1,606 @@
|
|||
/*
|
||||
Unobtrusive JavaScript
|
||||
https://github.com/rails/rails/blob/main/actionview/app/javascript
|
||||
Released under the MIT license
|
||||
*/
|
||||
(function(global, factory) {
|
||||
typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() : typeof define === "function" && define.amd ? define(factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self,
|
||||
global.Rails = factory());
|
||||
})(this, (function() {
|
||||
"use strict";
|
||||
const linkClickSelector = "a[data-confirm], a[data-method], a[data-remote]:not([disabled]), a[data-disable-with], a[data-disable]";
|
||||
const buttonClickSelector = {
|
||||
selector: "button[data-remote]:not([form]), button[data-confirm]:not([form])",
|
||||
exclude: "form button"
|
||||
};
|
||||
const inputChangeSelector = "select[data-remote], input[data-remote], textarea[data-remote]";
|
||||
const formSubmitSelector = "form:not([data-turbo=true])";
|
||||
const formInputClickSelector = "form:not([data-turbo=true]) input[type=submit], form:not([data-turbo=true]) input[type=image], form:not([data-turbo=true]) button[type=submit], form:not([data-turbo=true]) button:not([type]), input[type=submit][form], input[type=image][form], button[type=submit][form], button[form]:not([type])";
|
||||
const formDisableSelector = "input[data-disable-with]:enabled, button[data-disable-with]:enabled, textarea[data-disable-with]:enabled, input[data-disable]:enabled, button[data-disable]:enabled, textarea[data-disable]:enabled";
|
||||
const formEnableSelector = "input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled, input[data-disable]:disabled, button[data-disable]:disabled, textarea[data-disable]:disabled";
|
||||
const fileInputSelector = "input[name][type=file]:not([disabled])";
|
||||
const linkDisableSelector = "a[data-disable-with], a[data-disable]";
|
||||
const buttonDisableSelector = "button[data-remote][data-disable-with], button[data-remote][data-disable]";
|
||||
let nonce = null;
|
||||
const loadCSPNonce = () => {
|
||||
const metaTag = document.querySelector("meta[name=csp-nonce]");
|
||||
return nonce = metaTag && metaTag.content;
|
||||
};
|
||||
const cspNonce = () => nonce || loadCSPNonce();
|
||||
const m = Element.prototype.matches || Element.prototype.matchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.webkitMatchesSelector;
|
||||
const matches = function(element, selector) {
|
||||
if (selector.exclude) {
|
||||
return m.call(element, selector.selector) && !m.call(element, selector.exclude);
|
||||
} else {
|
||||
return m.call(element, selector);
|
||||
}
|
||||
};
|
||||
const EXPANDO = "_ujsData";
|
||||
const getData = (element, key) => element[EXPANDO] ? element[EXPANDO][key] : undefined;
|
||||
const setData = function(element, key, value) {
|
||||
if (!element[EXPANDO]) {
|
||||
element[EXPANDO] = {};
|
||||
}
|
||||
return element[EXPANDO][key] = value;
|
||||
};
|
||||
const $ = selector => Array.prototype.slice.call(document.querySelectorAll(selector));
|
||||
const csrfToken = () => {
|
||||
const meta = document.querySelector("meta[name=csrf-token]");
|
||||
return meta && meta.content;
|
||||
};
|
||||
const csrfParam = () => {
|
||||
const meta = document.querySelector("meta[name=csrf-param]");
|
||||
return meta && meta.content;
|
||||
};
|
||||
const CSRFProtection = xhr => {
|
||||
const token = csrfToken();
|
||||
if (token) {
|
||||
return xhr.setRequestHeader("X-CSRF-Token", token);
|
||||
}
|
||||
};
|
||||
const refreshCSRFTokens = () => {
|
||||
const token = csrfToken();
|
||||
const param = csrfParam();
|
||||
if (token && param) {
|
||||
return $('form input[name="' + param + '"]').forEach((input => input.value = token));
|
||||
}
|
||||
};
|
||||
const AcceptHeaders = {
|
||||
"*": "*/*",
|
||||
text: "text/plain",
|
||||
html: "text/html",
|
||||
xml: "application/xml, text/xml",
|
||||
json: "application/json, text/javascript",
|
||||
script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
|
||||
};
|
||||
const ajax = options => {
|
||||
options = prepareOptions(options);
|
||||
var xhr = createXHR(options, (function() {
|
||||
const response = processResponse(xhr.response != null ? xhr.response : xhr.responseText, xhr.getResponseHeader("Content-Type"));
|
||||
if (Math.floor(xhr.status / 100) === 2) {
|
||||
if (typeof options.success === "function") {
|
||||
options.success(response, xhr.statusText, xhr);
|
||||
}
|
||||
} else {
|
||||
if (typeof options.error === "function") {
|
||||
options.error(response, xhr.statusText, xhr);
|
||||
}
|
||||
}
|
||||
return typeof options.complete === "function" ? options.complete(xhr, xhr.statusText) : undefined;
|
||||
}));
|
||||
if (options.beforeSend && !options.beforeSend(xhr, options)) {
|
||||
return false;
|
||||
}
|
||||
if (xhr.readyState === XMLHttpRequest.OPENED) {
|
||||
return xhr.send(options.data);
|
||||
}
|
||||
};
|
||||
var prepareOptions = function(options) {
|
||||
options.url = options.url || location.href;
|
||||
options.type = options.type.toUpperCase();
|
||||
if (options.type === "GET" && options.data) {
|
||||
if (options.url.indexOf("?") < 0) {
|
||||
options.url += "?" + options.data;
|
||||
} else {
|
||||
options.url += "&" + options.data;
|
||||
}
|
||||
}
|
||||
if (!(options.dataType in AcceptHeaders)) {
|
||||
options.dataType = "*";
|
||||
}
|
||||
options.accept = AcceptHeaders[options.dataType];
|
||||
if (options.dataType !== "*") {
|
||||
options.accept += ", */*; q=0.01";
|
||||
}
|
||||
return options;
|
||||
};
|
||||
var createXHR = function(options, done) {
|
||||
const xhr = new XMLHttpRequest;
|
||||
xhr.open(options.type, options.url, true);
|
||||
xhr.setRequestHeader("Accept", options.accept);
|
||||
if (typeof options.data === "string") {
|
||||
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
|
||||
}
|
||||
if (!options.crossDomain) {
|
||||
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
||||
CSRFProtection(xhr);
|
||||
}
|
||||
xhr.withCredentials = !!options.withCredentials;
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||
return done(xhr);
|
||||
}
|
||||
};
|
||||
return xhr;
|
||||
};
|
||||
var processResponse = function(response, type) {
|
||||
if (typeof response === "string" && typeof type === "string") {
|
||||
if (type.match(/\bjson\b/)) {
|
||||
try {
|
||||
response = JSON.parse(response);
|
||||
} catch (error) {}
|
||||
} else if (type.match(/\b(?:java|ecma)script\b/)) {
|
||||
const script = document.createElement("script");
|
||||
script.setAttribute("nonce", cspNonce());
|
||||
script.text = response;
|
||||
document.head.appendChild(script).parentNode.removeChild(script);
|
||||
} else if (type.match(/\b(xml|html|svg)\b/)) {
|
||||
const parser = new DOMParser;
|
||||
type = type.replace(/;.+/, "");
|
||||
try {
|
||||
response = parser.parseFromString(response, type);
|
||||
} catch (error1) {}
|
||||
}
|
||||
}
|
||||
return response;
|
||||
};
|
||||
const href = element => element.href;
|
||||
const isCrossDomain = function(url) {
|
||||
const originAnchor = document.createElement("a");
|
||||
originAnchor.href = location.href;
|
||||
const urlAnchor = document.createElement("a");
|
||||
try {
|
||||
urlAnchor.href = url;
|
||||
return !((!urlAnchor.protocol || urlAnchor.protocol === ":") && !urlAnchor.host || originAnchor.protocol + "//" + originAnchor.host === urlAnchor.protocol + "//" + urlAnchor.host);
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
let preventDefault;
|
||||
let {CustomEvent: CustomEvent} = window;
|
||||
if (typeof CustomEvent !== "function") {
|
||||
CustomEvent = function(event, params) {
|
||||
const evt = document.createEvent("CustomEvent");
|
||||
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
|
||||
return evt;
|
||||
};
|
||||
CustomEvent.prototype = window.Event.prototype;
|
||||
({preventDefault: preventDefault} = CustomEvent.prototype);
|
||||
CustomEvent.prototype.preventDefault = function() {
|
||||
const result = preventDefault.call(this);
|
||||
if (this.cancelable && !this.defaultPrevented) {
|
||||
Object.defineProperty(this, "defaultPrevented", {
|
||||
get() {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
const fire = (obj, name, data) => {
|
||||
const event = new CustomEvent(name, {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
detail: data
|
||||
});
|
||||
obj.dispatchEvent(event);
|
||||
return !event.defaultPrevented;
|
||||
};
|
||||
const stopEverything = e => {
|
||||
fire(e.target, "ujs:everythingStopped");
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
};
|
||||
const delegate = (element, selector, eventType, handler) => element.addEventListener(eventType, (function(e) {
|
||||
let {target: target} = e;
|
||||
while (!!(target instanceof Element) && !matches(target, selector)) {
|
||||
target = target.parentNode;
|
||||
}
|
||||
if (target instanceof Element && handler.call(target, e) === false) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}));
|
||||
const toArray = e => Array.prototype.slice.call(e);
|
||||
const serializeElement = (element, additionalParam) => {
|
||||
let inputs = [ element ];
|
||||
if (matches(element, "form")) {
|
||||
inputs = toArray(element.elements);
|
||||
}
|
||||
const params = [];
|
||||
inputs.forEach((function(input) {
|
||||
if (!input.name || input.disabled) {
|
||||
return;
|
||||
}
|
||||
if (matches(input, "fieldset[disabled] *")) {
|
||||
return;
|
||||
}
|
||||
if (matches(input, "select")) {
|
||||
toArray(input.options).forEach((function(option) {
|
||||
if (option.selected) {
|
||||
params.push({
|
||||
name: input.name,
|
||||
value: option.value
|
||||
});
|
||||
}
|
||||
}));
|
||||
} else if (input.checked || [ "radio", "checkbox", "submit" ].indexOf(input.type) === -1) {
|
||||
params.push({
|
||||
name: input.name,
|
||||
value: input.value
|
||||
});
|
||||
}
|
||||
}));
|
||||
if (additionalParam) {
|
||||
params.push(additionalParam);
|
||||
}
|
||||
return params.map((function(param) {
|
||||
if (param.name) {
|
||||
return `${encodeURIComponent(param.name)}=${encodeURIComponent(param.value)}`;
|
||||
} else {
|
||||
return param;
|
||||
}
|
||||
})).join("&");
|
||||
};
|
||||
const formElements = (form, selector) => {
|
||||
if (matches(form, "form")) {
|
||||
return toArray(form.elements).filter((el => matches(el, selector)));
|
||||
} else {
|
||||
return toArray(form.querySelectorAll(selector));
|
||||
}
|
||||
};
|
||||
const handleConfirmWithRails = rails => function(e) {
|
||||
if (!allowAction(this, rails)) {
|
||||
stopEverything(e);
|
||||
}
|
||||
};
|
||||
const confirm = (message, element) => window.confirm(message);
|
||||
var allowAction = function(element, rails) {
|
||||
let callback;
|
||||
const message = element.getAttribute("data-confirm");
|
||||
if (!message) {
|
||||
return true;
|
||||
}
|
||||
let answer = false;
|
||||
if (fire(element, "confirm")) {
|
||||
try {
|
||||
answer = rails.confirm(message, element);
|
||||
} catch (error) {}
|
||||
callback = fire(element, "confirm:complete", [ answer ]);
|
||||
}
|
||||
return answer && callback;
|
||||
};
|
||||
const handleDisabledElement = function(e) {
|
||||
const element = this;
|
||||
if (element.disabled) {
|
||||
stopEverything(e);
|
||||
}
|
||||
};
|
||||
const enableElement = e => {
|
||||
let element;
|
||||
if (e instanceof Event) {
|
||||
if (isXhrRedirect(e)) {
|
||||
return;
|
||||
}
|
||||
element = e.target;
|
||||
} else {
|
||||
element = e;
|
||||
}
|
||||
if (matches(element, linkDisableSelector)) {
|
||||
return enableLinkElement(element);
|
||||
} else if (matches(element, buttonDisableSelector) || matches(element, formEnableSelector)) {
|
||||
return enableFormElement(element);
|
||||
} else if (matches(element, formSubmitSelector)) {
|
||||
return enableFormElements(element);
|
||||
}
|
||||
};
|
||||
const disableElement = e => {
|
||||
const element = e instanceof Event ? e.target : e;
|
||||
if (matches(element, linkDisableSelector)) {
|
||||
return disableLinkElement(element);
|
||||
} else if (matches(element, buttonDisableSelector) || matches(element, formDisableSelector)) {
|
||||
return disableFormElement(element);
|
||||
} else if (matches(element, formSubmitSelector)) {
|
||||
return disableFormElements(element);
|
||||
}
|
||||
};
|
||||
var disableLinkElement = function(element) {
|
||||
if (getData(element, "ujs:disabled")) {
|
||||
return;
|
||||
}
|
||||
const replacement = element.getAttribute("data-disable-with");
|
||||
if (replacement != null) {
|
||||
setData(element, "ujs:enable-with", element.innerHTML);
|
||||
element.innerHTML = replacement;
|
||||
}
|
||||
element.addEventListener("click", stopEverything);
|
||||
return setData(element, "ujs:disabled", true);
|
||||
};
|
||||
var enableLinkElement = function(element) {
|
||||
const originalText = getData(element, "ujs:enable-with");
|
||||
if (originalText != null) {
|
||||
element.innerHTML = originalText;
|
||||
setData(element, "ujs:enable-with", null);
|
||||
}
|
||||
element.removeEventListener("click", stopEverything);
|
||||
return setData(element, "ujs:disabled", null);
|
||||
};
|
||||
var disableFormElements = form => formElements(form, formDisableSelector).forEach(disableFormElement);
|
||||
var disableFormElement = function(element) {
|
||||
if (getData(element, "ujs:disabled")) {
|
||||
return;
|
||||
}
|
||||
const replacement = element.getAttribute("data-disable-with");
|
||||
if (replacement != null) {
|
||||
if (matches(element, "button")) {
|
||||
setData(element, "ujs:enable-with", element.innerHTML);
|
||||
element.innerHTML = replacement;
|
||||
} else {
|
||||
setData(element, "ujs:enable-with", element.value);
|
||||
element.value = replacement;
|
||||
}
|
||||
}
|
||||
element.disabled = true;
|
||||
return setData(element, "ujs:disabled", true);
|
||||
};
|
||||
var enableFormElements = form => formElements(form, formEnableSelector).forEach((element => enableFormElement(element)));
|
||||
var enableFormElement = function(element) {
|
||||
const originalText = getData(element, "ujs:enable-with");
|
||||
if (originalText != null) {
|
||||
if (matches(element, "button")) {
|
||||
element.innerHTML = originalText;
|
||||
} else {
|
||||
element.value = originalText;
|
||||
}
|
||||
setData(element, "ujs:enable-with", null);
|
||||
}
|
||||
element.disabled = false;
|
||||
return setData(element, "ujs:disabled", null);
|
||||
};
|
||||
var isXhrRedirect = function(event) {
|
||||
const xhr = event.detail ? event.detail[0] : undefined;
|
||||
return xhr && xhr.getResponseHeader("X-Xhr-Redirect");
|
||||
};
|
||||
const handleMethodWithRails = rails => function(e) {
|
||||
const link = this;
|
||||
const method = link.getAttribute("data-method");
|
||||
if (!method) {
|
||||
return;
|
||||
}
|
||||
const href = rails.href(link);
|
||||
const csrfToken$1 = csrfToken();
|
||||
const csrfParam$1 = csrfParam();
|
||||
const form = document.createElement("form");
|
||||
let formContent = `<input name='_method' value='${method}' type='hidden' />`;
|
||||
if (csrfParam$1 && csrfToken$1 && !isCrossDomain(href)) {
|
||||
formContent += `<input name='${csrfParam$1}' value='${csrfToken$1}' type='hidden' />`;
|
||||
}
|
||||
formContent += '<input type="submit" />';
|
||||
form.method = "post";
|
||||
form.action = href;
|
||||
form.target = link.target;
|
||||
form.innerHTML = formContent;
|
||||
form.style.display = "none";
|
||||
document.body.appendChild(form);
|
||||
form.querySelector('[type="submit"]').click();
|
||||
stopEverything(e);
|
||||
};
|
||||
const isRemote = function(element) {
|
||||
const value = element.getAttribute("data-remote");
|
||||
return value != null && value !== "false";
|
||||
};
|
||||
const handleRemoteWithRails = rails => function(e) {
|
||||
let data, method, url;
|
||||
const element = this;
|
||||
if (!isRemote(element)) {
|
||||
return true;
|
||||
}
|
||||
if (!fire(element, "ajax:before")) {
|
||||
fire(element, "ajax:stopped");
|
||||
return false;
|
||||
}
|
||||
const withCredentials = element.getAttribute("data-with-credentials");
|
||||
const dataType = element.getAttribute("data-type") || "script";
|
||||
if (matches(element, formSubmitSelector)) {
|
||||
const button = getData(element, "ujs:submit-button");
|
||||
method = getData(element, "ujs:submit-button-formmethod") || element.getAttribute("method") || "get";
|
||||
url = getData(element, "ujs:submit-button-formaction") || element.getAttribute("action") || location.href;
|
||||
if (method.toUpperCase() === "GET") {
|
||||
url = url.replace(/\?.*$/, "");
|
||||
}
|
||||
if (element.enctype === "multipart/form-data") {
|
||||
data = new FormData(element);
|
||||
if (button != null) {
|
||||
data.append(button.name, button.value);
|
||||
}
|
||||
} else {
|
||||
data = serializeElement(element, button);
|
||||
}
|
||||
setData(element, "ujs:submit-button", null);
|
||||
setData(element, "ujs:submit-button-formmethod", null);
|
||||
setData(element, "ujs:submit-button-formaction", null);
|
||||
} else if (matches(element, buttonClickSelector) || matches(element, inputChangeSelector)) {
|
||||
method = element.getAttribute("data-method");
|
||||
url = element.getAttribute("data-url");
|
||||
data = serializeElement(element, element.getAttribute("data-params"));
|
||||
} else {
|
||||
method = element.getAttribute("data-method");
|
||||
url = rails.href(element);
|
||||
data = element.getAttribute("data-params");
|
||||
}
|
||||
ajax({
|
||||
type: method || "GET",
|
||||
url: url,
|
||||
data: data,
|
||||
dataType: dataType,
|
||||
beforeSend(xhr, options) {
|
||||
if (fire(element, "ajax:beforeSend", [ xhr, options ])) {
|
||||
return fire(element, "ajax:send", [ xhr ]);
|
||||
} else {
|
||||
fire(element, "ajax:stopped");
|
||||
return false;
|
||||
}
|
||||
},
|
||||
success(...args) {
|
||||
return fire(element, "ajax:success", args);
|
||||
},
|
||||
error(...args) {
|
||||
return fire(element, "ajax:error", args);
|
||||
},
|
||||
complete(...args) {
|
||||
return fire(element, "ajax:complete", args);
|
||||
},
|
||||
crossDomain: isCrossDomain(url),
|
||||
withCredentials: withCredentials != null && withCredentials !== "false"
|
||||
});
|
||||
stopEverything(e);
|
||||
};
|
||||
const formSubmitButtonClick = function(e) {
|
||||
const button = this;
|
||||
const {form: form} = button;
|
||||
if (!form) {
|
||||
return;
|
||||
}
|
||||
if (button.name) {
|
||||
setData(form, "ujs:submit-button", {
|
||||
name: button.name,
|
||||
value: button.value
|
||||
});
|
||||
}
|
||||
setData(form, "ujs:formnovalidate-button", button.formNoValidate);
|
||||
setData(form, "ujs:submit-button-formaction", button.getAttribute("formaction"));
|
||||
return setData(form, "ujs:submit-button-formmethod", button.getAttribute("formmethod"));
|
||||
};
|
||||
const preventInsignificantClick = function(e) {
|
||||
const link = this;
|
||||
const method = (link.getAttribute("data-method") || "GET").toUpperCase();
|
||||
const data = link.getAttribute("data-params");
|
||||
const metaClick = e.metaKey || e.ctrlKey;
|
||||
const insignificantMetaClick = metaClick && method === "GET" && !data;
|
||||
const nonPrimaryMouseClick = e.button != null && e.button !== 0;
|
||||
if (nonPrimaryMouseClick || insignificantMetaClick) {
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
};
|
||||
const Rails = {
|
||||
$: $,
|
||||
ajax: ajax,
|
||||
buttonClickSelector: buttonClickSelector,
|
||||
buttonDisableSelector: buttonDisableSelector,
|
||||
confirm: confirm,
|
||||
cspNonce: cspNonce,
|
||||
csrfToken: csrfToken,
|
||||
csrfParam: csrfParam,
|
||||
CSRFProtection: CSRFProtection,
|
||||
delegate: delegate,
|
||||
disableElement: disableElement,
|
||||
enableElement: enableElement,
|
||||
fileInputSelector: fileInputSelector,
|
||||
fire: fire,
|
||||
formElements: formElements,
|
||||
formEnableSelector: formEnableSelector,
|
||||
formDisableSelector: formDisableSelector,
|
||||
formInputClickSelector: formInputClickSelector,
|
||||
formSubmitButtonClick: formSubmitButtonClick,
|
||||
formSubmitSelector: formSubmitSelector,
|
||||
getData: getData,
|
||||
handleDisabledElement: handleDisabledElement,
|
||||
href: href,
|
||||
inputChangeSelector: inputChangeSelector,
|
||||
isCrossDomain: isCrossDomain,
|
||||
linkClickSelector: linkClickSelector,
|
||||
linkDisableSelector: linkDisableSelector,
|
||||
loadCSPNonce: loadCSPNonce,
|
||||
matches: matches,
|
||||
preventInsignificantClick: preventInsignificantClick,
|
||||
refreshCSRFTokens: refreshCSRFTokens,
|
||||
serializeElement: serializeElement,
|
||||
setData: setData,
|
||||
stopEverything: stopEverything
|
||||
};
|
||||
const handleConfirm = handleConfirmWithRails(Rails);
|
||||
Rails.handleConfirm = handleConfirm;
|
||||
const handleMethod = handleMethodWithRails(Rails);
|
||||
Rails.handleMethod = handleMethod;
|
||||
const handleRemote = handleRemoteWithRails(Rails);
|
||||
Rails.handleRemote = handleRemote;
|
||||
const start = function() {
|
||||
if (window._rails_loaded) {
|
||||
throw new Error("rails-ujs has already been loaded!");
|
||||
}
|
||||
window.addEventListener("pageshow", (function() {
|
||||
$(formEnableSelector).forEach((function(el) {
|
||||
if (getData(el, "ujs:disabled")) {
|
||||
enableElement(el);
|
||||
}
|
||||
}));
|
||||
$(linkDisableSelector).forEach((function(el) {
|
||||
if (getData(el, "ujs:disabled")) {
|
||||
enableElement(el);
|
||||
}
|
||||
}));
|
||||
}));
|
||||
delegate(document, linkDisableSelector, "ajax:complete", enableElement);
|
||||
delegate(document, linkDisableSelector, "ajax:stopped", enableElement);
|
||||
delegate(document, buttonDisableSelector, "ajax:complete", enableElement);
|
||||
delegate(document, buttonDisableSelector, "ajax:stopped", enableElement);
|
||||
delegate(document, linkClickSelector, "click", preventInsignificantClick);
|
||||
delegate(document, linkClickSelector, "click", handleDisabledElement);
|
||||
delegate(document, linkClickSelector, "click", handleConfirm);
|
||||
delegate(document, linkClickSelector, "click", disableElement);
|
||||
delegate(document, linkClickSelector, "click", handleRemote);
|
||||
delegate(document, linkClickSelector, "click", handleMethod);
|
||||
delegate(document, buttonClickSelector, "click", preventInsignificantClick);
|
||||
delegate(document, buttonClickSelector, "click", handleDisabledElement);
|
||||
delegate(document, buttonClickSelector, "click", handleConfirm);
|
||||
delegate(document, buttonClickSelector, "click", disableElement);
|
||||
delegate(document, buttonClickSelector, "click", handleRemote);
|
||||
delegate(document, inputChangeSelector, "change", handleDisabledElement);
|
||||
delegate(document, inputChangeSelector, "change", handleConfirm);
|
||||
delegate(document, inputChangeSelector, "change", handleRemote);
|
||||
delegate(document, formSubmitSelector, "submit", handleDisabledElement);
|
||||
delegate(document, formSubmitSelector, "submit", handleConfirm);
|
||||
delegate(document, formSubmitSelector, "submit", handleRemote);
|
||||
delegate(document, formSubmitSelector, "submit", (e => setTimeout((() => disableElement(e)), 13)));
|
||||
delegate(document, formSubmitSelector, "ajax:send", disableElement);
|
||||
delegate(document, formSubmitSelector, "ajax:complete", enableElement);
|
||||
delegate(document, formInputClickSelector, "click", preventInsignificantClick);
|
||||
delegate(document, formInputClickSelector, "click", handleDisabledElement);
|
||||
delegate(document, formInputClickSelector, "click", handleConfirm);
|
||||
delegate(document, formInputClickSelector, "click", formSubmitButtonClick);
|
||||
document.addEventListener("DOMContentLoaded", refreshCSRFTokens);
|
||||
document.addEventListener("DOMContentLoaded", loadCSPNonce);
|
||||
return window._rails_loaded = true;
|
||||
};
|
||||
Rails.start = start;
|
||||
if (typeof jQuery !== "undefined" && jQuery && jQuery.ajax) {
|
||||
if (jQuery.rails) {
|
||||
throw new Error("If you load both jquery_ujs and rails-ujs, use rails-ujs only.");
|
||||
}
|
||||
jQuery.rails = Rails;
|
||||
jQuery.ajaxPrefilter((function(options, originalOptions, xhr) {
|
||||
if (!options.crossDomain) {
|
||||
return CSRFProtection(xhr);
|
||||
}
|
||||
}));
|
||||
}
|
||||
if (typeof exports !== "object" && typeof module === "undefined") {
|
||||
window.Rails = Rails;
|
||||
if (fire(document, "rails:attachBindings")) {
|
||||
start();
|
||||
}
|
||||
}
|
||||
return Rails;
|
||||
}));
|
|
@ -1,5 +0,0 @@
|
|||
/*
|
||||
Unobtrusive JavaScript
|
||||
https://github.com/rails/rails/blob/main/actionview/app/assets/javascripts
|
||||
Released under the MIT license
|
||||
*/
|
|
@ -1,30 +0,0 @@
|
|||
#= require_tree ../utils
|
||||
|
||||
{ fire, stopEverything } = Rails
|
||||
|
||||
Rails.handleConfirm = (e) ->
|
||||
stopEverything(e) unless allowAction(this)
|
||||
|
||||
# Default confirm dialog, may be overridden with custom confirm dialog in Rails.confirm
|
||||
Rails.confirm = (message, element) ->
|
||||
confirm(message)
|
||||
|
||||
# For 'data-confirm' attribute:
|
||||
# - Fires `confirm` event
|
||||
# - Shows the confirmation dialog
|
||||
# - Fires the `confirm:complete` event
|
||||
#
|
||||
# Returns `true` if no function stops the chain and user chose yes `false` otherwise.
|
||||
# Attaching a handler to the element's `confirm` event that returns a `falsy` value cancels the confirmation dialog.
|
||||
# Attaching a handler to the element's `confirm:complete` event that returns a `falsy` value makes this function
|
||||
# return false. The `confirm:complete` event is fired whether or not the user answered true or false to the dialog.
|
||||
allowAction = (element) ->
|
||||
message = element.getAttribute('data-confirm')
|
||||
return true unless message
|
||||
|
||||
answer = false
|
||||
if fire(element, 'confirm')
|
||||
try answer = Rails.confirm(message, element)
|
||||
callback = fire(element, 'confirm:complete', [answer])
|
||||
|
||||
answer and callback
|
|
@ -1,93 +0,0 @@
|
|||
#= require_tree ../utils
|
||||
|
||||
{ matches, getData, setData, stopEverything, formElements } = Rails
|
||||
|
||||
Rails.handleDisabledElement = (e) ->
|
||||
element = this
|
||||
stopEverything(e) if element.disabled
|
||||
|
||||
# Unified function to enable an element (link, button and form)
|
||||
Rails.enableElement = (e) ->
|
||||
if e instanceof Event
|
||||
return if isXhrRedirect(e)
|
||||
element = e.target
|
||||
else
|
||||
element = e
|
||||
|
||||
if matches(element, Rails.linkDisableSelector)
|
||||
enableLinkElement(element)
|
||||
else if matches(element, Rails.buttonDisableSelector) or matches(element, Rails.formEnableSelector)
|
||||
enableFormElement(element)
|
||||
else if matches(element, Rails.formSubmitSelector)
|
||||
enableFormElements(element)
|
||||
|
||||
# Unified function to disable an element (link, button and form)
|
||||
Rails.disableElement = (e) ->
|
||||
element = if e instanceof Event then e.target else e
|
||||
if matches(element, Rails.linkDisableSelector)
|
||||
disableLinkElement(element)
|
||||
else if matches(element, Rails.buttonDisableSelector) or matches(element, Rails.formDisableSelector)
|
||||
disableFormElement(element)
|
||||
else if matches(element, Rails.formSubmitSelector)
|
||||
disableFormElements(element)
|
||||
|
||||
# Replace element's html with the 'data-disable-with' after storing original html
|
||||
# and prevent clicking on it
|
||||
disableLinkElement = (element) ->
|
||||
return if getData(element, 'ujs:disabled')
|
||||
replacement = element.getAttribute('data-disable-with')
|
||||
if replacement?
|
||||
setData(element, 'ujs:enable-with', element.innerHTML) # store enabled state
|
||||
element.innerHTML = replacement
|
||||
element.addEventListener('click', stopEverything) # prevent further clicking
|
||||
setData(element, 'ujs:disabled', true)
|
||||
|
||||
# Restore element to its original state which was disabled by 'disableLinkElement' above
|
||||
enableLinkElement = (element) ->
|
||||
originalText = getData(element, 'ujs:enable-with')
|
||||
if originalText?
|
||||
element.innerHTML = originalText # set to old enabled state
|
||||
setData(element, 'ujs:enable-with', null) # clean up cache
|
||||
element.removeEventListener('click', stopEverything) # enable element
|
||||
setData(element, 'ujs:disabled', null)
|
||||
|
||||
# Disables form elements:
|
||||
# - Caches element value in 'ujs:enable-with' data store
|
||||
# - Replaces element text with value of 'data-disable-with' attribute
|
||||
# - Sets disabled property to true
|
||||
disableFormElements = (form) ->
|
||||
formElements(form, Rails.formDisableSelector).forEach(disableFormElement)
|
||||
|
||||
disableFormElement = (element) ->
|
||||
return if getData(element, 'ujs:disabled')
|
||||
replacement = element.getAttribute('data-disable-with')
|
||||
if replacement?
|
||||
if matches(element, 'button')
|
||||
setData(element, 'ujs:enable-with', element.innerHTML)
|
||||
element.innerHTML = replacement
|
||||
else
|
||||
setData(element, 'ujs:enable-with', element.value)
|
||||
element.value = replacement
|
||||
element.disabled = true
|
||||
setData(element, 'ujs:disabled', true)
|
||||
|
||||
# Re-enables disabled form elements:
|
||||
# - Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`)
|
||||
# - Sets disabled property to false
|
||||
enableFormElements = (form) ->
|
||||
formElements(form, Rails.formEnableSelector).forEach(enableFormElement)
|
||||
|
||||
enableFormElement = (element) ->
|
||||
originalText = getData(element, 'ujs:enable-with')
|
||||
if originalText?
|
||||
if matches(element, 'button')
|
||||
element.innerHTML = originalText
|
||||
else
|
||||
element.value = originalText
|
||||
setData(element, 'ujs:enable-with', null) # clean up cache
|
||||
element.disabled = false
|
||||
setData(element, 'ujs:disabled', null)
|
||||
|
||||
isXhrRedirect = (event) ->
|
||||
xhr = event.detail?[0]
|
||||
xhr?.getResponseHeader("X-Xhr-Redirect")?
|
|
@ -1,34 +0,0 @@
|
|||
#= require_tree ../utils
|
||||
|
||||
{ stopEverything } = Rails
|
||||
|
||||
# Handles "data-method" on links such as:
|
||||
# <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a>
|
||||
Rails.handleMethod = (e) ->
|
||||
link = this
|
||||
method = link.getAttribute('data-method')
|
||||
return unless method
|
||||
|
||||
href = Rails.href(link)
|
||||
csrfToken = Rails.csrfToken()
|
||||
csrfParam = Rails.csrfParam()
|
||||
form = document.createElement('form')
|
||||
formContent = "<input name='_method' value='#{method}' type='hidden' />"
|
||||
|
||||
if csrfParam? and csrfToken? and not Rails.isCrossDomain(href)
|
||||
formContent += "<input name='#{csrfParam}' value='#{csrfToken}' type='hidden' />"
|
||||
|
||||
# Must trigger submit by click on a button, else "submit" event handler won't work!
|
||||
# https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit
|
||||
formContent += '<input type="submit" />'
|
||||
|
||||
form.method = 'post'
|
||||
form.action = href
|
||||
form.target = link.target
|
||||
form.innerHTML = formContent
|
||||
form.style.display = 'none'
|
||||
|
||||
document.body.appendChild(form)
|
||||
form.querySelector('[type="submit"]').click()
|
||||
|
||||
stopEverything(e)
|
|
@ -1,93 +0,0 @@
|
|||
#= require_tree ../utils
|
||||
|
||||
{
|
||||
matches, getData, setData
|
||||
fire, stopEverything
|
||||
ajax, isCrossDomain
|
||||
serializeElement
|
||||
} = Rails
|
||||
|
||||
# Checks "data-remote" if true to handle the request through a XHR request.
|
||||
isRemote = (element) ->
|
||||
value = element.getAttribute('data-remote')
|
||||
value? and value isnt 'false'
|
||||
|
||||
# Submits "remote" forms and links with ajax
|
||||
Rails.handleRemote = (e) ->
|
||||
element = this
|
||||
|
||||
return true unless isRemote(element)
|
||||
unless fire(element, 'ajax:before')
|
||||
fire(element, 'ajax:stopped')
|
||||
return false
|
||||
|
||||
withCredentials = element.getAttribute('data-with-credentials')
|
||||
dataType = element.getAttribute('data-type') or 'script'
|
||||
|
||||
if matches(element, Rails.formSubmitSelector)
|
||||
# memoized value from clicked submit button
|
||||
button = getData(element, 'ujs:submit-button')
|
||||
method = getData(element, 'ujs:submit-button-formmethod') or element.getAttribute('method') or "get"
|
||||
url = getData(element, 'ujs:submit-button-formaction') or element.getAttribute('action') or location.href
|
||||
|
||||
# strip query string if it's a GET request
|
||||
url = url.replace(/\?.*$/, '') if method.toUpperCase() is 'GET'
|
||||
|
||||
if element.enctype is 'multipart/form-data'
|
||||
data = new FormData(element)
|
||||
data.append(button.name, button.value) if button?
|
||||
else
|
||||
data = serializeElement(element, button)
|
||||
|
||||
setData(element, 'ujs:submit-button', null)
|
||||
setData(element, 'ujs:submit-button-formmethod', null)
|
||||
setData(element, 'ujs:submit-button-formaction', null)
|
||||
else if matches(element, Rails.buttonClickSelector) or matches(element, Rails.inputChangeSelector)
|
||||
method = element.getAttribute('data-method')
|
||||
url = element.getAttribute('data-url')
|
||||
data = serializeElement(element, element.getAttribute('data-params'))
|
||||
else
|
||||
method = element.getAttribute('data-method')
|
||||
url = Rails.href(element)
|
||||
data = element.getAttribute('data-params')
|
||||
|
||||
ajax(
|
||||
type: method or 'GET'
|
||||
url: url
|
||||
data: data
|
||||
dataType: dataType
|
||||
# stopping the "ajax:beforeSend" event will cancel the ajax request
|
||||
beforeSend: (xhr, options) ->
|
||||
if fire(element, 'ajax:beforeSend', [xhr, options])
|
||||
fire(element, 'ajax:send', [xhr])
|
||||
else
|
||||
fire(element, 'ajax:stopped')
|
||||
return false
|
||||
success: (args...) -> fire(element, 'ajax:success', args)
|
||||
error: (args...) -> fire(element, 'ajax:error', args)
|
||||
complete: (args...) -> fire(element, 'ajax:complete', args)
|
||||
crossDomain: isCrossDomain(url)
|
||||
withCredentials: withCredentials? and withCredentials isnt 'false'
|
||||
)
|
||||
stopEverything(e)
|
||||
|
||||
Rails.formSubmitButtonClick = (e) ->
|
||||
button = this
|
||||
form = button.form
|
||||
return unless form
|
||||
# Register the pressed submit button
|
||||
setData(form, 'ujs:submit-button', name: button.name, value: button.value) if button.name
|
||||
# Save attributes from button
|
||||
setData(form, 'ujs:formnovalidate-button', button.formNoValidate)
|
||||
setData(form, 'ujs:submit-button-formaction', button.getAttribute('formaction'))
|
||||
setData(form, 'ujs:submit-button-formmethod', button.getAttribute('formmethod'))
|
||||
|
||||
Rails.preventInsignificantClick = (e) ->
|
||||
link = this
|
||||
method = (link.getAttribute('data-method') or 'GET').toUpperCase()
|
||||
data = link.getAttribute('data-params')
|
||||
metaClick = e.metaKey or e.ctrlKey
|
||||
insignificantMetaClick = metaClick and method is 'GET' and not data
|
||||
nonPrimaryMouseClick = e.button? and e.button isnt 0
|
||||
e.stopImmediatePropagation() if nonPrimaryMouseClick or insignificantMetaClick
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
{
|
||||
fire, delegate
|
||||
getData, $
|
||||
refreshCSRFTokens, CSRFProtection
|
||||
loadCSPNonce
|
||||
enableElement, disableElement, handleDisabledElement
|
||||
handleConfirm, preventInsignificantClick
|
||||
handleRemote, formSubmitButtonClick,
|
||||
handleMethod
|
||||
} = Rails
|
||||
|
||||
# For backward compatibility
|
||||
if jQuery? and jQuery.ajax?
|
||||
throw new Error('If you load both jquery_ujs and rails-ujs, use rails-ujs only.') if jQuery.rails
|
||||
jQuery.rails = Rails
|
||||
jQuery.ajaxPrefilter (options, originalOptions, xhr) ->
|
||||
CSRFProtection(xhr) unless options.crossDomain
|
||||
|
||||
Rails.start = ->
|
||||
# Cut down on the number of issues from people inadvertently including
|
||||
# rails-ujs twice by detecting and raising an error when it happens.
|
||||
throw new Error('rails-ujs has already been loaded!') if window._rails_loaded
|
||||
|
||||
# This event works the same as the load event, except that it fires every
|
||||
# time the page is loaded.
|
||||
# See https://github.com/rails/jquery-ujs/issues/357
|
||||
# See https://developer.mozilla.org/en-US/docs/Using_Firefox_1.5_caching
|
||||
window.addEventListener 'pageshow', ->
|
||||
$(Rails.formEnableSelector).forEach (el) ->
|
||||
enableElement(el) if getData(el, 'ujs:disabled')
|
||||
$(Rails.linkDisableSelector).forEach (el) ->
|
||||
enableElement(el) if getData(el, 'ujs:disabled')
|
||||
|
||||
delegate document, Rails.linkDisableSelector, 'ajax:complete', enableElement
|
||||
delegate document, Rails.linkDisableSelector, 'ajax:stopped', enableElement
|
||||
delegate document, Rails.buttonDisableSelector, 'ajax:complete', enableElement
|
||||
delegate document, Rails.buttonDisableSelector, 'ajax:stopped', enableElement
|
||||
|
||||
delegate document, Rails.linkClickSelector, 'click', preventInsignificantClick
|
||||
delegate document, Rails.linkClickSelector, 'click', handleDisabledElement
|
||||
delegate document, Rails.linkClickSelector, 'click', handleConfirm
|
||||
delegate document, Rails.linkClickSelector, 'click', disableElement
|
||||
delegate document, Rails.linkClickSelector, 'click', handleRemote
|
||||
delegate document, Rails.linkClickSelector, 'click', handleMethod
|
||||
|
||||
delegate document, Rails.buttonClickSelector, 'click', preventInsignificantClick
|
||||
delegate document, Rails.buttonClickSelector, 'click', handleDisabledElement
|
||||
delegate document, Rails.buttonClickSelector, 'click', handleConfirm
|
||||
delegate document, Rails.buttonClickSelector, 'click', disableElement
|
||||
delegate document, Rails.buttonClickSelector, 'click', handleRemote
|
||||
|
||||
delegate document, Rails.inputChangeSelector, 'change', handleDisabledElement
|
||||
delegate document, Rails.inputChangeSelector, 'change', handleConfirm
|
||||
delegate document, Rails.inputChangeSelector, 'change', handleRemote
|
||||
|
||||
delegate document, Rails.formSubmitSelector, 'submit', handleDisabledElement
|
||||
delegate document, Rails.formSubmitSelector, 'submit', handleConfirm
|
||||
delegate document, Rails.formSubmitSelector, 'submit', handleRemote
|
||||
# Normal mode submit
|
||||
# Slight timeout so that the submit button gets properly serialized
|
||||
delegate document, Rails.formSubmitSelector, 'submit', (e) -> setTimeout((-> disableElement(e)), 13)
|
||||
delegate document, Rails.formSubmitSelector, 'ajax:send', disableElement
|
||||
delegate document, Rails.formSubmitSelector, 'ajax:complete', enableElement
|
||||
|
||||
delegate document, Rails.formInputClickSelector, 'click', preventInsignificantClick
|
||||
delegate document, Rails.formInputClickSelector, 'click', handleDisabledElement
|
||||
delegate document, Rails.formInputClickSelector, 'click', handleConfirm
|
||||
delegate document, Rails.formInputClickSelector, 'click', formSubmitButtonClick
|
||||
|
||||
document.addEventListener('DOMContentLoaded', refreshCSRFTokens)
|
||||
document.addEventListener('DOMContentLoaded', loadCSPNonce)
|
||||
window._rails_loaded = true
|
||||
|
||||
if window.Rails is Rails and fire(document, 'rails:attachBindings')
|
||||
Rails.start()
|
|
@ -1,98 +0,0 @@
|
|||
#= require ./csp
|
||||
#= require ./csrf
|
||||
#= require ./event
|
||||
|
||||
{ cspNonce, CSRFProtection, fire } = Rails
|
||||
|
||||
AcceptHeaders =
|
||||
'*': '*/*'
|
||||
text: 'text/plain'
|
||||
html: 'text/html'
|
||||
xml: 'application/xml, text/xml'
|
||||
json: 'application/json, text/javascript'
|
||||
script: 'text/javascript, application/javascript, application/ecmascript, application/x-ecmascript'
|
||||
|
||||
Rails.ajax = (options) ->
|
||||
options = prepareOptions(options)
|
||||
xhr = createXHR options, ->
|
||||
response = processResponse(xhr.response ? xhr.responseText, xhr.getResponseHeader('Content-Type'))
|
||||
if xhr.status // 100 == 2
|
||||
options.success?(response, xhr.statusText, xhr)
|
||||
else
|
||||
options.error?(response, xhr.statusText, xhr)
|
||||
options.complete?(xhr, xhr.statusText)
|
||||
|
||||
if options.beforeSend? && !options.beforeSend(xhr, options)
|
||||
return false
|
||||
|
||||
if xhr.readyState is XMLHttpRequest.OPENED
|
||||
xhr.send(options.data)
|
||||
|
||||
prepareOptions = (options) ->
|
||||
options.url = options.url or location.href
|
||||
options.type = options.type.toUpperCase()
|
||||
# append data to url if it's a GET request
|
||||
if options.type is 'GET' and options.data
|
||||
if options.url.indexOf('?') < 0
|
||||
options.url += '?' + options.data
|
||||
else
|
||||
options.url += '&' + options.data
|
||||
# Use "*" as default dataType
|
||||
options.dataType = '*' unless AcceptHeaders[options.dataType]?
|
||||
options.accept = AcceptHeaders[options.dataType]
|
||||
options.accept += ', */*; q=0.01' if options.dataType isnt '*'
|
||||
options
|
||||
|
||||
createXHR = (options, done) ->
|
||||
xhr = new XMLHttpRequest()
|
||||
# Open and set up xhr
|
||||
xhr.open(options.type, options.url, true)
|
||||
xhr.setRequestHeader('Accept', options.accept)
|
||||
# Set Content-Type only when sending a string
|
||||
# Sending FormData will automatically set Content-Type to multipart/form-data
|
||||
if typeof options.data is 'string'
|
||||
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8')
|
||||
unless options.crossDomain
|
||||
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
|
||||
# Add X-CSRF-Token
|
||||
CSRFProtection(xhr)
|
||||
xhr.withCredentials = !!options.withCredentials
|
||||
xhr.onreadystatechange = ->
|
||||
done(xhr) if xhr.readyState is XMLHttpRequest.DONE
|
||||
xhr
|
||||
|
||||
processResponse = (response, type) ->
|
||||
if typeof response is 'string' and typeof type is 'string'
|
||||
if type.match(/\bjson\b/)
|
||||
try response = JSON.parse(response)
|
||||
else if type.match(/\b(?:java|ecma)script\b/)
|
||||
script = document.createElement('script')
|
||||
script.setAttribute('nonce', cspNonce())
|
||||
script.text = response
|
||||
document.head.appendChild(script).parentNode.removeChild(script)
|
||||
else if type.match(/\b(xml|html|svg)\b/)
|
||||
parser = new DOMParser()
|
||||
type = type.replace(/;.+/, '') # remove something like ';charset=utf-8'
|
||||
try response = parser.parseFromString(response, type)
|
||||
response
|
||||
|
||||
# Default way to get an element's href. May be overridden at Rails.href.
|
||||
Rails.href = (element) -> element.href
|
||||
|
||||
# Determines if the request is a cross domain request.
|
||||
Rails.isCrossDomain = (url) ->
|
||||
originAnchor = document.createElement('a')
|
||||
originAnchor.href = location.href
|
||||
urlAnchor = document.createElement('a')
|
||||
try
|
||||
urlAnchor.href = url
|
||||
# If URL protocol is false or is a string containing a single colon
|
||||
# *and* host are false, assume it is not a cross-domain request
|
||||
# (should only be the case for IE7 and IE compatibility mode).
|
||||
# Otherwise, evaluate protocol and host of the URL against the origin
|
||||
# protocol and host.
|
||||
!(((!urlAnchor.protocol || urlAnchor.protocol == ':') && !urlAnchor.host) ||
|
||||
(originAnchor.protocol + '//' + originAnchor.host == urlAnchor.protocol + '//' + urlAnchor.host))
|
||||
catch e
|
||||
# If there is an error parsing the URL, assume it is crossDomain.
|
||||
true
|
|
@ -1,8 +0,0 @@
|
|||
nonce = null
|
||||
|
||||
Rails.loadCSPNonce = ->
|
||||
nonce = document.querySelector("meta[name=csp-nonce]")?.content
|
||||
|
||||
# Returns the Content-Security-Policy nonce for inline scripts.
|
||||
Rails.cspNonce = ->
|
||||
nonce ? Rails.loadCSPNonce()
|
|
@ -1,25 +0,0 @@
|
|||
#= require ./dom
|
||||
|
||||
{ $ } = Rails
|
||||
|
||||
# Up-to-date Cross-Site Request Forgery token
|
||||
csrfToken = Rails.csrfToken = ->
|
||||
meta = document.querySelector('meta[name=csrf-token]')
|
||||
meta and meta.content
|
||||
|
||||
# URL param that must contain the CSRF token
|
||||
csrfParam = Rails.csrfParam = ->
|
||||
meta = document.querySelector('meta[name=csrf-param]')
|
||||
meta and meta.content
|
||||
|
||||
# Make sure that every Ajax request sends the CSRF token
|
||||
Rails.CSRFProtection = (xhr) ->
|
||||
token = csrfToken()
|
||||
xhr.setRequestHeader('X-CSRF-Token', token) if token?
|
||||
|
||||
# Make sure that all forms have actual up-to-date tokens (cached forms contain old ones)
|
||||
Rails.refreshCSRFTokens = ->
|
||||
token = csrfToken()
|
||||
param = csrfParam()
|
||||
if token? and param?
|
||||
$('form input[name="' + param + '"]').forEach (input) -> input.value = token
|
|
@ -1,35 +0,0 @@
|
|||
m = Element.prototype.matches or
|
||||
Element.prototype.matchesSelector or
|
||||
Element.prototype.mozMatchesSelector or
|
||||
Element.prototype.msMatchesSelector or
|
||||
Element.prototype.oMatchesSelector or
|
||||
Element.prototype.webkitMatchesSelector
|
||||
|
||||
# Checks if the given native dom element matches the selector
|
||||
# element::
|
||||
# native DOM element
|
||||
# selector::
|
||||
# CSS selector string or
|
||||
# a JavaScript object with `selector` and `exclude` properties
|
||||
# Examples: "form", { selector: "form", exclude: "form[data-remote='true']"}
|
||||
Rails.matches = (element, selector) ->
|
||||
if selector.exclude?
|
||||
m.call(element, selector.selector) and not m.call(element, selector.exclude)
|
||||
else
|
||||
m.call(element, selector)
|
||||
|
||||
# get and set data on a given element using "expando properties"
|
||||
# See: https://developer.mozilla.org/en-US/docs/Glossary/Expando
|
||||
expando = '_ujsData'
|
||||
|
||||
Rails.getData = (element, key) ->
|
||||
element[expando]?[key]
|
||||
|
||||
Rails.setData = (element, key, value) ->
|
||||
element[expando] ?= {}
|
||||
element[expando][key] = value
|
||||
|
||||
# a wrapper for document.querySelectorAll
|
||||
# returns an Array
|
||||
Rails.$ = (selector) ->
|
||||
Array.prototype.slice.call(document.querySelectorAll(selector))
|
|
@ -1,68 +0,0 @@
|
|||
#= require ./dom
|
||||
|
||||
{ matches } = Rails
|
||||
|
||||
# Polyfill for CustomEvent in IE9+
|
||||
# https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
|
||||
CustomEvent = window.CustomEvent
|
||||
|
||||
if typeof CustomEvent isnt 'function'
|
||||
CustomEvent = (event, params) ->
|
||||
evt = document.createEvent('CustomEvent')
|
||||
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail)
|
||||
evt
|
||||
|
||||
CustomEvent.prototype = window.Event.prototype
|
||||
|
||||
# Fix setting `defaultPrevented` when `preventDefault()` is called
|
||||
# http://stackoverflow.com/questions/23349191/event-preventdefault-is-not-working-in-ie-11-for-custom-events
|
||||
{ preventDefault } = CustomEvent.prototype
|
||||
CustomEvent.prototype.preventDefault = ->
|
||||
result = preventDefault.call(this)
|
||||
if @cancelable and not @defaultPrevented
|
||||
Object.defineProperty(this, 'defaultPrevented', get: -> true)
|
||||
result
|
||||
|
||||
# Triggers a custom event on an element and returns false if the event result is false
|
||||
# obj::
|
||||
# a native DOM element
|
||||
# name::
|
||||
# string that corresponds to the event you want to trigger
|
||||
# e.g. 'click', 'submit'
|
||||
# data::
|
||||
# data you want to pass when you dispatch an event
|
||||
fire = Rails.fire = (obj, name, data) ->
|
||||
event = new CustomEvent(
|
||||
name,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
detail: data,
|
||||
)
|
||||
obj.dispatchEvent(event)
|
||||
!event.defaultPrevented
|
||||
|
||||
# Helper function, needed to provide consistent behavior in IE
|
||||
Rails.stopEverything = (e) ->
|
||||
fire(e.target, 'ujs:everythingStopped')
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
e.stopImmediatePropagation()
|
||||
|
||||
# Delegates events
|
||||
# to a specified parent `element`, which fires event `handler`
|
||||
# for the specified `selector` when an event of `eventType` is triggered
|
||||
# element::
|
||||
# parent element that will listen for events e.g. document
|
||||
# selector::
|
||||
# CSS selector; or an object that has `selector` and `exclude` properties (see: Rails.matches)
|
||||
# eventType::
|
||||
# string representing the event e.g. 'submit', 'click'
|
||||
# handler::
|
||||
# the event handler to be called
|
||||
Rails.delegate = (element, selector, eventType, handler) ->
|
||||
element.addEventListener eventType, (e) ->
|
||||
target = e.target
|
||||
target = target.parentNode until not (target instanceof Element) or matches(target, selector)
|
||||
if target instanceof Element and handler.call(target, e) == false
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
|
@ -1,37 +0,0 @@
|
|||
#= require ./dom
|
||||
|
||||
{ matches } = Rails
|
||||
|
||||
toArray = (e) -> Array.prototype.slice.call(e)
|
||||
|
||||
Rails.serializeElement = (element, additionalParam) ->
|
||||
inputs = [element]
|
||||
inputs = toArray(element.elements) if matches(element, 'form')
|
||||
params = []
|
||||
|
||||
inputs.forEach (input) ->
|
||||
return if !input.name || input.disabled
|
||||
return if matches(input, 'fieldset[disabled] *')
|
||||
if matches(input, 'select')
|
||||
toArray(input.options).forEach (option) ->
|
||||
params.push(name: input.name, value: option.value) if option.selected
|
||||
else if input.checked or ['radio', 'checkbox', 'submit'].indexOf(input.type) == -1
|
||||
params.push(name: input.name, value: input.value)
|
||||
|
||||
params.push(additionalParam) if additionalParam
|
||||
|
||||
params.map (param) ->
|
||||
if param.name?
|
||||
"#{encodeURIComponent(param.name)}=#{encodeURIComponent(param.value)}"
|
||||
else
|
||||
param
|
||||
.join('&')
|
||||
|
||||
# Helper function that returns form elements that match the specified CSS selector
|
||||
# If form is actually a "form" element this will return associated elements outside the from that have
|
||||
# the html form attribute set
|
||||
Rails.formElements = (form, selector) ->
|
||||
if matches(form, 'form')
|
||||
toArray(form.elements).filter (el) -> matches(el, selector)
|
||||
else
|
||||
toArray(form.querySelectorAll(selector))
|
|
@ -0,0 +1,33 @@
|
|||
import { fire, stopEverything } from "../utils/event"
|
||||
|
||||
const handleConfirmWithRails = (rails) => function(e) {
|
||||
if (!allowAction(this, rails)) { stopEverything(e) }
|
||||
}
|
||||
|
||||
// Default confirm dialog, may be overridden with custom confirm dialog in Rails.confirm
|
||||
const confirm = (message, element) => window.confirm(message)
|
||||
|
||||
// For 'data-confirm' attribute:
|
||||
// - Fires `confirm` event
|
||||
// - Shows the confirmation dialog
|
||||
// - Fires the `confirm:complete` event
|
||||
//
|
||||
// Returns `true` if no function stops the chain and user chose yes `false` otherwise.
|
||||
// Attaching a handler to the element's `confirm` event that returns a `falsy` value cancels the confirmation dialog.
|
||||
// Attaching a handler to the element's `confirm:complete` event that returns a `falsy` value makes this function
|
||||
// return false. The `confirm:complete` event is fired whether or not the user answered true or false to the dialog.
|
||||
var allowAction = function(element, rails) {
|
||||
let callback
|
||||
const message = element.getAttribute("data-confirm")
|
||||
if (!message) { return true }
|
||||
|
||||
let answer = false
|
||||
if (fire(element, "confirm")) {
|
||||
try { answer = rails.confirm(message, element) } catch(error) { /* do nothing */ }
|
||||
callback = fire(element, "confirm:complete", [answer])
|
||||
}
|
||||
|
||||
return answer && callback
|
||||
}
|
||||
|
||||
export { handleConfirmWithRails, confirm }
|
|
@ -0,0 +1,118 @@
|
|||
import {
|
||||
linkDisableSelector,
|
||||
buttonDisableSelector,
|
||||
formDisableSelector,
|
||||
formEnableSelector,
|
||||
formSubmitSelector
|
||||
} from "../utils/constants"
|
||||
import { matches, getData, setData } from "../utils/dom"
|
||||
import { stopEverything } from "../utils/event"
|
||||
import { formElements } from "../utils/form"
|
||||
|
||||
const handleDisabledElement = function(e) {
|
||||
const element = this
|
||||
if (element.disabled) { stopEverything(e) }
|
||||
}
|
||||
|
||||
// Unified function to enable an element (link, button and form)
|
||||
const enableElement = (e) => {
|
||||
let element
|
||||
if (e instanceof Event) {
|
||||
if (isXhrRedirect(e)) { return }
|
||||
element = e.target
|
||||
} else {
|
||||
element = e
|
||||
}
|
||||
|
||||
if (matches(element, linkDisableSelector)) {
|
||||
return enableLinkElement(element)
|
||||
} else if (matches(element, buttonDisableSelector) || matches(element, formEnableSelector)) {
|
||||
return enableFormElement(element)
|
||||
} else if (matches(element, formSubmitSelector)) {
|
||||
return enableFormElements(element)
|
||||
}
|
||||
}
|
||||
|
||||
// Unified function to disable an element (link, button and form)
|
||||
const disableElement = (e) => {
|
||||
const element = e instanceof Event ? e.target : e
|
||||
if (matches(element, linkDisableSelector)) {
|
||||
return disableLinkElement(element)
|
||||
} else if (matches(element, buttonDisableSelector) || matches(element, formDisableSelector)) {
|
||||
return disableFormElement(element)
|
||||
} else if (matches(element, formSubmitSelector)) {
|
||||
return disableFormElements(element)
|
||||
}
|
||||
}
|
||||
|
||||
// Replace element's html with the 'data-disable-with' after storing original html
|
||||
// and prevent clicking on it
|
||||
var disableLinkElement = function(element) {
|
||||
if (getData(element, "ujs:disabled")) { return }
|
||||
const replacement = element.getAttribute("data-disable-with")
|
||||
if (replacement != null) {
|
||||
setData(element, "ujs:enable-with", element.innerHTML) // store enabled state
|
||||
element.innerHTML = replacement
|
||||
}
|
||||
element.addEventListener("click", stopEverything) // prevent further clicking
|
||||
return setData(element, "ujs:disabled", true)
|
||||
}
|
||||
|
||||
// Restore element to its original state which was disabled by 'disableLinkElement' above
|
||||
var enableLinkElement = function(element) {
|
||||
const originalText = getData(element, "ujs:enable-with")
|
||||
if (originalText != null) {
|
||||
element.innerHTML = originalText // set to old enabled state
|
||||
setData(element, "ujs:enable-with", null) // clean up cache
|
||||
}
|
||||
element.removeEventListener("click", stopEverything) // enable element
|
||||
return setData(element, "ujs:disabled", null)
|
||||
}
|
||||
|
||||
// Disables form elements:
|
||||
// - Caches element value in 'ujs:enable-with' data store
|
||||
// - Replaces element text with value of 'data-disable-with' attribute
|
||||
// - Sets disabled property to true
|
||||
var disableFormElements = form => formElements(form, formDisableSelector).forEach(disableFormElement)
|
||||
|
||||
var disableFormElement = function(element) {
|
||||
if (getData(element, "ujs:disabled")) { return }
|
||||
const replacement = element.getAttribute("data-disable-with")
|
||||
if (replacement != null) {
|
||||
if (matches(element, "button")) {
|
||||
setData(element, "ujs:enable-with", element.innerHTML)
|
||||
element.innerHTML = replacement
|
||||
} else {
|
||||
setData(element, "ujs:enable-with", element.value)
|
||||
element.value = replacement
|
||||
}
|
||||
}
|
||||
element.disabled = true
|
||||
return setData(element, "ujs:disabled", true)
|
||||
}
|
||||
|
||||
// Re-enables disabled form elements:
|
||||
// - Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`)
|
||||
// - Sets disabled property to false
|
||||
var enableFormElements = form => formElements(form, formEnableSelector).forEach(element => enableFormElement(element))
|
||||
|
||||
var enableFormElement = function(element) {
|
||||
const originalText = getData(element, "ujs:enable-with")
|
||||
if (originalText != null) {
|
||||
if (matches(element, "button")) {
|
||||
element.innerHTML = originalText
|
||||
} else {
|
||||
element.value = originalText
|
||||
}
|
||||
setData(element, "ujs:enable-with", null) // clean up cache
|
||||
}
|
||||
element.disabled = false
|
||||
return setData(element, "ujs:disabled", null)
|
||||
}
|
||||
|
||||
var isXhrRedirect = function(event) {
|
||||
const xhr = event.detail ? event.detail[0] : undefined
|
||||
return xhr && xhr.getResponseHeader("X-Xhr-Redirect")
|
||||
}
|
||||
|
||||
export { handleDisabledElement, enableElement, disableElement }
|
|
@ -0,0 +1,38 @@
|
|||
import { isCrossDomain } from "../utils/ajax"
|
||||
import * as csrf from "../utils/csrf"
|
||||
import { stopEverything } from "../utils/event"
|
||||
|
||||
// Handles "data-method" on links such as:
|
||||
// <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a>
|
||||
const handleMethodWithRails = (rails) => function(e) {
|
||||
const link = this
|
||||
const method = link.getAttribute("data-method")
|
||||
if (!method) { return }
|
||||
|
||||
const href = rails.href(link)
|
||||
const csrfToken = csrf.csrfToken()
|
||||
const csrfParam = csrf.csrfParam()
|
||||
const form = document.createElement("form")
|
||||
let formContent = `<input name='_method' value='${method}' type='hidden' />`
|
||||
|
||||
if (csrfParam && csrfToken && !isCrossDomain(href)) {
|
||||
formContent += `<input name='${csrfParam}' value='${csrfToken}' type='hidden' />`
|
||||
}
|
||||
|
||||
// Must trigger submit by click on a button, else "submit" event handler won't work!
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit
|
||||
formContent += "<input type=\"submit\" />"
|
||||
|
||||
form.method = "post"
|
||||
form.action = href
|
||||
form.target = link.target
|
||||
form.innerHTML = formContent
|
||||
form.style.display = "none"
|
||||
|
||||
document.body.appendChild(form)
|
||||
form.querySelector("[type=\"submit\"]").click()
|
||||
|
||||
stopEverything(e)
|
||||
}
|
||||
|
||||
export { handleMethodWithRails }
|
|
@ -0,0 +1,103 @@
|
|||
import { formSubmitSelector, buttonClickSelector, inputChangeSelector } from "../utils/constants"
|
||||
import { ajax, isCrossDomain } from "../utils/ajax"
|
||||
import { matches, getData, setData } from "../utils/dom"
|
||||
import { fire, stopEverything } from "../utils/event"
|
||||
import { serializeElement } from "../utils/form"
|
||||
|
||||
// Checks "data-remote" if true to handle the request through a XHR request.
|
||||
const isRemote = function(element) {
|
||||
const value = element.getAttribute("data-remote")
|
||||
return (value != null) && (value !== "false")
|
||||
}
|
||||
|
||||
// Submits "remote" forms and links with ajax
|
||||
const handleRemoteWithRails = (rails) => function(e) {
|
||||
let data, method, url
|
||||
const element = this
|
||||
|
||||
if (!isRemote(element)) { return true }
|
||||
if (!fire(element, "ajax:before")) {
|
||||
fire(element, "ajax:stopped")
|
||||
return false
|
||||
}
|
||||
|
||||
const withCredentials = element.getAttribute("data-with-credentials")
|
||||
const dataType = element.getAttribute("data-type") || "script"
|
||||
|
||||
if (matches(element, formSubmitSelector)) {
|
||||
// memoized value from clicked submit button
|
||||
const button = getData(element, "ujs:submit-button")
|
||||
method = getData(element, "ujs:submit-button-formmethod") || element.getAttribute("method") || "get"
|
||||
url = getData(element, "ujs:submit-button-formaction") || element.getAttribute("action") || location.href
|
||||
|
||||
// strip query string if it's a GET request
|
||||
if (method.toUpperCase() === "GET") { url = url.replace(/\?.*$/, "") }
|
||||
|
||||
if (element.enctype === "multipart/form-data") {
|
||||
data = new FormData(element)
|
||||
if (button != null) { data.append(button.name, button.value) }
|
||||
} else {
|
||||
data = serializeElement(element, button)
|
||||
}
|
||||
|
||||
setData(element, "ujs:submit-button", null)
|
||||
setData(element, "ujs:submit-button-formmethod", null)
|
||||
setData(element, "ujs:submit-button-formaction", null)
|
||||
} else if (matches(element, buttonClickSelector) || matches(element, inputChangeSelector)) {
|
||||
method = element.getAttribute("data-method")
|
||||
url = element.getAttribute("data-url")
|
||||
data = serializeElement(element, element.getAttribute("data-params"))
|
||||
} else {
|
||||
method = element.getAttribute("data-method")
|
||||
url = rails.href(element)
|
||||
data = element.getAttribute("data-params")
|
||||
}
|
||||
|
||||
ajax({
|
||||
type: method || "GET",
|
||||
url,
|
||||
data,
|
||||
dataType,
|
||||
// stopping the "ajax:beforeSend" event will cancel the ajax request
|
||||
beforeSend(xhr, options) {
|
||||
if (fire(element, "ajax:beforeSend", [xhr, options])) {
|
||||
return fire(element, "ajax:send", [xhr])
|
||||
} else {
|
||||
fire(element, "ajax:stopped")
|
||||
return false
|
||||
}
|
||||
},
|
||||
success(...args) { return fire(element, "ajax:success", args) },
|
||||
error(...args) { return fire(element, "ajax:error", args) },
|
||||
complete(...args) { return fire(element, "ajax:complete", args) },
|
||||
crossDomain: isCrossDomain(url),
|
||||
withCredentials: (withCredentials != null) && (withCredentials !== "false")
|
||||
})
|
||||
stopEverything(e)
|
||||
}
|
||||
|
||||
const formSubmitButtonClick = function(e) {
|
||||
const button = this
|
||||
const {
|
||||
form
|
||||
} = button
|
||||
if (!form) { return }
|
||||
// Register the pressed submit button
|
||||
if (button.name) { setData(form, "ujs:submit-button", {name: button.name, value: button.value}) }
|
||||
// Save attributes from button
|
||||
setData(form, "ujs:formnovalidate-button", button.formNoValidate)
|
||||
setData(form, "ujs:submit-button-formaction", button.getAttribute("formaction"))
|
||||
return setData(form, "ujs:submit-button-formmethod", button.getAttribute("formmethod"))
|
||||
}
|
||||
|
||||
const preventInsignificantClick = function(e) {
|
||||
const link = this
|
||||
const method = (link.getAttribute("data-method") || "GET").toUpperCase()
|
||||
const data = link.getAttribute("data-params")
|
||||
const metaClick = e.metaKey || e.ctrlKey
|
||||
const insignificantMetaClick = metaClick && (method === "GET") && !data
|
||||
const nonPrimaryMouseClick = (e.button != null) && (e.button !== 0)
|
||||
if (nonPrimaryMouseClick || insignificantMetaClick) { e.stopImmediatePropagation() }
|
||||
}
|
||||
|
||||
export { handleRemoteWithRails, formSubmitButtonClick, preventInsignificantClick }
|
|
@ -0,0 +1,164 @@
|
|||
import {
|
||||
linkClickSelector,
|
||||
buttonClickSelector,
|
||||
inputChangeSelector,
|
||||
formSubmitSelector,
|
||||
formInputClickSelector,
|
||||
formDisableSelector,
|
||||
formEnableSelector,
|
||||
fileInputSelector,
|
||||
linkDisableSelector,
|
||||
buttonDisableSelector
|
||||
} from "./utils/constants"
|
||||
|
||||
import { ajax, href, isCrossDomain } from "./utils/ajax"
|
||||
import { cspNonce, loadCSPNonce } from "./utils/csp"
|
||||
import { csrfToken, csrfParam, CSRFProtection, refreshCSRFTokens } from "./utils/csrf"
|
||||
import { matches, getData, setData, $ } from "./utils/dom"
|
||||
import { fire, stopEverything, delegate } from "./utils/event"
|
||||
import { serializeElement, formElements } from "./utils/form"
|
||||
|
||||
import { confirm, handleConfirmWithRails } from "./features/confirm"
|
||||
import { handleDisabledElement, enableElement, disableElement } from "./features/disable"
|
||||
import { handleMethodWithRails } from "./features/method"
|
||||
import { handleRemoteWithRails, formSubmitButtonClick, preventInsignificantClick } from "./features/remote"
|
||||
|
||||
const Rails = {
|
||||
$,
|
||||
ajax,
|
||||
buttonClickSelector,
|
||||
buttonDisableSelector,
|
||||
confirm,
|
||||
cspNonce,
|
||||
csrfToken,
|
||||
csrfParam,
|
||||
CSRFProtection,
|
||||
delegate,
|
||||
disableElement,
|
||||
enableElement,
|
||||
fileInputSelector,
|
||||
fire,
|
||||
formElements,
|
||||
formEnableSelector,
|
||||
formDisableSelector,
|
||||
formInputClickSelector,
|
||||
formSubmitButtonClick,
|
||||
formSubmitSelector,
|
||||
getData,
|
||||
handleDisabledElement,
|
||||
href,
|
||||
inputChangeSelector,
|
||||
isCrossDomain,
|
||||
linkClickSelector,
|
||||
linkDisableSelector,
|
||||
loadCSPNonce,
|
||||
matches,
|
||||
preventInsignificantClick,
|
||||
refreshCSRFTokens,
|
||||
serializeElement,
|
||||
setData,
|
||||
stopEverything
|
||||
}
|
||||
|
||||
// needs to be able to call Rails.confirm in case its overridden
|
||||
const handleConfirm = handleConfirmWithRails(Rails)
|
||||
Rails.handleConfirm = handleConfirm
|
||||
|
||||
// needs to be able to call Rails.href in case its overridden
|
||||
const handleMethod = handleMethodWithRails(Rails)
|
||||
Rails.handleMethod = handleMethod
|
||||
|
||||
// needs to be able to call Rails.href in case its overridden
|
||||
const handleRemote = handleRemoteWithRails(Rails)
|
||||
Rails.handleRemote = handleRemote
|
||||
|
||||
const start = function() {
|
||||
// Cut down on the number of issues from people inadvertently including
|
||||
// rails-ujs twice by detecting and raising an error when it happens.
|
||||
if (window._rails_loaded) { throw new Error("rails-ujs has already been loaded!") }
|
||||
|
||||
// This event works the same as the load event, except that it fires every
|
||||
// time the page is loaded.
|
||||
// See https://github.com/rails/jquery-ujs/issues/357
|
||||
// See https://developer.mozilla.org/en-US/docs/Using_Firefox_1.5_caching
|
||||
window.addEventListener("pageshow", function() {
|
||||
$(formEnableSelector).forEach(function(el) {
|
||||
if (getData(el, "ujs:disabled")) {
|
||||
enableElement(el)
|
||||
}
|
||||
})
|
||||
$(linkDisableSelector).forEach(function(el) {
|
||||
if (getData(el, "ujs:disabled")) {
|
||||
enableElement(el)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
delegate(document, linkDisableSelector, "ajax:complete", enableElement)
|
||||
delegate(document, linkDisableSelector, "ajax:stopped", enableElement)
|
||||
delegate(document, buttonDisableSelector, "ajax:complete", enableElement)
|
||||
delegate(document, buttonDisableSelector, "ajax:stopped", enableElement)
|
||||
|
||||
delegate(document, linkClickSelector, "click", preventInsignificantClick)
|
||||
delegate(document, linkClickSelector, "click", handleDisabledElement)
|
||||
delegate(document, linkClickSelector, "click", handleConfirm)
|
||||
delegate(document, linkClickSelector, "click", disableElement)
|
||||
delegate(document, linkClickSelector, "click", handleRemote)
|
||||
delegate(document, linkClickSelector, "click", handleMethod)
|
||||
|
||||
delegate(document, buttonClickSelector, "click", preventInsignificantClick)
|
||||
delegate(document, buttonClickSelector, "click", handleDisabledElement)
|
||||
delegate(document, buttonClickSelector, "click", handleConfirm)
|
||||
delegate(document, buttonClickSelector, "click", disableElement)
|
||||
delegate(document, buttonClickSelector, "click", handleRemote)
|
||||
|
||||
delegate(document, inputChangeSelector, "change", handleDisabledElement)
|
||||
delegate(document, inputChangeSelector, "change", handleConfirm)
|
||||
delegate(document, inputChangeSelector, "change", handleRemote)
|
||||
|
||||
delegate(document, formSubmitSelector, "submit", handleDisabledElement)
|
||||
delegate(document, formSubmitSelector, "submit", handleConfirm)
|
||||
delegate(document, formSubmitSelector, "submit", handleRemote)
|
||||
// Normal mode submit
|
||||
// Slight timeout so that the submit button gets properly serialized
|
||||
delegate(document, formSubmitSelector, "submit", e => setTimeout((() => disableElement(e)), 13))
|
||||
delegate(document, formSubmitSelector, "ajax:send", disableElement)
|
||||
delegate(document, formSubmitSelector, "ajax:complete", enableElement)
|
||||
|
||||
delegate(document, formInputClickSelector, "click", preventInsignificantClick)
|
||||
delegate(document, formInputClickSelector, "click", handleDisabledElement)
|
||||
delegate(document, formInputClickSelector, "click", handleConfirm)
|
||||
delegate(document, formInputClickSelector, "click", formSubmitButtonClick)
|
||||
|
||||
document.addEventListener("DOMContentLoaded", refreshCSRFTokens)
|
||||
document.addEventListener("DOMContentLoaded", loadCSPNonce)
|
||||
return window._rails_loaded = true
|
||||
}
|
||||
Rails.start = start
|
||||
|
||||
// For backward compatibility
|
||||
if (typeof jQuery !== "undefined" && jQuery && jQuery.ajax) {
|
||||
if (jQuery.rails) { throw new Error("If you load both jquery_ujs and rails-ujs, use rails-ujs only.") }
|
||||
jQuery.rails = Rails
|
||||
jQuery.ajaxPrefilter(function(options, originalOptions, xhr) {
|
||||
if (!options.crossDomain) { return CSRFProtection(xhr) }
|
||||
})
|
||||
}
|
||||
|
||||
// This block is to maintain backwards compatibility with the existing
|
||||
// difference between what happens in a bundler and what happens using a
|
||||
// sprockets compiler. In the sprockets case, Rails.start() is called
|
||||
// automatically, but it is not in the ESModule case.
|
||||
if (typeof exports !== "object" && typeof module === "undefined") {
|
||||
// The coffeescript bundle would set this at the very top. The Rollup bundle
|
||||
// doesn't set this until the entire bundle has finished running, so we need
|
||||
// to make sure its set before firing the rails:attachBindings event for
|
||||
// backwards compatibility.
|
||||
window.Rails = Rails
|
||||
|
||||
if (fire(document, "rails:attachBindings")) {
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
export default Rails
|
|
@ -0,0 +1,119 @@
|
|||
import { cspNonce } from "./csp"
|
||||
import { CSRFProtection } from "./csrf"
|
||||
|
||||
const AcceptHeaders = {
|
||||
"*": "*/*",
|
||||
text: "text/plain",
|
||||
html: "text/html",
|
||||
xml: "application/xml, text/xml",
|
||||
json: "application/json, text/javascript",
|
||||
script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
|
||||
}
|
||||
|
||||
const ajax = (options) => {
|
||||
options = prepareOptions(options)
|
||||
var xhr = createXHR(options, function() {
|
||||
const response = processResponse(xhr.response != null ? xhr.response : xhr.responseText, xhr.getResponseHeader("Content-Type"))
|
||||
if (Math.floor(xhr.status / 100) === 2) {
|
||||
if (typeof options.success === "function") {
|
||||
options.success(response, xhr.statusText, xhr)
|
||||
}
|
||||
} else {
|
||||
if (typeof options.error === "function") {
|
||||
options.error(response, xhr.statusText, xhr)
|
||||
}
|
||||
}
|
||||
return (typeof options.complete === "function" ? options.complete(xhr, xhr.statusText) : undefined)
|
||||
})
|
||||
|
||||
if (options.beforeSend && !options.beforeSend(xhr, options)) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (xhr.readyState === XMLHttpRequest.OPENED) {
|
||||
return xhr.send(options.data)
|
||||
}
|
||||
}
|
||||
|
||||
var prepareOptions = function(options) {
|
||||
options.url = options.url || location.href
|
||||
options.type = options.type.toUpperCase()
|
||||
// append data to url if it's a GET request
|
||||
if ((options.type === "GET") && options.data) {
|
||||
if (options.url.indexOf("?") < 0) {
|
||||
options.url += "?" + options.data
|
||||
} else {
|
||||
options.url += "&" + options.data
|
||||
}
|
||||
}
|
||||
// Use "*" as default dataType
|
||||
if (!(options.dataType in AcceptHeaders)) { options.dataType = "*" }
|
||||
options.accept = AcceptHeaders[options.dataType]
|
||||
if (options.dataType !== "*") { options.accept += ", */*; q=0.01" }
|
||||
return options
|
||||
}
|
||||
|
||||
var createXHR = function(options, done) {
|
||||
const xhr = new XMLHttpRequest()
|
||||
// Open and set up xhr
|
||||
xhr.open(options.type, options.url, true)
|
||||
xhr.setRequestHeader("Accept", options.accept)
|
||||
// Set Content-Type only when sending a string
|
||||
// Sending FormData will automatically set Content-Type to multipart/form-data
|
||||
if (typeof options.data === "string") {
|
||||
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
|
||||
}
|
||||
if (!options.crossDomain) {
|
||||
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest")
|
||||
// Add X-CSRF-Token
|
||||
CSRFProtection(xhr)
|
||||
}
|
||||
xhr.withCredentials = !!options.withCredentials
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState === XMLHttpRequest.DONE) { return done(xhr) }
|
||||
}
|
||||
return xhr
|
||||
}
|
||||
|
||||
var processResponse = function(response, type) {
|
||||
if ((typeof response === "string") && (typeof type === "string")) {
|
||||
if (type.match(/\bjson\b/)) {
|
||||
try { response = JSON.parse(response) } catch (error) { /* do nothing */ }
|
||||
} else if (type.match(/\b(?:java|ecma)script\b/)) {
|
||||
const script = document.createElement("script")
|
||||
script.setAttribute("nonce", cspNonce())
|
||||
script.text = response
|
||||
document.head.appendChild(script).parentNode.removeChild(script)
|
||||
} else if (type.match(/\b(xml|html|svg)\b/)) {
|
||||
const parser = new DOMParser()
|
||||
type = type.replace(/;.+/, "") // remove something like ';charset=utf-8'
|
||||
try { response = parser.parseFromString(response, type) } catch (error1) { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
// Default way to get an element's href. May be overridden at Rails.href.
|
||||
const href = element => element.href
|
||||
|
||||
// Determines if the request is a cross domain request.
|
||||
const isCrossDomain = function(url) {
|
||||
const originAnchor = document.createElement("a")
|
||||
originAnchor.href = location.href
|
||||
const urlAnchor = document.createElement("a")
|
||||
try {
|
||||
urlAnchor.href = url
|
||||
// If URL protocol is false or is a string containing a single colon
|
||||
// *and* host are false, assume it is not a cross-domain request
|
||||
// (should only be the case for IE7 and IE compatibility mode).
|
||||
// Otherwise, evaluate protocol and host of the URL against the origin
|
||||
// protocol and host.
|
||||
return !(((!urlAnchor.protocol || (urlAnchor.protocol === ":")) && !urlAnchor.host) ||
|
||||
((originAnchor.protocol + "//" + originAnchor.host) === (urlAnchor.protocol + "//" + urlAnchor.host)))
|
||||
} catch (e) {
|
||||
// If there is an error parsing the URL, assume it is crossDomain.
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
export { ajax, href, isCrossDomain }
|
|
@ -0,0 +1,45 @@
|
|||
// Link elements bound by rails-ujs
|
||||
const linkClickSelector = "a[data-confirm], a[data-method], a[data-remote]:not([disabled]), a[data-disable-with], a[data-disable]"
|
||||
|
||||
// Button elements bound by rails-ujs
|
||||
const buttonClickSelector = {
|
||||
selector: "button[data-remote]:not([form]), button[data-confirm]:not([form])",
|
||||
exclude: "form button"
|
||||
}
|
||||
|
||||
// Select elements bound by rails-ujs
|
||||
const inputChangeSelector = "select[data-remote], input[data-remote], textarea[data-remote]"
|
||||
|
||||
// Form elements bound by rails-ujs
|
||||
const formSubmitSelector = "form:not([data-turbo=true])"
|
||||
|
||||
// Form input elements bound by rails-ujs
|
||||
const formInputClickSelector = "form:not([data-turbo=true]) input[type=submit], form:not([data-turbo=true]) input[type=image], form:not([data-turbo=true]) button[type=submit], form:not([data-turbo=true]) button:not([type]), input[type=submit][form], input[type=image][form], button[type=submit][form], button[form]:not([type])"
|
||||
|
||||
// Form input elements disabled during form submission
|
||||
const formDisableSelector = "input[data-disable-with]:enabled, button[data-disable-with]:enabled, textarea[data-disable-with]:enabled, input[data-disable]:enabled, button[data-disable]:enabled, textarea[data-disable]:enabled"
|
||||
|
||||
// Form input elements re-enabled after form submission
|
||||
const formEnableSelector = "input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled, input[data-disable]:disabled, button[data-disable]:disabled, textarea[data-disable]:disabled"
|
||||
|
||||
// Form file input elements
|
||||
const fileInputSelector = "input[name][type=file]:not([disabled])"
|
||||
|
||||
// Link onClick disable selector with possible re-enable after remote submission
|
||||
const linkDisableSelector = "a[data-disable-with], a[data-disable]"
|
||||
|
||||
// Button onClick disable selector with possible re-enable after remote submission
|
||||
const buttonDisableSelector = "button[data-remote][data-disable-with], button[data-remote][data-disable]"
|
||||
|
||||
export {
|
||||
linkClickSelector,
|
||||
buttonClickSelector,
|
||||
inputChangeSelector,
|
||||
formSubmitSelector,
|
||||
formInputClickSelector,
|
||||
formDisableSelector,
|
||||
formEnableSelector,
|
||||
fileInputSelector,
|
||||
linkDisableSelector,
|
||||
buttonDisableSelector
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
let nonce = null
|
||||
|
||||
const loadCSPNonce = () => {
|
||||
const metaTag = document.querySelector("meta[name=csp-nonce]")
|
||||
return nonce = metaTag && metaTag.content
|
||||
}
|
||||
|
||||
// Returns the Content-Security-Policy nonce for inline scripts.
|
||||
const cspNonce = () => nonce || loadCSPNonce()
|
||||
|
||||
export { cspNonce, loadCSPNonce }
|
|
@ -0,0 +1,30 @@
|
|||
import { $ } from "./dom"
|
||||
|
||||
// Up-to-date Cross-Site Request Forgery token
|
||||
const csrfToken = () => {
|
||||
const meta = document.querySelector("meta[name=csrf-token]")
|
||||
return meta && meta.content
|
||||
}
|
||||
|
||||
// URL param that must contain the CSRF token
|
||||
const csrfParam = () => {
|
||||
const meta = document.querySelector("meta[name=csrf-param]")
|
||||
return meta && meta.content
|
||||
}
|
||||
|
||||
// Make sure that every Ajax request sends the CSRF token
|
||||
const CSRFProtection = (xhr) => {
|
||||
const token = csrfToken()
|
||||
if (token) { return xhr.setRequestHeader("X-CSRF-Token", token) }
|
||||
}
|
||||
|
||||
// Make sure that all forms have actual up-to-date tokens (cached forms contain old ones)
|
||||
const refreshCSRFTokens = () => {
|
||||
const token = csrfToken()
|
||||
const param = csrfParam()
|
||||
if (token && param) {
|
||||
return $("form input[name=\"" + param + "\"]").forEach(input => input.value = token)
|
||||
}
|
||||
}
|
||||
|
||||
export { csrfToken, csrfParam, CSRFProtection, refreshCSRFTokens }
|
|
@ -0,0 +1,38 @@
|
|||
const m = Element.prototype.matches ||
|
||||
Element.prototype.matchesSelector ||
|
||||
Element.prototype.mozMatchesSelector ||
|
||||
Element.prototype.msMatchesSelector ||
|
||||
Element.prototype.oMatchesSelector ||
|
||||
Element.prototype.webkitMatchesSelector
|
||||
|
||||
// Checks if the given native dom element matches the selector
|
||||
// element::
|
||||
// native DOM element
|
||||
// selector::
|
||||
// CSS selector string or
|
||||
// a JavaScript object with `selector` and `exclude` properties
|
||||
// Examples: "form", { selector: "form", exclude: "form[data-remote='true']"}
|
||||
const matches = function(element, selector) {
|
||||
if (selector.exclude) {
|
||||
return m.call(element, selector.selector) && !m.call(element, selector.exclude)
|
||||
} else {
|
||||
return m.call(element, selector)
|
||||
}
|
||||
}
|
||||
|
||||
// get and set data on a given element using "expando properties"
|
||||
// See: https://developer.mozilla.org/en-US/docs/Glossary/Expando
|
||||
const EXPANDO = "_ujsData"
|
||||
|
||||
const getData = (element, key) => element[EXPANDO] ? element[EXPANDO][key] : undefined
|
||||
|
||||
const setData = function(element, key, value) {
|
||||
if (!element[EXPANDO]) { element[EXPANDO] = {} }
|
||||
return element[EXPANDO][key] = value
|
||||
}
|
||||
|
||||
// a wrapper for document.querySelectorAll
|
||||
// returns an Array
|
||||
const $ = selector => Array.prototype.slice.call(document.querySelectorAll(selector))
|
||||
|
||||
export { matches, getData, setData, $ }
|
|
@ -0,0 +1,82 @@
|
|||
import { matches } from "./dom"
|
||||
|
||||
let preventDefault
|
||||
|
||||
// Polyfill for CustomEvent in IE9+
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
|
||||
let {
|
||||
CustomEvent
|
||||
} = window
|
||||
|
||||
if (typeof CustomEvent !== "function") {
|
||||
CustomEvent = function(event, params) {
|
||||
const evt = document.createEvent("CustomEvent")
|
||||
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail)
|
||||
return evt
|
||||
}
|
||||
|
||||
CustomEvent.prototype = window.Event.prototype;
|
||||
|
||||
// Fix setting `defaultPrevented` when `preventDefault()` is called
|
||||
// http://stackoverflow.com/questions/23349191/event-preventdefault-is-not-working-in-ie-11-for-custom-events
|
||||
({ preventDefault } = CustomEvent.prototype)
|
||||
CustomEvent.prototype.preventDefault = function() {
|
||||
const result = preventDefault.call(this)
|
||||
if (this.cancelable && !this.defaultPrevented) {
|
||||
Object.defineProperty(this, "defaultPrevented", {get() { return true }})
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// Triggers a custom event on an element and returns false if the event result is false
|
||||
// obj::
|
||||
// a native DOM element
|
||||
// name::
|
||||
// string that corresponds to the event you want to trigger
|
||||
// e.g. 'click', 'submit'
|
||||
// data::
|
||||
// data you want to pass when you dispatch an event
|
||||
const fire = (obj, name, data) => {
|
||||
const event = new CustomEvent(
|
||||
name, {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
detail: data
|
||||
}
|
||||
)
|
||||
obj.dispatchEvent(event)
|
||||
return !event.defaultPrevented
|
||||
}
|
||||
|
||||
// Helper function, needed to provide consistent behavior in IE
|
||||
const stopEverything = (e) => {
|
||||
fire(e.target, "ujs:everythingStopped")
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
e.stopImmediatePropagation()
|
||||
}
|
||||
|
||||
// Delegates events
|
||||
// to a specified parent `element`, which fires event `handler`
|
||||
// for the specified `selector` when an event of `eventType` is triggered
|
||||
// element::
|
||||
// parent element that will listen for events e.g. document
|
||||
// selector::
|
||||
// CSS selector; or an object that has `selector` and `exclude` properties (see: Rails.matches)
|
||||
// eventType::
|
||||
// string representing the event e.g. 'submit', 'click'
|
||||
// handler::
|
||||
// the event handler to be called
|
||||
const delegate = (element, selector, eventType, handler) => element.addEventListener(eventType, function(e) {
|
||||
let {
|
||||
target
|
||||
} = e
|
||||
while (!!(target instanceof Element) && !matches(target, selector)) { target = target.parentNode }
|
||||
if (target instanceof Element && (handler.call(target, e) === false)) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}
|
||||
})
|
||||
|
||||
export { fire, stopEverything, delegate }
|
|
@ -0,0 +1,43 @@
|
|||
import { matches } from "./dom"
|
||||
|
||||
const toArray = e => Array.prototype.slice.call(e)
|
||||
|
||||
const serializeElement = (element, additionalParam) => {
|
||||
let inputs = [element]
|
||||
if (matches(element, "form")) { inputs = toArray(element.elements) }
|
||||
const params = []
|
||||
|
||||
inputs.forEach(function(input) {
|
||||
if (!input.name || input.disabled) { return }
|
||||
if (matches(input, "fieldset[disabled] *")) { return }
|
||||
if (matches(input, "select")) {
|
||||
toArray(input.options).forEach(function(option) {
|
||||
if (option.selected) { params.push({name: input.name, value: option.value}) }
|
||||
})
|
||||
} else if (input.checked || (["radio", "checkbox", "submit"].indexOf(input.type) === -1)) {
|
||||
params.push({name: input.name, value: input.value})
|
||||
}
|
||||
})
|
||||
|
||||
if (additionalParam) { params.push(additionalParam) }
|
||||
|
||||
return params.map(function(param) {
|
||||
if (param.name) {
|
||||
return `${encodeURIComponent(param.name)}=${encodeURIComponent(param.value)}`
|
||||
} else {
|
||||
return param
|
||||
}}).join("&")
|
||||
}
|
||||
|
||||
// Helper function that returns form elements that match the specified CSS selector
|
||||
// If form is actually a "form" element this will return associated elements outside the from that have
|
||||
// the html form attribute set
|
||||
const formElements = (form, selector) => {
|
||||
if (matches(form, "form")) {
|
||||
return toArray(form.elements).filter(el => matches(el, selector))
|
||||
} else {
|
||||
return toArray(form.querySelectorAll(selector))
|
||||
}
|
||||
}
|
||||
|
||||
export { serializeElement, formElements }
|
|
@ -3,9 +3,3 @@ load_paths:
|
|||
|
||||
logical_paths:
|
||||
- rails-ujs.js
|
||||
|
||||
build:
|
||||
logical_paths:
|
||||
- rails-ujs.js
|
||||
path: lib/assets/compiled
|
||||
clean: true
|
||||
|
|
|
@ -2,17 +2,18 @@
|
|||
"name": "@rails/ujs",
|
||||
"version": "7.1.0-alpha",
|
||||
"description": "Ruby on Rails unobtrusive scripting adapter",
|
||||
"main": "lib/assets/compiled/rails-ujs.js",
|
||||
"main": "app/assets/javascripts/rails-ujs.js",
|
||||
"module": "app/assets/javascripts/rails-ujs.esm.js",
|
||||
"files": [
|
||||
"lib/assets/compiled/*.js"
|
||||
"app/assets/javascripts/*.js"
|
||||
],
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "bundle exec blade build",
|
||||
"build": "rollup --config rollup.config.js",
|
||||
"test": "echo \"See the README: https://github.com/rails/rails/blob/main/actionview/app/assets/javascripts#how-to-run-tests\" && exit 1",
|
||||
"lint": "coffeelint app/assets/javascripts && eslint test/ujs/public/test"
|
||||
"lint": "eslint app/javascript"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -30,7 +31,9 @@
|
|||
},
|
||||
"homepage": "https://rubyonrails.org/",
|
||||
"devDependencies": {
|
||||
"coffeelint": "^2.1.0",
|
||||
"eslint": "^2.13.1"
|
||||
"eslint": "^4.19.1",
|
||||
"eslint-plugin-import": "^2.23.4",
|
||||
"rollup": "^2.53.3",
|
||||
"rollup-plugin-terser": "^7.0.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
import { terser } from "rollup-plugin-terser"
|
||||
|
||||
const banner = `
|
||||
/*
|
||||
Unobtrusive JavaScript
|
||||
https://github.com/rails/rails/blob/main/actionview/app/javascript
|
||||
Released under the MIT license
|
||||
*/
|
||||
`
|
||||
|
||||
const terserOptions = {
|
||||
mangle: false,
|
||||
compress: false,
|
||||
format: {
|
||||
beautify: true,
|
||||
indent_level: 2,
|
||||
comments: function (node, comment) {
|
||||
if (comment.type == "comment2") {
|
||||
// multiline comment
|
||||
return comment.value.includes("Released under the MIT license")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default [
|
||||
{
|
||||
input: "app/javascript/rails-ujs/index.js",
|
||||
output: {
|
||||
file: "app/assets/javascripts/rails-ujs.js",
|
||||
format: "umd",
|
||||
name: "Rails",
|
||||
banner,
|
||||
},
|
||||
plugins: [
|
||||
terser(terserOptions)
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
input: "app/javascript/rails-ujs/index.js",
|
||||
output: {
|
||||
file: "app/assets/javascripts/rails-ujs.esm.js",
|
||||
format: "es",
|
||||
banner,
|
||||
},
|
||||
plugins: [
|
||||
terser(terserOptions)
|
||||
]
|
||||
}
|
||||
]
|
|
@ -534,12 +534,6 @@ Module.new do
|
|||
f.puts 'require "rails/all"'
|
||||
end
|
||||
|
||||
unless File.exist?("#{RAILS_FRAMEWORK_ROOT}/actionview/lib/assets/compiled/rails-ujs.js")
|
||||
Dir.chdir("#{RAILS_FRAMEWORK_ROOT}/actionview") do
|
||||
sh "yarn build"
|
||||
end
|
||||
end
|
||||
|
||||
assets_path = "#{RAILS_FRAMEWORK_ROOT}/railties/test/isolation/assets"
|
||||
unless Dir.exist?("#{assets_path}/node_modules")
|
||||
Dir.chdir(assets_path) do
|
||||
|
|
422
yarn.lock
422
yarn.lock
|
@ -293,11 +293,6 @@ ajv-errors@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.0.tgz#ecf021fa108fd17dfb5e6b383f2dd233e31ffc59"
|
||||
integrity sha1-7PAh+hCP0X37Xms4Py3SM+Mf/Fk=
|
||||
|
||||
ajv-keywords@^1.0.0:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c"
|
||||
integrity sha1-MU3QpLM2j609/NxU7eYXG4htrzw=
|
||||
|
||||
ajv-keywords@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762"
|
||||
|
@ -308,14 +303,6 @@ ajv-keywords@^3.1.0:
|
|||
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a"
|
||||
integrity sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=
|
||||
|
||||
ajv@^4.7.0:
|
||||
version "4.11.8"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536"
|
||||
integrity sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=
|
||||
dependencies:
|
||||
co "^4.6.0"
|
||||
json-stable-stringify "^1.0.1"
|
||||
|
||||
ajv@^5.2.3, ajv@^5.3.0:
|
||||
version "5.5.2"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
|
||||
|
@ -336,11 +323,6 @@ ajv@^6.1.0, ajv@^6.5.5:
|
|||
json-schema-traverse "^0.4.1"
|
||||
uri-js "^4.2.2"
|
||||
|
||||
ansi-escapes@^1.1.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
|
||||
integrity sha1-06ioOzGapneTZisT52HHkRQiMG4=
|
||||
|
||||
ansi-escapes@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30"
|
||||
|
@ -907,7 +889,7 @@ caseless@~0.12.0:
|
|||
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
|
||||
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
|
||||
|
||||
chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3:
|
||||
chalk@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
|
||||
integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=
|
||||
|
@ -992,13 +974,6 @@ class-utils@^0.3.5:
|
|||
isobject "^3.0.0"
|
||||
static-extend "^0.1.1"
|
||||
|
||||
cli-cursor@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987"
|
||||
integrity sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=
|
||||
dependencies:
|
||||
restore-cursor "^1.0.1"
|
||||
|
||||
cli-cursor@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
|
||||
|
@ -1016,28 +991,6 @@ co@^4.6.0:
|
|||
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
|
||||
integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=
|
||||
|
||||
code-point-at@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
|
||||
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
|
||||
|
||||
coffeelint@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/coffeelint/-/coffeelint-2.1.0.tgz#af65df3634e999d9ac01480736c36d3cd2f5dad8"
|
||||
integrity sha1-r2XfNjTpmdmsAUgHNsNtPNL12tg=
|
||||
dependencies:
|
||||
coffeescript "^2.1.0"
|
||||
glob "^7.0.6"
|
||||
ignore "^3.0.9"
|
||||
optimist "^0.6.1"
|
||||
resolve "^0.6.3"
|
||||
strip-json-comments "^1.0.2"
|
||||
|
||||
coffeescript@^2.1.0:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/coffeescript/-/coffeescript-2.3.2.tgz#e854a7020dfe47b7cf4dd412042e32ef1e269810"
|
||||
integrity sha512-YObiFDoukx7qPBi/K0kUKyntEZDfBQiqs/DbrR1xzASKOBjGT7auD85/DiPeRr9k++lRj7l3uA9TNMLfyfcD/Q==
|
||||
|
||||
collection-visit@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
|
||||
|
@ -1127,7 +1080,7 @@ concat-map@0.0.1:
|
|||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||
|
||||
concat-stream@^1.4.6, concat-stream@^1.5.0, concat-stream@^1.6.0:
|
||||
concat-stream@^1.5.0, concat-stream@^1.6.0:
|
||||
version "1.6.2"
|
||||
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
|
||||
integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
|
||||
|
@ -1294,13 +1247,6 @@ cyclist@~0.2.2:
|
|||
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640"
|
||||
integrity sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=
|
||||
|
||||
d@1:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f"
|
||||
integrity sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=
|
||||
dependencies:
|
||||
es5-ext "^0.10.9"
|
||||
|
||||
dashdash@^1.12.0:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
|
||||
|
@ -1318,7 +1264,7 @@ date-now@^0.1.4:
|
|||
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
|
||||
integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=
|
||||
|
||||
debug@2.6.9, debug@^2.1.1, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9:
|
||||
debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||
|
@ -1427,7 +1373,7 @@ diffie-hellman@^5.0.0:
|
|||
miller-rabin "^4.0.0"
|
||||
randombytes "^2.0.0"
|
||||
|
||||
doctrine@1.5.0, doctrine@^1.2.2:
|
||||
doctrine@1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
|
||||
integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=
|
||||
|
@ -1615,36 +1561,6 @@ es-to-primitive@^1.2.1:
|
|||
is-date-object "^1.0.1"
|
||||
is-symbol "^1.0.2"
|
||||
|
||||
es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14:
|
||||
version "0.10.46"
|
||||
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.46.tgz#efd99f67c5a7ec789baa3daa7f79870388f7f572"
|
||||
integrity sha512-24XxRvJXNFwEMpJb3nOkiRJKRoupmjYmOPVlI65Qy2SrtxwOTB+g6ODjBKOtwEHbYrhWRty9xxOWLNdClT2djw==
|
||||
dependencies:
|
||||
es6-iterator "~2.0.3"
|
||||
es6-symbol "~3.1.1"
|
||||
next-tick "1"
|
||||
|
||||
es6-iterator@^2.0.1, es6-iterator@~2.0.1, es6-iterator@~2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
|
||||
integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c=
|
||||
dependencies:
|
||||
d "1"
|
||||
es5-ext "^0.10.35"
|
||||
es6-symbol "^3.1.1"
|
||||
|
||||
es6-map@^0.1.3:
|
||||
version "0.1.5"
|
||||
resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0"
|
||||
integrity sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=
|
||||
dependencies:
|
||||
d "1"
|
||||
es5-ext "~0.10.14"
|
||||
es6-iterator "~2.0.1"
|
||||
es6-set "~0.1.5"
|
||||
es6-symbol "~3.1.1"
|
||||
event-emitter "~0.3.5"
|
||||
|
||||
es6-promise@^4.0.3:
|
||||
version "4.2.5"
|
||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.5.tgz#da6d0d5692efb461e082c14817fe2427d8f5d054"
|
||||
|
@ -1657,35 +1573,6 @@ es6-promisify@^5.0.0:
|
|||
dependencies:
|
||||
es6-promise "^4.0.3"
|
||||
|
||||
es6-set@~0.1.5:
|
||||
version "0.1.5"
|
||||
resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1"
|
||||
integrity sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=
|
||||
dependencies:
|
||||
d "1"
|
||||
es5-ext "~0.10.14"
|
||||
es6-iterator "~2.0.1"
|
||||
es6-symbol "3.1.1"
|
||||
event-emitter "~0.3.5"
|
||||
|
||||
es6-symbol@3.1.1, es6-symbol@^3.1.1, es6-symbol@~3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77"
|
||||
integrity sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=
|
||||
dependencies:
|
||||
d "1"
|
||||
es5-ext "~0.10.14"
|
||||
|
||||
es6-weak-map@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772f92d96f"
|
||||
integrity sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=
|
||||
dependencies:
|
||||
d "1"
|
||||
es5-ext "^0.10.14"
|
||||
es6-iterator "^2.0.1"
|
||||
es6-symbol "^3.1.1"
|
||||
|
||||
escape-html@~1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
|
||||
|
@ -1696,16 +1583,6 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
|
|||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
|
||||
|
||||
escope@^3.6.0:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3"
|
||||
integrity sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=
|
||||
dependencies:
|
||||
es6-map "^0.1.3"
|
||||
es6-weak-map "^2.0.1"
|
||||
esrecurse "^4.1.0"
|
||||
estraverse "^4.1.1"
|
||||
|
||||
eslint-import-resolver-node@^0.3.1:
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a"
|
||||
|
@ -1796,46 +1673,7 @@ eslint-visitor-keys@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d"
|
||||
integrity sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==
|
||||
|
||||
eslint@^2.13.1:
|
||||
version "2.13.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-2.13.1.tgz#e4cc8fa0f009fb829aaae23855a29360be1f6c11"
|
||||
integrity sha1-5MyPoPAJ+4KaquI4VaKTYL4fbBE=
|
||||
dependencies:
|
||||
chalk "^1.1.3"
|
||||
concat-stream "^1.4.6"
|
||||
debug "^2.1.1"
|
||||
doctrine "^1.2.2"
|
||||
es6-map "^0.1.3"
|
||||
escope "^3.6.0"
|
||||
espree "^3.1.6"
|
||||
estraverse "^4.2.0"
|
||||
esutils "^2.0.2"
|
||||
file-entry-cache "^1.1.1"
|
||||
glob "^7.0.3"
|
||||
globals "^9.2.0"
|
||||
ignore "^3.1.2"
|
||||
imurmurhash "^0.1.4"
|
||||
inquirer "^0.12.0"
|
||||
is-my-json-valid "^2.10.0"
|
||||
is-resolvable "^1.0.0"
|
||||
js-yaml "^3.5.1"
|
||||
json-stable-stringify "^1.0.0"
|
||||
levn "^0.3.0"
|
||||
lodash "^4.0.0"
|
||||
mkdirp "^0.5.0"
|
||||
optionator "^0.8.1"
|
||||
path-is-absolute "^1.0.0"
|
||||
path-is-inside "^1.0.1"
|
||||
pluralize "^1.2.1"
|
||||
progress "^1.1.8"
|
||||
require-uncached "^1.0.2"
|
||||
shelljs "^0.6.0"
|
||||
strip-json-comments "~1.0.1"
|
||||
table "^3.7.8"
|
||||
text-table "~0.2.0"
|
||||
user-home "^2.0.0"
|
||||
|
||||
eslint@^4.3.0:
|
||||
eslint@^4.19.1, eslint@^4.3.0:
|
||||
version "4.19.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.19.1.tgz#32d1d653e1d90408854bfb296f076ec7e186a300"
|
||||
integrity sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==
|
||||
|
@ -1879,7 +1717,7 @@ eslint@^4.3.0:
|
|||
table "4.0.2"
|
||||
text-table "~0.2.0"
|
||||
|
||||
espree@^3.1.6, espree@^3.5.4:
|
||||
espree@^3.5.4:
|
||||
version "3.5.4"
|
||||
resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7"
|
||||
integrity sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==
|
||||
|
@ -1906,7 +1744,7 @@ esrecurse@^4.1.0:
|
|||
dependencies:
|
||||
estraverse "^4.1.0"
|
||||
|
||||
estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0:
|
||||
estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
|
||||
integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=
|
||||
|
@ -1926,14 +1764,6 @@ esutils@^2.0.2:
|
|||
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
|
||||
integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=
|
||||
|
||||
event-emitter@~0.3.5:
|
||||
version "0.3.5"
|
||||
resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39"
|
||||
integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=
|
||||
dependencies:
|
||||
d "1"
|
||||
es5-ext "~0.10.14"
|
||||
|
||||
eventemitter3@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163"
|
||||
|
@ -1982,11 +1812,6 @@ exists-stat@1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/exists-stat/-/exists-stat-1.0.0.tgz#0660e3525a2e89d9e446129440c272edfa24b529"
|
||||
integrity sha1-BmDjUlouidnkRhKUQMJy7foktSk=
|
||||
|
||||
exit-hook@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8"
|
||||
integrity sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=
|
||||
|
||||
expand-braces@^0.1.1:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/expand-braces/-/expand-braces-0.1.2.tgz#488b1d1d2451cb3d3a6b192cfc030f44c5855fea"
|
||||
|
@ -2109,14 +1934,6 @@ figgy-pudding@^3.1.0, figgy-pudding@^3.5.1:
|
|||
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
|
||||
integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==
|
||||
|
||||
figures@^1.3.5:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
|
||||
integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=
|
||||
dependencies:
|
||||
escape-string-regexp "^1.0.5"
|
||||
object-assign "^4.1.0"
|
||||
|
||||
figures@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
|
||||
|
@ -2124,14 +1941,6 @@ figures@^2.0.0:
|
|||
dependencies:
|
||||
escape-string-regexp "^1.0.5"
|
||||
|
||||
file-entry-cache@^1.1.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-1.3.1.tgz#44c61ea607ae4be9c1402f41f44270cbfe334ff8"
|
||||
integrity sha1-RMYepgeuS+nBQC9B9EJwy/4zT/g=
|
||||
dependencies:
|
||||
flat-cache "^1.2.1"
|
||||
object-assign "^4.0.1"
|
||||
|
||||
file-entry-cache@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361"
|
||||
|
@ -2323,20 +2132,6 @@ functional-red-black-tree@^1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
|
||||
integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
|
||||
|
||||
generate-function@^2.0.0:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f"
|
||||
integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==
|
||||
dependencies:
|
||||
is-property "^1.0.2"
|
||||
|
||||
generate-object-property@^1.1.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0"
|
||||
integrity sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=
|
||||
dependencies:
|
||||
is-property "^1.0.0"
|
||||
|
||||
get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6"
|
||||
|
@ -2373,7 +2168,7 @@ glob-parent@^3.1.0:
|
|||
is-glob "^3.1.0"
|
||||
path-dirname "^1.0.0"
|
||||
|
||||
glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2:
|
||||
glob@^7.0.0, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2:
|
||||
version "7.1.3"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
|
||||
integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
|
||||
|
@ -2422,11 +2217,6 @@ globals@^11.0.1:
|
|||
resolved "https://registry.yarnpkg.com/globals/-/globals-11.9.0.tgz#bde236808e987f290768a93d065060d78e6ab249"
|
||||
integrity sha512-5cJVtyXWH8PiJPVLZzzoIizXx944O4OmRro5MWKx5fT4MgcN7OfaMutPeaTdJCCURwbWdhhcCWcKIffPnmTzBg==
|
||||
|
||||
globals@^9.2.0:
|
||||
version "9.18.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
|
||||
integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==
|
||||
|
||||
graceful-fs@^4.1.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2:
|
||||
version "4.1.15"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
|
||||
|
@ -2624,7 +2414,7 @@ iferr@^0.1.5:
|
|||
resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501"
|
||||
integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE=
|
||||
|
||||
ignore@^3.0.9, ignore@^3.1.2, ignore@^3.3.3:
|
||||
ignore@^3.3.3:
|
||||
version "3.3.10"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043"
|
||||
integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==
|
||||
|
@ -2662,25 +2452,6 @@ ini@^1.3.4:
|
|||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
|
||||
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
|
||||
|
||||
inquirer@^0.12.0:
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e"
|
||||
integrity sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=
|
||||
dependencies:
|
||||
ansi-escapes "^1.1.0"
|
||||
ansi-regex "^2.0.0"
|
||||
chalk "^1.0.0"
|
||||
cli-cursor "^1.0.1"
|
||||
cli-width "^2.0.0"
|
||||
figures "^1.3.5"
|
||||
lodash "^4.3.0"
|
||||
readline2 "^1.0.1"
|
||||
run-async "^0.1.0"
|
||||
rx-lite "^3.1.2"
|
||||
string-width "^1.0.1"
|
||||
strip-ansi "^3.0.0"
|
||||
through "^2.3.6"
|
||||
|
||||
inquirer@^3.0.6:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9"
|
||||
|
@ -2826,13 +2597,6 @@ is-extglob@^2.1.0, is-extglob@^2.1.1:
|
|||
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
|
||||
integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
|
||||
|
||||
is-fullwidth-code-point@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
|
||||
integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs=
|
||||
dependencies:
|
||||
number-is-nan "^1.0.0"
|
||||
|
||||
is-fullwidth-code-point@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
|
||||
|
@ -2857,22 +2621,6 @@ is-module@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
|
||||
integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=
|
||||
|
||||
is-my-ip-valid@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz#7b351b8e8edd4d3995d4d066680e664d94696824"
|
||||
integrity sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==
|
||||
|
||||
is-my-json-valid@^2.10.0:
|
||||
version "2.19.0"
|
||||
resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.19.0.tgz#8fd6e40363cd06b963fa877d444bfb5eddc62175"
|
||||
integrity sha512-mG0f/unGX1HZ5ep4uhRaPOS8EkAY8/j6mDRMJrutq4CqhoJWYp7qAlonIPy3TV7p3ju4TK9fo/PbnoksWmsp5Q==
|
||||
dependencies:
|
||||
generate-function "^2.0.0"
|
||||
generate-object-property "^1.1.0"
|
||||
is-my-ip-valid "^1.0.0"
|
||||
jsonpointer "^4.0.0"
|
||||
xtend "^4.0.0"
|
||||
|
||||
is-negative-zero@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24"
|
||||
|
@ -2907,11 +2655,6 @@ is-promise@^2.1.0:
|
|||
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
|
||||
integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=
|
||||
|
||||
is-property@^1.0.0, is-property@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84"
|
||||
integrity sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=
|
||||
|
||||
is-reference@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7"
|
||||
|
@ -3022,7 +2765,7 @@ js-tokens@^4.0.0:
|
|||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
||||
|
||||
js-yaml@^3.5.1, js-yaml@^3.9.1:
|
||||
js-yaml@^3.9.1:
|
||||
version "3.12.0"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1"
|
||||
integrity sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==
|
||||
|
@ -3060,13 +2803,6 @@ json-stable-stringify-without-jsonify@^1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
|
||||
integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
|
||||
|
||||
json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af"
|
||||
integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=
|
||||
dependencies:
|
||||
jsonify "~0.0.0"
|
||||
|
||||
json-stringify-safe@~5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
|
||||
|
@ -3084,16 +2820,6 @@ json5@^2.2.0:
|
|||
dependencies:
|
||||
minimist "^1.2.5"
|
||||
|
||||
jsonify@~0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
|
||||
integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=
|
||||
|
||||
jsonpointer@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9"
|
||||
integrity sha1-T9kss04OnbPInIYi7PUfm5eMbLk=
|
||||
|
||||
jsprim@^1.2.2:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
|
||||
|
@ -3255,7 +2981,7 @@ lodash.debounce@^4.0.8:
|
|||
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
|
||||
integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
|
||||
|
||||
lodash@4.17.11, lodash@^4.0.0, lodash@^4.16.6, lodash@^4.17.10, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0, lodash@^4.5.0, lodash@^4.8.0:
|
||||
lodash@4.17.11, lodash@^4.16.6, lodash@^4.17.10, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0, lodash@^4.5.0, lodash@^4.8.0:
|
||||
version "4.17.11"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
|
||||
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
|
||||
|
@ -3461,7 +3187,7 @@ mixin-deep@^1.2.0:
|
|||
for-in "^1.0.2"
|
||||
is-extendable "^1.0.1"
|
||||
|
||||
mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0:
|
||||
mkdirp@^0.5.1, mkdirp@~0.5.0:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
|
||||
integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
|
||||
|
@ -3497,11 +3223,6 @@ ms@^2.1.1:
|
|||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
|
||||
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
|
||||
|
||||
mute-stream@0.0.5:
|
||||
version "0.0.5"
|
||||
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0"
|
||||
integrity sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=
|
||||
|
||||
mute-stream@0.0.7:
|
||||
version "0.0.7"
|
||||
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
|
||||
|
@ -3544,11 +3265,6 @@ neo-async@^2.5.0:
|
|||
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.0.tgz#b9d15e4d71c6762908654b5183ed38b753340835"
|
||||
integrity sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==
|
||||
|
||||
next-tick@1:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
|
||||
integrity sha1-yobR/ogoFpsBICCOPchCS524NCw=
|
||||
|
||||
nice-try@^1.0.4:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||
|
@ -3617,17 +3333,12 @@ null-check@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/null-check/-/null-check-1.0.0.tgz#977dffd7176012b9ec30d2a39db5cf72a0439edd"
|
||||
integrity sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=
|
||||
|
||||
number-is-nan@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
|
||||
integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
|
||||
|
||||
oauth-sign@~0.9.0:
|
||||
version "0.9.0"
|
||||
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
|
||||
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
|
||||
|
||||
object-assign@^4.0.1, object-assign@^4.1.0:
|
||||
object-assign@^4.0.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
||||
|
@ -3703,11 +3414,6 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
|||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
onetime@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789"
|
||||
integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=
|
||||
|
||||
onetime@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
|
||||
|
@ -3723,7 +3429,7 @@ optimist@^0.6.1:
|
|||
minimist "~0.0.1"
|
||||
wordwrap "~0.0.2"
|
||||
|
||||
optionator@^0.8.1, optionator@^0.8.2:
|
||||
optionator@^0.8.2:
|
||||
version "0.8.2"
|
||||
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64"
|
||||
integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=
|
||||
|
@ -3740,11 +3446,6 @@ os-browserify@^0.3.0:
|
|||
resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27"
|
||||
integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=
|
||||
|
||||
os-homedir@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
|
||||
integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
|
||||
|
||||
os-tmpdir@~1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
|
||||
|
@ -3889,7 +3590,7 @@ path-is-absolute@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
|
||||
|
||||
path-is-inside@^1.0.1, path-is-inside@^1.0.2:
|
||||
path-is-inside@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
|
||||
integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=
|
||||
|
@ -3994,11 +3695,6 @@ pkg-up@^2.0.0:
|
|||
dependencies:
|
||||
find-up "^2.1.0"
|
||||
|
||||
pluralize@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45"
|
||||
integrity sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=
|
||||
|
||||
pluralize@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777"
|
||||
|
@ -4024,11 +3720,6 @@ process@^0.11.10:
|
|||
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
|
||||
integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
|
||||
|
||||
progress@^1.1.8:
|
||||
version "1.1.8"
|
||||
resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
|
||||
integrity sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=
|
||||
|
||||
progress@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.1.tgz#c9242169342b1c29d275889c95734621b1952e31"
|
||||
|
@ -4242,15 +3933,6 @@ readdirp@^2.0.0:
|
|||
micromatch "^3.1.10"
|
||||
readable-stream "^2.0.2"
|
||||
|
||||
readline2@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35"
|
||||
integrity sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=
|
||||
dependencies:
|
||||
code-point-at "^1.0.0"
|
||||
is-fullwidth-code-point "^1.0.0"
|
||||
mute-stream "0.0.5"
|
||||
|
||||
regex-not@^1.0.0, regex-not@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
|
||||
|
@ -4310,7 +3992,7 @@ request@2.88.0:
|
|||
tunnel-agent "^0.6.0"
|
||||
uuid "^3.3.2"
|
||||
|
||||
require-uncached@^1.0.2, require-uncached@^1.0.3:
|
||||
require-uncached@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3"
|
||||
integrity sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=
|
||||
|
@ -4348,11 +4030,6 @@ resolve@1.5.0:
|
|||
dependencies:
|
||||
path-parse "^1.0.5"
|
||||
|
||||
resolve@^0.6.3:
|
||||
version "0.6.3"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-0.6.3.tgz#dd957982e7e736debdf53b58a4dd91754575dd46"
|
||||
integrity sha1-3ZV5gufnNt699TtYpN2RdUV13UY=
|
||||
|
||||
resolve@^1.13.1, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0:
|
||||
version "1.20.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
|
||||
|
@ -4368,14 +4045,6 @@ resolve@^1.5.0, resolve@^1.6.0:
|
|||
dependencies:
|
||||
path-parse "^1.0.5"
|
||||
|
||||
restore-cursor@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541"
|
||||
integrity sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=
|
||||
dependencies:
|
||||
exit-hook "^1.0.0"
|
||||
onetime "^1.0.0"
|
||||
|
||||
restore-cursor@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
|
||||
|
@ -4419,7 +4088,7 @@ rollup-plugin-terser@^7.0.2:
|
|||
serialize-javascript "^4.0.0"
|
||||
terser "^5.0.0"
|
||||
|
||||
rollup@^2.35.1:
|
||||
rollup@^2.35.1, rollup@^2.53.3:
|
||||
version "2.53.3"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.53.3.tgz#14b0e57f0874d4ad23bdbb13050cf70bcd1eabf7"
|
||||
integrity sha512-79QIGP5DXz5ZHYnCPi3tLz+elOQi6gudp9YINdaJdjG0Yddubo6JRFUM//qCZ0Bap/GJrsUoEBVdSOc4AkMlRA==
|
||||
|
@ -4431,13 +4100,6 @@ rsvp@^3.3.3:
|
|||
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.6.2.tgz#2e96491599a96cde1b515d5674a8f7a91452926a"
|
||||
integrity sha512-OfWGQTb9vnwRjwtA2QwpG2ICclHC3pgXZO5xt8H2EfgDquO0qVdSb5T88L4qJVAEugbS56pAuV4XZM58UX8ulw==
|
||||
|
||||
run-async@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389"
|
||||
integrity sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=
|
||||
dependencies:
|
||||
once "^1.3.0"
|
||||
|
||||
run-async@^2.2.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
|
||||
|
@ -4464,11 +4126,6 @@ rx-lite@*, rx-lite@^4.0.8:
|
|||
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
|
||||
integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=
|
||||
|
||||
rx-lite@^3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
|
||||
integrity sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=
|
||||
|
||||
safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
|
@ -4603,11 +4260,6 @@ shebang-regex@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
|
||||
integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
|
||||
|
||||
shelljs@^0.6.0:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.6.1.tgz#ec6211bed1920442088fe0f70b2837232ed2c8a8"
|
||||
integrity sha1-7GIRvtGSBEIIj+D3Cyg3Iy7SyKg=
|
||||
|
||||
side-channel@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
|
||||
|
@ -4622,11 +4274,6 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
|
|||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
|
||||
integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
|
||||
|
||||
slice-ansi@0.0.4:
|
||||
version "0.0.4"
|
||||
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
|
||||
integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=
|
||||
|
||||
slice-ansi@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d"
|
||||
|
@ -4892,16 +4539,7 @@ streamroller@0.7.0:
|
|||
mkdirp "^0.5.1"
|
||||
readable-stream "^2.3.0"
|
||||
|
||||
string-width@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
|
||||
integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
|
||||
dependencies:
|
||||
code-point-at "^1.0.0"
|
||||
is-fullwidth-code-point "^1.0.0"
|
||||
strip-ansi "^3.0.0"
|
||||
|
||||
string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1:
|
||||
string-width@^2.1.0, string-width@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
|
||||
integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
|
||||
|
@ -4963,11 +4601,6 @@ strip-eof@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
|
||||
integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
|
||||
|
||||
strip-json-comments@^1.0.2, strip-json-comments@~1.0.1:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91"
|
||||
integrity sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=
|
||||
|
||||
strip-json-comments@~2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
||||
|
@ -5004,18 +4637,6 @@ table@4.0.2:
|
|||
slice-ansi "1.0.0"
|
||||
string-width "^2.1.1"
|
||||
|
||||
table@^3.7.8:
|
||||
version "3.8.3"
|
||||
resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f"
|
||||
integrity sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=
|
||||
dependencies:
|
||||
ajv "^4.7.0"
|
||||
ajv-keywords "^1.0.0"
|
||||
chalk "^1.1.1"
|
||||
lodash "^4.0.0"
|
||||
slice-ansi "0.0.4"
|
||||
string-width "^2.0.0"
|
||||
|
||||
tapable@^1.0.0, tapable@^1.1.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.1.tgz#4d297923c5a72a42360de2ab52dadfaaec00018e"
|
||||
|
@ -5294,13 +4915,6 @@ use@^3.1.0:
|
|||
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
||||
integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
|
||||
|
||||
user-home@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f"
|
||||
integrity sha1-nHC/2Babwdy/SGBODwS4tJzenp8=
|
||||
dependencies:
|
||||
os-homedir "^1.0.0"
|
||||
|
||||
useragent@2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/useragent/-/useragent-2.3.0.tgz#217f943ad540cb2128658ab23fc960f6a88c9972"
|
||||
|
|
Loading…
Reference in New Issue