diff options
Diffstat (limited to 'actionview/app/assets')
9 files changed, 75 insertions, 34 deletions
diff --git a/actionview/app/assets/javascripts/MIT-LICENSE b/actionview/app/assets/javascripts/MIT-LICENSE index befcbdc7b7..28e1b12496 100644 --- a/actionview/app/assets/javascripts/MIT-LICENSE +++ b/actionview/app/assets/javascripts/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2007-2017 Rails Core team +Copyright (c) 2007-2018 Rails Core team Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/actionview/app/assets/javascripts/README.md b/actionview/app/assets/javascripts/README.md index f321b9f720..b74fa1afad 100644 --- a/actionview/app/assets/javascripts/README.md +++ b/actionview/app/assets/javascripts/README.md @@ -1,5 +1,4 @@ -Ruby on Rails unobtrusive scripting adapter. -======================================== +# Ruby on Rails unobtrusive scripting adapter This unobtrusive scripting support file is developed for the Ruby on Rails framework, but is not strictly tied to any specific backend. You can drop this into any application to: @@ -8,53 +7,51 @@ This unobtrusive scripting support file is developed for the Ruby on Rails frame - make forms or hyperlinks submit data asynchronously with Ajax; - have submit buttons become automatically disabled on form submit to prevent double-clicking. -These features are achieved by adding certain ["data" attributes][data] to your HTML markup. In Rails, they are added by the framework's template helpers. +These features are achieved by adding certain [`data` attributes][data] to your HTML markup. In Rails, they are added by the framework's template helpers. -Requirements ------------- +## Optional prerequisites -- HTML5 doctype (optional). +Note that the `data` attributes this library adds are a feature of HTML5. If you're not targeting HTML5, these attributes may make your HTML to fail [validation][validator]. However, this shouldn't create any issues for web browsers or other user agents. -If you don't use HTML5, adding "data" attributes to your HTML4 or XHTML pages might make them fail [W3C markup validation][validator]. However, this shouldn't create any issues for web browsers or other user agents. +## Installation -Installation using npm ------------- +### NPM -Run `npm install rails-ujs --save` to install the rails-ujs package. + npm install rails-ujs --save + +### Yarn + + yarn add rails-ujs -Installation using Yarn ------------- +Ensure that `.yarnclean` does not include `assets` if you use [yarn autoclean](https://yarnpkg.com/lang/en/docs/cli/autoclean/). -Run `yarn add rails-ujs` to install the rails-ujs package. +## Usage -Usage ------------- +### Asset pipeline -Require `rails-ujs` in your application.js manifest. +In a conventional Rails application that uses the asset pipeline, require `rails-ujs` in your `application.js` manifest: ```javascript //= require rails-ujs ``` -Usage with yarn ------------- +### ES2015+ -When using with the Webpacker gem or your preferred JavaScript bundler, just -add the following to your main JS file and compile. +If you're using the Webpacker gem or some other JavaScript bundler, add the following to your main JS file: ```javascript import Rails from 'rails-ujs'; Rails.start() ``` -How to run tests ------------- +## How to run tests Run `bundle exec rake ujs:server` first, and then run the web tests by visiting http://localhost:4567 in your browser. ## License + rails-ujs is released under the [MIT License](MIT-LICENSE). -[data]: http://www.w3.org/TR/html5/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes "Embedding custom non-visible data with the data-* attributes" +[data]: https://www.w3.org/TR/html5/dom.html#embedding-custom-non-visible-data-with-the-data-attributes "Embedding custom non-visible data with the data-* attributes" [validator]: http://validator.w3.org/ [csrf]: http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html diff --git a/actionview/app/assets/javascripts/rails-ujs/features/confirm.coffee b/actionview/app/assets/javascripts/rails-ujs/features/confirm.coffee index 72b5aaa218..0738ffcdc9 100644 --- a/actionview/app/assets/javascripts/rails-ujs/features/confirm.coffee +++ b/actionview/app/assets/javascripts/rails-ujs/features/confirm.coffee @@ -5,6 +5,10 @@ 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 @@ -20,7 +24,7 @@ allowAction = (element) -> answer = false if fire(element, 'confirm') - try answer = confirm(message) + try answer = Rails.confirm(message, element) callback = fire(element, 'confirm:complete', [answer]) answer and callback diff --git a/actionview/app/assets/javascripts/rails-ujs/features/remote.coffee b/actionview/app/assets/javascripts/rails-ujs/features/remote.coffee index 852587042c..b3448dabac 100644 --- a/actionview/app/assets/javascripts/rails-ujs/features/remote.coffee +++ b/actionview/app/assets/javascripts/rails-ujs/features/remote.coffee @@ -62,7 +62,7 @@ Rails.handleRemote = (e) -> fire(element, 'ajax:send', [xhr]) else fire(element, 'ajax:stopped') - xhr.abort() + return false success: (args...) -> fire(element, 'ajax:success', args) error: (args...) -> fire(element, 'ajax:error', args) complete: (args...) -> fire(element, 'ajax:complete', args) diff --git a/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee b/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee index a653d3af3d..019bda635a 100644 --- a/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee +++ b/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee @@ -1,7 +1,8 @@ +#= require ./csp #= require ./csrf #= require ./event -{ CSRFProtection, fire } = Rails +{ cspNonce, CSRFProtection, fire } = Rails AcceptHeaders = '*': '*/*' @@ -20,13 +21,12 @@ Rails.ajax = (options) -> else options.error?(response, xhr.statusText, xhr) options.complete?(xhr, xhr.statusText) - # Call beforeSend hook - options.beforeSend?(xhr, options) - # Send the request + + if options.beforeSend? && !options.beforeSend(xhr, options) + return false + if xhr.readyState is XMLHttpRequest.OPENED xhr.send(options.data) - else - fire(document, 'ajaxStop') # to be compatible with jQuery.ajax prepareOptions = (options) -> options.url = options.url or location.href @@ -66,9 +66,10 @@ processResponse = (response, type) -> 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/) + else if type.match(/\bxml\b/) parser = new DOMParser() type = type.replace(/;.+/, '') # remove something like ';charset=utf-8' try response = parser.parseFromString(response, type) diff --git a/actionview/app/assets/javascripts/rails-ujs/utils/csp.coffee b/actionview/app/assets/javascripts/rails-ujs/utils/csp.coffee new file mode 100644 index 0000000000..8d2d6ce447 --- /dev/null +++ b/actionview/app/assets/javascripts/rails-ujs/utils/csp.coffee @@ -0,0 +1,4 @@ +# Content-Security-Policy nonce for inline scripts +cspNonce = Rails.cspNonce = -> + meta = document.querySelector('meta[name=csp-nonce]') + meta and meta.content diff --git a/actionview/app/assets/javascripts/rails-ujs/utils/dom.coffee b/actionview/app/assets/javascripts/rails-ujs/utils/dom.coffee index 6bef618147..3d3c5bb330 100644 --- a/actionview/app/assets/javascripts/rails-ujs/utils/dom.coffee +++ b/actionview/app/assets/javascripts/rails-ujs/utils/dom.coffee @@ -5,6 +5,13 @@ m = Element.prototype.matches 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) diff --git a/actionview/app/assets/javascripts/rails-ujs/utils/event.coffee b/actionview/app/assets/javascripts/rails-ujs/utils/event.coffee index 8d3ff007ea..a7eee52060 100644 --- a/actionview/app/assets/javascripts/rails-ujs/utils/event.coffee +++ b/actionview/app/assets/javascripts/rails-ujs/utils/event.coffee @@ -11,9 +11,26 @@ if typeof CustomEvent isnt 'function' 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 corrspends 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, @@ -31,6 +48,17 @@ Rails.stopEverything = (e) -> 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 diff --git a/actionview/app/assets/javascripts/rails-ujs/utils/form.coffee b/actionview/app/assets/javascripts/rails-ujs/utils/form.coffee index 5fa337b518..736cab08db 100644 --- a/actionview/app/assets/javascripts/rails-ujs/utils/form.coffee +++ b/actionview/app/assets/javascripts/rails-ujs/utils/form.coffee @@ -10,7 +10,7 @@ Rails.serializeElement = (element, additionalParam) -> params = [] inputs.forEach (input) -> - return unless input.name + return if !input.name || input.disabled if matches(input, 'select') toArray(input.options).forEach (option) -> params.push(name: input.name, value: option.value) if option.selected |