Permalink
Browse files

Import rails-ujs v0.1.0 from rails/rails-ujs

  • Loading branch information...
guilleiguaran committed Feb 20, 2017
1 parent a2234c5 commit 41c33bd4b2ec3f4a482e6030b6fda15091d81e4a
@@ -1,21 +1,21 @@
#= export Rails
@Rails =
# Link elements bound by jquery-ujs
# 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 jquery-ujs
# 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 jquery-ujs
# Select elements bound by rails-ujs
inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]'
# Form elements bound by jquery-ujs
# Form elements bound by rails-ujs
formSubmitSelector: 'form'
# Form input elements bound by jquery-ujs
# Form input elements bound by rails-ujs
formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form 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
@@ -24,9 +24,6 @@
# 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 required input elements
requiredInputSelector: 'input[name][required]:not([disabled]), textarea[name][required]:not([disabled])'
# Form file input elements
fileInputSelector: 'input[name][type=file]:not([disabled])'
@@ -4,7 +4,7 @@
matches, getData, setData
fire, stopEverything
ajax, isCrossDomain
blankInputs, serializeElement
serializeElement
} = Rails
# Checks "data-remote" if true to handle the request through a XHR request.
@@ -71,16 +71,6 @@ Rails.handleRemote = (e) ->
)
stopEverything(e)
# Check whether any required fields are empty
# In both ajax mode and normal mode
Rails.validateForm = (e) ->
form = this
return if form.noValidate or getData(form, 'ujs:formnovalidate-button')
# Skip other logic when required values are missing or file upload is present
blankRequiredInputs = blankInputs(form, Rails.requiredInputSelector, false)
if blankRequiredInputs.length > 0 and fire(form, 'ajax:aborted:required', [blankRequiredInputs])
stopEverything(e)
Rails.formSubmitButtonClick = (e) ->
button = this
form = button.form
@@ -14,7 +14,7 @@
refreshCSRFTokens, CSRFProtection
enableElement, disableElement
handleConfirm
handleRemote, validateForm, formSubmitButtonClick, handleMetaClick
handleRemote, formSubmitButtonClick, handleMetaClick
handleMethod
} = Rails
@@ -25,9 +25,9 @@ if jQuery? and not jQuery.rails
CSRFProtection(xhr) unless options.crossDomain
Rails.start = ->
# Cut down on the number of issues from people inadvertently including jquery_ujs twice
# by detecting and raising an error when it happens.
throw new Error('jquery-ujs has already been loaded!') if window._rails_loaded
# 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.
@@ -58,7 +58,6 @@ Rails.start = ->
delegate document, Rails.inputChangeSelector, 'change', handleRemote
delegate document, Rails.formSubmitSelector, 'submit', handleConfirm
delegate document, Rails.formSubmitSelector, 'submit', validateForm
delegate document, Rails.formSubmitSelector, 'submit', handleRemote
# Normal mode submit
# Slight timeout so that the submit button gets properly serialized
@@ -6,7 +6,7 @@
# https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
CustomEvent = window.CustomEvent
if typeof CustomEvent is 'function'
if typeof CustomEvent isnt 'function'
CustomEvent = (event, params) ->
evt = document.createEvent('CustomEvent')
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail)
@@ -14,7 +14,7 @@ Rails.serializeElement = (element, additionalParam) ->
if matches(input, 'select')
toArray(input.options).forEach (option) ->
params.push(name: input.name, value: option.value) if option.selected
else if input.type isnt 'radio' and input.type isnt 'checkbox' or input.checked
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
@@ -34,28 +34,3 @@ Rails.formElements = (form, selector) ->
toArray(form.elements).filter (el) -> matches(el, selector)
else
toArray(form.querySelectorAll(selector))
# Helper function which checks for blank inputs in a form that match the specified CSS selector
Rails.blankInputs = (form, selector, nonBlank) ->
foundInputs = []
requiredInputs = toArray(form.querySelectorAll(selector or 'input, textarea'))
checkedRadioButtonNames = {}
requiredInputs.forEach (input) ->
if input.type is 'radio'
# Don't count unchecked required radio as blank if other radio with same name is checked,
# regardless of whether same-name radio input has required attribute or not. The spec
# states https://www.w3.org/TR/html5/forms.html#the-required-attribute
radioName = input.name
# Skip if we've already seen the radio with this name.
unless checkedRadioButtonNames[radioName]
# If none checked
if form.querySelectorAll("input[type=radio][name='#{radioName}']:checked").length == 0
radios = form.querySelectorAll("input[type=radio][name='#{radioName}']")
foundInputs = foundInputs.concat(toArray(radios))
# We only need to check each name once.
checkedRadioButtonNames[radioName] = radioName
else
valueToCheck = if input.type is 'checkbox' then input.checked else !!input.value
foundInputs.push(input) if valueToCheck is nonBlank
foundInputs
@@ -108,202 +108,6 @@ asyncTest('stopping the "ajax:beforeSend" event aborts the request', 1, function
})
})
asyncTest('blank required form input field should abort request and trigger "ajax:aborted:required" event', 5, function() {
$(document).bind('iframe:loading', function() {
ok(false, 'form should not get submitted')
})
var form = $('form[data-remote]')
.append($('<input type="text" name="user_name" required="required">'))
.append($('<textarea name="user_bio" required="required"></textarea>'))
.bindNative('ajax:beforeSend', function() {
ok(false, 'ajax:beforeSend should not run')
})
.bindNative('ajax:aborted:required', function(e, data) {
data = $(data)
ok(data.length == 2, 'ajax:aborted:required event is passed all blank required inputs (jQuery objects)')
ok(data.first().is('input[name="user_name"]'), 'ajax:aborted:required adds blank required input to data')
ok(data.last().is('textarea[name="user_bio"]'), 'ajax:aborted:required adds blank required textarea to data')
ok(true, 'ajax:aborted:required should run')
})
.triggerNative('submit')
setTimeout(function() {
form.find('input[required],textarea[required]').val('Tyler')
form.unbind('ajax:beforeSend')
submit()
}, 13)
})
asyncTest('blank required form input for non-remote form should abort normal submission', 1, function() {
var form = $('form[data-remote]')
.append($('<input type="text" name="user_name" required="required">'))
.removeAttr('data-remote')
.bindNative('ujs:everythingStopped', function() {
ok(true, 'ujs:everythingStopped should run')
})
.triggerNative('submit')
setTimeout(function() {
start()
}, 13)
})
asyncTest('form should be submitted with blank required fields if handler is bound to "ajax:aborted:required" event that returns false', 1, function() {
var form = $('form[data-remote]')
.append($('<input type="text" name="user_name" required="required">'))
.bindNative('ajax:beforeSend', function() {
ok(true, 'ajax:beforeSend should run')
})
.bindNative('ajax:aborted:required', function() {
return false
})
.triggerNative('submit')
setTimeout(function() {
start()
}, 13)
})
asyncTest('disabled fields should not be included in blank required check', 2, function() {
var form = $('form[data-remote]')
.append($('<input type="text" name="user_name" required="required" disabled="disabled">'))
.append($('<textarea name="user_bio" required="required" disabled="disabled"></textarea>'))
.bindNative('ajax:beforeSend', function() {
ok(true, 'ajax:beforeSend should run')
})
.bindNative('ajax:aborted:required', function() {
ok(false, 'ajax:aborted:required should not run')
})
submit()
})
asyncTest('form should be submitted with blank required fields if it has the "novalidate" attribute', 2, function() {
var form = $('form[data-remote]')
.append($('<input type="text" name="user_name" required="required">'))
.attr('novalidate', 'novalidate')
.bindNative('ajax:beforeSend', function() {
ok(true, 'ajax:beforeSend should run')
})
.bindNative('ajax:aborted:required', function() {
ok(false, 'ajax:aborted:required should not run')
})
submit()
})
asyncTest('form should be submitted with blank required fields if the button has the "formnovalidate" attribute', 2, function() {
var submit_button = $('<input type="submit" formnovalidate>')
var form = $('form[data-remote]')
.append($('<input type="text" name="user_name" required="required">'))
.append(submit_button)
.bindNative('ajax:beforeSend', function() {
ok(true, 'ajax:beforeSend should run')
})
.bindNative('ajax:aborted:required', function() {
ok(false, 'ajax:aborted:required should not run')
})
submit_with_button(submit_button)
})
asyncTest('blank required form input for non-remote form with "novalidate" attribute should not abort normal submission', 1, function() {
$(document).bind('iframe:loading', function() {
ok(true, 'form should get submitted')
})
var form = $('form[data-remote]')
.append($('<input type="text" name="user_name" required="required">'))
.removeAttr('data-remote')
.attr('novalidate', 'novalidate')
.triggerNative('submit')
setTimeout(function() {
start()
}, 13)
})
asyncTest('unchecked required checkbox should abort form submission', 1, function() {
var form = $('form[data-remote]')
.append($('<input type="checkbox" name="agree" required="required">'))
.removeAttr('data-remote')
.bindNative('ujs:everythingStopped', function() {
ok(true, 'ujs:everythingStopped should run')
})
.triggerNative('submit')
setTimeout(function() {
start()
}, 13)
})
asyncTest('unchecked required radio should abort form submission', 3, function() {
var form = $('form[data-remote]')
.append($('<input type="radio" name="yes_no_none" required="required" value=1>'))
.append($('<input type="radio" name="yes_no_none" required="required" value=2>'))
.removeAttr('data-remote')
.bindNative('ujs:everythingStopped', function() {
ok(true, 'ujs:everythingStopped should run')
})
.bindNative('ajax:aborted:required', function(e, data) {
data = $(data)
equal(data.length, 2, 'blankRequiredInputs should include both radios')
ok(data.first().is('input[type=radio][value=1]'), 'blankRequiredInputs[0] should be the first radio')
})
.triggerNative('submit')
setTimeout(function() {
start()
}, 13)
})
asyncTest('required radio should only require one to be checked', 1, function() {
$(document).bind('iframe:loading', function() {
ok(true, 'form should get submitted')
})
var form = $('form[data-remote]')
.append($('<input type="radio" name="yes_no" required="required" value=1 id="checkme">'))
.append($('<input type="radio" name="yes_no" required="required" value=2>'))
.removeAttr('data-remote')
.bindNative('ujs:everythingStopped', function() {
ok(false, 'ujs:everythingStopped should not run')
})
.find('#checkme').prop('checked', true)
.end()
.triggerNative('submit')
setTimeout(function() {
start()
}, 13)
})
asyncTest('required radio should only require one to be checked if not all radios are required', 1, function() {
$(document).bind('iframe:loading', function() {
ok(true, 'form should get submitted')
})
var form = $('form[data-remote]')
// Check the radio that is not required
.append($('<input type="radio" name="yes_no_maybe" value=1 >'))
// Check the radio that is not required
.append($('<input type="radio" name="yes_no_maybe" value=2 id="checkme">'))
// Only one needs to be required
.append($('<input type="radio" name="yes_no_maybe" required="required" value=3>'))
.removeAttr('data-remote')
.bindNative('ujs:everythingStopped', function() {
ok(false, 'ujs:everythingStopped should not run')
})
.find('#checkme').prop('checked', true)
.end()
.triggerNative('submit')
setTimeout(function() {
start()
}, 13)
})
function skipIt() {
// This test cannot work due to the security feature in browsers which makes the value
// attribute of file input fields readonly, so it cannot be set with default value.
@@ -73,7 +73,6 @@ asyncTest('clicking on a link with data-remote attribute', 5, function() {
.bindNative('ajax:success', function(e, data, status, xhr) {
App.assertCallbackInvoked('ajax:success')
App.assertRequestPath(data, '/echo')
console.log(data.params)
equal(data.params.data1, 'value1', 'ajax arguments should have key data1 with right value')
equal(data.params.data2, 'value2', 'ajax arguments should have key data2 with right value')
App.assertGetRequest(data)
@@ -398,3 +397,19 @@ asyncTest('form should be serialized correctly', 6, function() {
})
.triggerNative('submit')
})
asyncTest('form buttons should only be serialized when clicked', 4, function() {
$('form')
.append('<input type="submit" name="submit1" value="submit1" />')
.append('<button name="submit2" value="submit2" />')
.append('<button name="submit3" value="submit3" />')
.bindNative('ajax:success', function(e, data, status, xhr) {
equal(data.params.submit1, undefined)
equal(data.params.submit2, 'submit2')
equal(data.params.submit3, undefined)
equal(data['rack.request.form_vars'], 'user_name=john&submit2=submit2')
start()
})
.find('[name=submit2]').triggerNative('click')
})
@@ -46,7 +46,7 @@ asyncTest('the event selector strings are overridable', 1, function() {
start()
})
asyncTest('including jquery-ujs multiple times throws error', 1, function() {
asyncTest('including rails-ujs multiple times throws error', 1, function() {
throws(function() {
Rails.start()
}, 'appending rails.js again throws error')
Oops, something went wrong.

0 comments on commit 41c33bd

Please sign in to comment.