aboutsummaryrefslogtreecommitdiffstats
path: root/actionview/app/assets/javascripts/features
diff options
context:
space:
mode:
Diffstat (limited to 'actionview/app/assets/javascripts/features')
-rw-r--r--actionview/app/assets/javascripts/features/confirm.coffee26
-rw-r--r--actionview/app/assets/javascripts/features/disable.coffee78
-rw-r--r--actionview/app/assets/javascripts/features/method.coffee34
-rw-r--r--actionview/app/assets/javascripts/features/remote.coffee100
4 files changed, 238 insertions, 0 deletions
diff --git a/actionview/app/assets/javascripts/features/confirm.coffee b/actionview/app/assets/javascripts/features/confirm.coffee
new file mode 100644
index 0000000000..72b5aaa218
--- /dev/null
+++ b/actionview/app/assets/javascripts/features/confirm.coffee
@@ -0,0 +1,26 @@
+#= require_tree ../utils
+
+{ fire, stopEverything } = Rails
+
+Rails.handleConfirm = (e) ->
+ stopEverything(e) unless allowAction(this)
+
+# 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 = confirm(message)
+ callback = fire(element, 'confirm:complete', [answer])
+
+ answer and callback
diff --git a/actionview/app/assets/javascripts/features/disable.coffee b/actionview/app/assets/javascripts/features/disable.coffee
new file mode 100644
index 0000000000..e8cce7da40
--- /dev/null
+++ b/actionview/app/assets/javascripts/features/disable.coffee
@@ -0,0 +1,78 @@
+#= require_tree ../utils
+
+{ matches, getData, setData, stopEverything, formElements } = Rails
+
+# Unified function to enable an element (link, button and form)
+Rails.enableElement = (e) ->
+ element = if e instanceof Event then e.target else 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) ->
+ 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) ->
+ 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)
diff --git a/actionview/app/assets/javascripts/features/method.coffee b/actionview/app/assets/javascripts/features/method.coffee
new file mode 100644
index 0000000000..d04d9414dd
--- /dev/null
+++ b/actionview/app/assets/javascripts/features/method.coffee
@@ -0,0 +1,34 @@
+#= 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)
diff --git a/actionview/app/assets/javascripts/features/remote.coffee b/actionview/app/assets/javascripts/features/remote.coffee
new file mode 100644
index 0000000000..30a5dc21fa
--- /dev/null
+++ b/actionview/app/assets/javascripts/features/remote.coffee
@@ -0,0 +1,100 @@
+#= require_tree ../utils
+
+{
+ matches, getData, setData
+ fire, stopEverything
+ ajax, isCrossDomain
+ blankInputs, 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.method
+ 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')
+ xhr.abort()
+ 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)
+
+# 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
+ 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.handleMetaClick = (e) ->
+ link = this
+ method = (link.getAttribute('data-method') or 'GET').toUpperCase()
+ data = link.getAttribute('data-params')
+ metaClick = e.metaKey or e.ctrlKey
+ e.stopImmediatePropagation() if metaClick and method is 'GET' and not data