Skip to content

Commit ad3a477

Browse files
committed
Add rails-ujs to Action View
1 parent 0cafbd4 commit ad3a477

14 files changed

+649
-0
lines changed

actionview/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/lib/assets/compiled
2+
/tmp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#= export Rails
2+
3+
@Rails =
4+
# Link elements bound by jquery-ujs
5+
linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote]:not([disabled]), a[data-disable-with], a[data-disable]'
6+
7+
# Button elements bound by jquery-ujs
8+
buttonClickSelector:
9+
selector: 'button[data-remote]:not([form]), button[data-confirm]:not([form])'
10+
exclude: 'form button'
11+
12+
# Select elements bound by jquery-ujs
13+
inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]'
14+
15+
# Form elements bound by jquery-ujs
16+
formSubmitSelector: 'form'
17+
18+
# Form input elements bound by jquery-ujs
19+
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])'
20+
21+
# Form input elements disabled during form submission
22+
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'
23+
24+
# Form input elements re-enabled after form submission
25+
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'
26+
27+
# Form required input elements
28+
requiredInputSelector: 'input[name][required]:not([disabled]), textarea[name][required]:not([disabled])'
29+
30+
# Form file input elements
31+
fileInputSelector: 'input[name][type=file]:not([disabled])'
32+
33+
# Link onClick disable selector with possible reenable after remote submission
34+
linkDisableSelector: 'a[data-disable-with], a[data-disable]'
35+
36+
# Button onClick disable selector with possible reenable after remote submission
37+
buttonDisableSelector: 'button[data-remote][data-disable-with], button[data-remote][data-disable]'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#= require_tree ../utils
2+
3+
{ fire, stopEverything } = Rails
4+
5+
Rails.handleConfirm = (e) ->
6+
stopEverything(e) unless allowAction(this)
7+
8+
# For 'data-confirm' attribute:
9+
# - Fires `confirm` event
10+
# - Shows the confirmation dialog
11+
# - Fires the `confirm:complete` event
12+
#
13+
# Returns `true` if no function stops the chain and user chose yes `false` otherwise.
14+
# Attaching a handler to the element's `confirm` event that returns a `falsy` value cancels the confirmation dialog.
15+
# Attaching a handler to the element's `confirm:complete` event that returns a `falsy` value makes this function
16+
# return false. The `confirm:complete` event is fired whether or not the user answered true or false to the dialog.
17+
allowAction = (element) ->
18+
message = element.getAttribute('data-confirm')
19+
return true unless message
20+
21+
answer = false
22+
if fire(element, 'confirm')
23+
try answer = confirm(message)
24+
callback = fire(element, 'confirm:complete', [answer])
25+
26+
answer and callback
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#= require_tree ../utils
2+
3+
{ matches, getData, setData, stopEverything, formElements } = Rails
4+
5+
# Unified function to enable an element (link, button and form)
6+
Rails.enableElement = (e) ->
7+
element = if e instanceof Event then e.target else e
8+
if matches(element, Rails.linkDisableSelector)
9+
enableLinkElement(element)
10+
else if matches(element, Rails.buttonDisableSelector) or matches(element, Rails.formEnableSelector)
11+
enableFormElement(element)
12+
else if matches(element, Rails.formSubmitSelector)
13+
enableFormElements(element)
14+
15+
# Unified function to disable an element (link, button and form)
16+
Rails.disableElement = (e) ->
17+
element = if e instanceof Event then e.target else e
18+
if matches(element, Rails.linkDisableSelector)
19+
disableLinkElement(element)
20+
else if matches(element, Rails.buttonDisableSelector) or matches(element, Rails.formDisableSelector)
21+
disableFormElement(element)
22+
else if matches(element, Rails.formSubmitSelector)
23+
disableFormElements(element)
24+
25+
# Replace element's html with the 'data-disable-with' after storing original html
26+
# and prevent clicking on it
27+
disableLinkElement = (element) ->
28+
replacement = element.getAttribute('data-disable-with')
29+
if replacement?
30+
setData(element, 'ujs:enable-with', element.innerHTML) # store enabled state
31+
element.innerHTML = replacement
32+
element.addEventListener('click', stopEverything) # prevent further clicking
33+
setData(element, 'ujs:disabled', true)
34+
35+
# Restore element to its original state which was disabled by 'disableLinkElement' above
36+
enableLinkElement = (element) ->
37+
originalText = getData(element, 'ujs:enable-with')
38+
if originalText?
39+
element.innerHTML = originalText # set to old enabled state
40+
setData(element, 'ujs:enable-with', null) # clean up cache
41+
element.removeEventListener('click', stopEverything) # enable element
42+
setData(element, 'ujs:disabled', null)
43+
44+
# Disables form elements:
45+
# - Caches element value in 'ujs:enable-with' data store
46+
# - Replaces element text with value of 'data-disable-with' attribute
47+
# - Sets disabled property to true
48+
disableFormElements = (form) ->
49+
formElements(form, Rails.formDisableSelector).forEach(disableFormElement)
50+
51+
disableFormElement = (element) ->
52+
replacement = element.getAttribute('data-disable-with')
53+
if replacement?
54+
if matches(element, 'button')
55+
setData(element, 'ujs:enable-with', element.innerHTML)
56+
element.innerHTML = replacement
57+
else
58+
setData(element, 'ujs:enable-with', element.value)
59+
element.value = replacement
60+
element.disabled = true
61+
setData(element, 'ujs:disabled', true)
62+
63+
# Re-enables disabled form elements:
64+
# - Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`)
65+
# - Sets disabled property to false
66+
enableFormElements = (form) ->
67+
formElements(form, Rails.formEnableSelector).forEach(enableFormElement)
68+
69+
enableFormElement = (element) ->
70+
originalText = getData(element, 'ujs:enable-with')
71+
if originalText?
72+
if matches(element, 'button')
73+
element.innerHTML = originalText
74+
else
75+
element.value = originalText
76+
setData(element, 'ujs:enable-with', null) # clean up cache
77+
element.disabled = false
78+
setData(element, 'ujs:disabled', null)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#= require_tree ../utils
2+
3+
{ stopEverything } = Rails
4+
5+
# Handles "data-method" on links such as:
6+
# <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a>
7+
Rails.handleMethod = (e) ->
8+
link = this
9+
method = link.getAttribute('data-method')
10+
return unless method
11+
12+
href = Rails.href(link)
13+
csrfToken = Rails.csrfToken()
14+
csrfParam = Rails.csrfParam()
15+
form = document.createElement('form')
16+
formContent = "<input name='_method' value='#{method}' type='hidden' />"
17+
18+
if csrfParam? and csrfToken? and not Rails.isCrossDomain(href)
19+
formContent += "<input name='#{csrfParam}' value='#{csrfToken}' type='hidden' />"
20+
21+
# Must trigger submit by click on a button, else "submit" event handler won't work!
22+
# https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit
23+
formContent += '<input type="submit" />'
24+
25+
form.method = 'post'
26+
form.action = href
27+
form.target = link.target
28+
form.innerHTML = formContent
29+
form.style.display = 'none'
30+
31+
document.body.appendChild(form)
32+
form.querySelector('[type="submit"]').click()
33+
34+
stopEverything(e)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#= require_tree ../utils
2+
3+
{
4+
matches, getData, setData
5+
fire, stopEverything
6+
ajax, isCrossDomain
7+
blankInputs, serializeElement
8+
} = Rails
9+
10+
# Checks "data-remote" if true to handle the request through a XHR request.
11+
isRemote = (element) ->
12+
value = element.getAttribute('data-remote')
13+
value? and value isnt 'false'
14+
15+
# Submits "remote" forms and links with ajax
16+
Rails.handleRemote = (e) ->
17+
element = this
18+
19+
return true unless isRemote(element)
20+
unless fire(element, 'ajax:before')
21+
fire(element, 'ajax:stopped')
22+
return false
23+
24+
withCredentials = element.getAttribute('data-with-credentials')
25+
dataType = element.getAttribute('data-type') or 'script'
26+
27+
if matches(element, Rails.formSubmitSelector)
28+
# memoized value from clicked submit button
29+
button = getData(element, 'ujs:submit-button')
30+
method = getData(element, 'ujs:submit-button-formmethod') or element.method
31+
url = getData(element, 'ujs:submit-button-formaction') or element.getAttribute('action') or location.href
32+
33+
# strip query string if it's a GET request
34+
url = url.replace(/\?.*$/, '') if method.toUpperCase() is 'GET'
35+
36+
if element.enctype is 'multipart/form-data'
37+
data = new FormData(element)
38+
data.append(button.name, button.value) if button?
39+
else
40+
data = serializeElement(element, button)
41+
42+
setData(element, 'ujs:submit-button', null)
43+
setData(element, 'ujs:submit-button-formmethod', null)
44+
setData(element, 'ujs:submit-button-formaction', null)
45+
else if matches(element, Rails.buttonClickSelector) or matches(element, Rails.inputChangeSelector)
46+
method = element.getAttribute('data-method')
47+
url = element.getAttribute('data-url')
48+
data = serializeElement(element, element.getAttribute('data-params'))
49+
else
50+
method = element.getAttribute('data-method')
51+
url = Rails.href(element)
52+
data = element.getAttribute('data-params')
53+
54+
ajax(
55+
type: method or 'GET'
56+
url: url
57+
data: data
58+
dataType: dataType
59+
# stopping the "ajax:beforeSend" event will cancel the ajax request
60+
beforeSend: (xhr, options) ->
61+
if fire(element, 'ajax:beforeSend', [xhr, options])
62+
fire(element, 'ajax:send', [xhr])
63+
else
64+
fire(element, 'ajax:stopped')
65+
xhr.abort()
66+
success: (args...) -> fire(element, 'ajax:success', args)
67+
error: (args...) -> fire(element, 'ajax:error', args)
68+
complete: (args...) -> fire(element, 'ajax:complete', args)
69+
crossDomain: isCrossDomain(url)
70+
withCredentials: withCredentials? and withCredentials isnt 'false'
71+
)
72+
stopEverything(e)
73+
74+
# Check whether any required fields are empty
75+
# In both ajax mode and normal mode
76+
Rails.validateForm = (e) ->
77+
form = this
78+
return if form.noValidate or getData(form, 'ujs:formnovalidate-button')
79+
# Skip other logic when required values are missing or file upload is present
80+
blankRequiredInputs = blankInputs(form, Rails.requiredInputSelector, false)
81+
if blankRequiredInputs.length > 0 and fire(form, 'ajax:aborted:required', [blankRequiredInputs])
82+
stopEverything(e)
83+
84+
Rails.formSubmitButtonClick = (e) ->
85+
button = this
86+
form = button.form
87+
return unless form
88+
# Register the pressed submit button
89+
setData(form, 'ujs:submit-button', name: button.name, value: button.value) if button.name
90+
# Save attributes from button
91+
setData(form, 'ujs:formnovalidate-button', button.formNoValidate)
92+
setData(form, 'ujs:submit-button-formaction', button.getAttribute('formaction'))
93+
setData(form, 'ujs:submit-button-formmethod', button.getAttribute('formmethod'))
94+
95+
Rails.handleMetaClick = (e) ->
96+
link = this
97+
method = (link.getAttribute('data-method') or 'GET').toUpperCase()
98+
data = link.getAttribute('data-params')
99+
metaClick = e.metaKey or e.ctrlKey
100+
e.stopImmediatePropagation() if metaClick and method is 'GET' and not data
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#
2+
# Unobtrusive JavaScript
3+
# https://github.com/rails/rails-ujs
4+
#
5+
# Released under the MIT license
6+
#
7+
#= require ./config
8+
#= require_tree ./utils
9+
#= require_tree ./features
10+
11+
{
12+
fire, delegate
13+
getData, $
14+
refreshCSRFTokens, CSRFProtection
15+
enableElement, disableElement
16+
handleConfirm
17+
handleRemote, validateForm, formSubmitButtonClick, handleMetaClick
18+
handleMethod
19+
} = Rails
20+
21+
# For backward compatibility
22+
if jQuery? and not jQuery.rails
23+
jQuery.rails = Rails
24+
jQuery.ajaxPrefilter (options, originalOptions, xhr) ->
25+
CSRFProtection(xhr) unless options.crossDomain
26+
27+
Rails.start = ->
28+
# Cut down on the number of issues from people inadvertently including jquery_ujs twice
29+
# by detecting and raising an error when it happens.
30+
throw new Error('jquery-ujs has already been loaded!') if window._rails_loaded
31+
32+
# This event works the same as the load event, except that it fires every
33+
# time the page is loaded.
34+
# See https://github.com/rails/jquery-ujs/issues/357
35+
# See https://developer.mozilla.org/en-US/docs/Using_Firefox_1.5_caching
36+
window.addEventListener 'pageshow', ->
37+
$(Rails.formEnableSelector).forEach (el) ->
38+
enableElement(el) if getData(el, 'ujs:disabled')
39+
$(Rails.linkDisableSelector).forEach (el) ->
40+
enableElement(el) if getData(el, 'ujs:disabled')
41+
42+
delegate document, Rails.linkDisableSelector, 'ajax:complete', enableElement
43+
delegate document, Rails.linkDisableSelector, 'ajax:stopped', enableElement
44+
delegate document, Rails.buttonDisableSelector, 'ajax:complete', enableElement
45+
delegate document, Rails.buttonDisableSelector, 'ajax:stopped', enableElement
46+
47+
delegate document, Rails.linkClickSelector, 'click', handleConfirm
48+
delegate document, Rails.linkClickSelector, 'click', handleMetaClick
49+
delegate document, Rails.linkClickSelector, 'click', disableElement
50+
delegate document, Rails.linkClickSelector, 'click', handleRemote
51+
delegate document, Rails.linkClickSelector, 'click', handleMethod
52+
53+
delegate document, Rails.buttonClickSelector, 'click', handleConfirm
54+
delegate document, Rails.buttonClickSelector, 'click', disableElement
55+
delegate document, Rails.buttonClickSelector, 'click', handleRemote
56+
57+
delegate document, Rails.inputChangeSelector, 'change', handleConfirm
58+
delegate document, Rails.inputChangeSelector, 'change', handleRemote
59+
60+
delegate document, Rails.formSubmitSelector, 'submit', handleConfirm
61+
delegate document, Rails.formSubmitSelector, 'submit', validateForm
62+
delegate document, Rails.formSubmitSelector, 'submit', handleRemote
63+
# Normal mode submit
64+
# Slight timeout so that the submit button gets properly serialized
65+
delegate document, Rails.formSubmitSelector, 'submit', (e) -> setTimeout((-> disableElement(e)), 13)
66+
delegate document, Rails.formSubmitSelector, 'ajax:send', disableElement
67+
delegate document, Rails.formSubmitSelector, 'ajax:complete', enableElement
68+
69+
delegate document, Rails.formInputClickSelector, 'click', handleConfirm
70+
delegate document, Rails.formInputClickSelector, 'click', formSubmitButtonClick
71+
72+
document.addEventListener('DOMContentLoaded', refreshCSRFTokens)
73+
window._rails_loaded = true
74+
75+
if window.Rails is Rails and fire(document, 'rails:attachBindings')
76+
Rails.start()

0 commit comments

Comments
 (0)