shoelace-rails 0.4.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +11 -61
  3. data/.gitignore +1 -6
  4. data/Appraisals +6 -0
  5. data/CHANGELOG.md +22 -5
  6. data/Gemfile +0 -3
  7. data/Rakefile +2 -17
  8. data/app/helpers/shoelace/form_helper.rb +63 -14
  9. data/gemfiles/rails_60.gemfile +0 -1
  10. data/gemfiles/rails_61.gemfile +0 -1
  11. data/gemfiles/rails_70.gemfile +0 -1
  12. data/gemfiles/rails_71.gemfile +10 -0
  13. data/gemfiles/rails_edge.gemfile +0 -1
  14. data/lib/shoelace/rails/version.rb +1 -1
  15. data/shoelace-rails.gemspec +1 -1
  16. data/test/helpers/form_helper_test.rb +40 -11
  17. data/test/helpers/translation_test.rb +160 -0
  18. metadata +7 -58
  19. data/dist/.keep +0 -0
  20. data/dist/types/.keep +0 -0
  21. data/package.json +0 -50
  22. data/rollup.config.js +0 -49
  23. data/src/index.ts +0 -2
  24. data/src/turbo/index.ts +0 -6
  25. data/src/turbo/polyfills/formdata-event.js +0 -27
  26. data/src/turbo/sl-turbo-form.ts +0 -110
  27. data/src/turbolinks/features/confirm.ts +0 -42
  28. data/src/turbolinks/features/disable.ts +0 -94
  29. data/src/turbolinks/features/remote.ts +0 -107
  30. data/src/turbolinks/index.ts +0 -6
  31. data/src/turbolinks/selectors.ts +0 -38
  32. data/src/turbolinks/start.ts +0 -38
  33. data/src/turbolinks/turbolinks.ts +0 -78
  34. data/src/turbolinks/utils/ajax.ts +0 -146
  35. data/src/turbolinks/utils/csp.ts +0 -20
  36. data/src/turbolinks/utils/csrf.ts +0 -33
  37. data/src/turbolinks/utils/dom.ts +0 -40
  38. data/src/turbolinks/utils/event.ts +0 -57
  39. data/src/turbolinks/utils/form.ts +0 -58
  40. data/test/dummy_app/Gemfile +0 -19
  41. data/test/dummy_app/Rakefile +0 -6
  42. data/test/dummy_app/app/controllers/hotwire_forms_controller.rb +0 -46
  43. data/test/dummy_app/app/controllers/turbolinks_forms_controller.rb +0 -37
  44. data/test/dummy_app/app/models/user.rb +0 -16
  45. data/test/dummy_app/app/packs/entrypoints/hotwire.js +0 -1
  46. data/test/dummy_app/app/packs/entrypoints/turbolinks.js +0 -5
  47. data/test/dummy_app/app/views/hotwire_forms/form.html.erb +0 -45
  48. data/test/dummy_app/app/views/hotwire_forms/show.html.erb +0 -5
  49. data/test/dummy_app/app/views/layouts/application.html.erb +0 -39
  50. data/test/dummy_app/app/views/turbolinks_forms/form.html.erb +0 -44
  51. data/test/dummy_app/app/views/turbolinks_forms/show.html.erb +0 -5
  52. data/test/dummy_app/bin/rails +0 -5
  53. data/test/dummy_app/bin/webpack +0 -18
  54. data/test/dummy_app/bin/yarn +0 -18
  55. data/test/dummy_app/config/application.rb +0 -16
  56. data/test/dummy_app/config/boot.rb +0 -4
  57. data/test/dummy_app/config/environment.rb +0 -2
  58. data/test/dummy_app/config/environments/development.rb +0 -10
  59. data/test/dummy_app/config/environments/test.rb +0 -18
  60. data/test/dummy_app/config/routes.rb +0 -4
  61. data/test/dummy_app/config/webpack/development.js +0 -5
  62. data/test/dummy_app/config/webpack/production.js +0 -1
  63. data/test/dummy_app/config/webpack/test.js +0 -5
  64. data/test/dummy_app/config/webpacker.yml +0 -33
  65. data/test/dummy_app/config.ru +0 -6
  66. data/test/dummy_app/package.json +0 -24
  67. data/test/dummy_app/test/system/hotwire_form_test.rb +0 -63
  68. data/test/dummy_app/test/system/turbolinks_form_test.rb +0 -38
  69. data/test/dummy_app/test/test_helper.rb +0 -68
  70. data/tsconfig.json +0 -19
  71. data/yarn.lock +0 -249
@@ -1,94 +0,0 @@
1
- // This code was heavily inspired by the rails-ujs project.
2
- // Copyright (c) 2007-2021 Rails Core team.
3
- import { buttonDisableSelector, formDisableSelector, formEnableSelector, formSubmitSelector } from "../selectors"
4
- import { getData, matches, setData } from "../utils/dom"
5
- import { stopEverything } from "../utils/event"
6
- import { formElements } from "../utils/form"
7
-
8
- export const handleDisabledElement = (event) => {
9
- const element: HTMLInputElement | HTMLFormElement = event.target
10
-
11
- if (element.disabled) {
12
- return stopEverything(event)
13
- }
14
- }
15
-
16
- // Unified function to enable an element (link, button and form)
17
- export const enableElement = function (e) {
18
- let element
19
-
20
- if (e instanceof Event) {
21
- if (isXhrRedirect(e)) {
22
- return
23
- }
24
- element = e.target
25
- } else {
26
- element = e
27
- }
28
-
29
- if (matches(element, buttonDisableSelector) || matches(element, formEnableSelector)) {
30
- return enableFormElement(element)
31
- } else if (matches(element, formSubmitSelector)) {
32
- return enableFormElements(element)
33
- }
34
- }
35
-
36
- // Unified function to disable an element (link, button and form)
37
- export const disableElement = function (e) {
38
- const element = e instanceof Event ? e.target : e
39
-
40
- if (matches(element, buttonDisableSelector) || matches(element, formDisableSelector)) {
41
- return disableFormElement(element)
42
- } else if (matches(element, formSubmitSelector)) {
43
- return disableFormElements(element)
44
- }
45
- }
46
-
47
- // Disables form elements:
48
- // - Caches element value in 'ujs:enable-with' data store
49
- // - Replaces element text with value of 'data-disable-with' attribute
50
- // - Sets disabled property to true
51
- const disableFormElements = (form) => formElements(form, formDisableSelector).forEach(disableFormElement)
52
-
53
- const disableFormElement = function (element) {
54
- if (getData(element, "ujs:disabled")) {
55
- return
56
- }
57
-
58
- const replacement = element.getAttribute("data-disable-with")
59
- if (replacement != null) {
60
- if (matches(element, "button")) {
61
- setData(element, "ujs:enable-with", element.innerHTML)
62
- element.innerHTML = replacement
63
- } else {
64
- setData(element, "ujs:enable-with", element.value)
65
- element.value = replacement
66
- }
67
- }
68
- element.disabled = true
69
- return setData(element, "ujs:disabled", true)
70
- }
71
-
72
- // Re-enables disabled form elements:
73
- // - Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`)
74
- // - Sets disabled property to false
75
- const enableFormElements = (form) => formElements(form, formEnableSelector).forEach(enableFormElement)
76
-
77
- const enableFormElement = function (element) {
78
- const originalText = getData(element, "ujs:enable-with")
79
- if (originalText != null) {
80
- if (matches(element, "button")) {
81
- element.innerHTML = originalText
82
- } else {
83
- element.value = originalText
84
- }
85
- setData(element, "ujs:enable-with", null) // clean up cache
86
- }
87
- element.disabled = false
88
- return setData(element, "ujs:disabled", null)
89
- }
90
-
91
- const isXhrRedirect = function (event) {
92
- const xhr = event.detail != null ? event.detail[0] : undefined
93
- return (xhr != null ? xhr.getResponseHeader("X-Xhr-Redirect") : undefined) != null
94
- }
@@ -1,107 +0,0 @@
1
- // This code was heavily inspired by the rails-ujs project.
2
- // Copyright (c) 2007-2021 Rails Core team.
3
- import { formSubmitSelector } from "../selectors"
4
- import { ajax, href, isCrossDomain } from "../utils/ajax"
5
- import { getData, matches, setData } from "../utils/dom"
6
- import { fire, stopEverything } from "../utils/event"
7
-
8
- const isRemote = function (element) {
9
- const value = element.getAttribute("data-remote")
10
- return value != null && value !== "false"
11
- }
12
-
13
- export const handleRemote = function (event: CustomEvent<{ formData: FormData; formControls: HTMLElement[] }>) {
14
- const element = this
15
-
16
- if (!isRemote(element)) {
17
- return true
18
- }
19
-
20
- if (!fire(element, "ajax:before")) {
21
- fire(element, "ajax:stopped")
22
- return false
23
- }
24
-
25
- let method, url, data
26
-
27
- const withCredentials = element.getAttribute("data-with-credentials")
28
- const dataType = element.getAttribute("data-type") || "script"
29
-
30
- if (matches(element, formSubmitSelector)) {
31
- // memoized value from clicked submit button
32
- method = getData(element, "ujs:submit-button-formmethod") || element.getAttribute("method") || "GET"
33
- url = getData(element, "ujs:submit-button-formaction") || element.getAttribute("action") || location.href
34
-
35
- // strip query string if it's a GET request
36
- if (method.toUpperCase() === "GET") {
37
- url = url.replace(/\?.*$/, "")
38
- data = Array.from<Array<string>, string>(event.detail.formData as any, (e) =>
39
- e.map(encodeURIComponent).join("=")
40
- ).join("&")
41
- } else {
42
- data = event.detail.formData
43
- }
44
-
45
- setData(element, "ujs:submit-button", null)
46
- setData(element, "ujs:submit-button-formmethod", null)
47
- setData(element, "ujs:submit-button-formaction", null)
48
- } else {
49
- method = element.getAttribute("data-method")
50
- url = href(element)
51
- data = element.getAttribute("data-params")
52
- }
53
-
54
- ajax({
55
- type: method,
56
- url,
57
- data,
58
- 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
- return fire(element, "ajax:send", [xhr])
63
- } else {
64
- fire(element, "ajax:stopped")
65
- return false
66
- }
67
- },
68
- success: (...args) => fire(element, "ajax:success", args),
69
- error: (...args) => fire(element, "ajax:error", args),
70
- complete: (...args) => fire(element, "ajax:complete", args),
71
- crossDomain: isCrossDomain(url),
72
- withCredentials: withCredentials != null && withCredentials !== "false",
73
- })
74
-
75
- return stopEverything(event)
76
- }
77
-
78
- export const formSubmitButtonClick = (event) => {
79
- const button: HTMLButtonElement = event.target
80
- const { form } = button
81
-
82
- if (!form) {
83
- return
84
- }
85
-
86
- if (button.name) {
87
- setData(form, "ujs:submit-button", { name: button.name, value: button.value })
88
- }
89
-
90
- setData(form, "ujs:formnovalidate-button", button.formNoValidate)
91
- setData(form, "ujs:submit-button-formaction", button.getAttribute("formaction"))
92
- return setData(form, "ujs:submit-button-formmethod", button.getAttribute("formmethod"))
93
- }
94
-
95
- export const preventInsignificantClick = (event) => {
96
- const link: HTMLElement = event.target
97
-
98
- const method = (link.getAttribute("data-method") || "GET").toUpperCase()
99
- const data = link.getAttribute("data-params")
100
- const metaClick = event.metaKey || event.ctrlKey
101
- const insignificantMetaClick = metaClick && method === "GET" && !data
102
- const nonPrimaryMouseClick = event.button != null && event.button !== 0
103
-
104
- if (nonPrimaryMouseClick || insignificantMetaClick) {
105
- return event.stopImmediatePropagation()
106
- }
107
- }
@@ -1,6 +0,0 @@
1
- export * from "./utils/event"
2
- export * from "./utils/dom"
3
- export * from "./features/remote"
4
- export * from "./selectors"
5
- export * from "./turbolinks"
6
- export * from "./start"
@@ -1,38 +0,0 @@
1
- // This code was heavily inspired by the rails-ujs project.
2
- // Copyright (c) 2007-2021 Rails Core team.
3
- export const buttonClickSelector = {
4
- selector: "sl-button[data-remote]:not([form]), sl-button[data-confirm]:not([form])",
5
- exclude: "sl-form sl-button",
6
- }
7
-
8
- export const formSubmitSelector = "sl-form[data-remote]"
9
-
10
- export const formInputClickSelector = [
11
- "sl-form sl-button[submit]",
12
- "sl-form sl-button:not([type])",
13
- "sl-button[submit][sl-form]",
14
- "sl-button[sl-form]:not([type])",
15
- ].join(", ")
16
-
17
- export const formDisableSelector = [
18
- "sl-input[data-disable-with]:not([disabled])",
19
- "sl-button[data-disable-with]:not([disabled])",
20
- "sl-textarea[data-disable-with]:not([disabled])",
21
- "sl-input[data-disable]:not([disabled])",
22
- "sl-button[data-disable]:not([disabled])",
23
- "sl-textarea[data-disable]:not([disabled])",
24
- ].join(", ")
25
-
26
- export const formEnableSelector = [
27
- "sl-input[data-disable-with][disabled]",
28
- "sl-button[data-disable-with][disabled]",
29
- "sl-textarea[data-disable-with][disabled]",
30
- "sl-input[data-disable][disabled]",
31
- "sl-button[data-disable][disabled]",
32
- "sl-textarea[data-disable][disabled]",
33
- ].join(", ")
34
-
35
- export const buttonDisableSelector = [
36
- "sl-button[data-remote][data-disable-with]",
37
- "sl-button[data-remote][data-disable]",
38
- ].join(", ")
@@ -1,38 +0,0 @@
1
- // This code was heavily inspired by the rails-ujs project.
2
- // Copyright (c) 2007-2021 Rails Core team.
3
- import { handleConfirm } from "./features/confirm"
4
- import { disableElement, enableElement, handleDisabledElement } from "./features/disable"
5
- import { formSubmitButtonClick, handleRemote, preventInsignificantClick } from "./features/remote"
6
- import { buttonClickSelector, buttonDisableSelector, formInputClickSelector, formSubmitSelector } from "./selectors"
7
-
8
- import { delegate } from "./utils/event"
9
-
10
- export const getDefaultAssetPath = () => {
11
- const rootUrl = (document.currentScript as any).src.replace(/\/packs.*$/, "")
12
-
13
- return `${rootUrl}/packs/js/`
14
- }
15
-
16
- export const startUjs = () => {
17
- delegate(document, buttonDisableSelector, "ajax:complete", enableElement)
18
- delegate(document, buttonDisableSelector, "ajax:stopped", enableElement)
19
-
20
- delegate(document, buttonClickSelector, "click", preventInsignificantClick)
21
- delegate(document, buttonClickSelector, "click", handleDisabledElement)
22
- delegate(document, buttonClickSelector, "click", handleConfirm)
23
- delegate(document, buttonClickSelector, "click", disableElement)
24
- delegate(document, buttonClickSelector, "click", handleRemote)
25
-
26
- delegate(document, formSubmitSelector, "sl-submit", handleDisabledElement)
27
- delegate(document, formSubmitSelector, "sl-submit", handleConfirm)
28
- delegate(document, formSubmitSelector, "sl-submit", handleRemote)
29
-
30
- // simulates a normal form submit:
31
- delegate(document, formSubmitSelector, "ajax:send", disableElement)
32
- delegate(document, formSubmitSelector, "ajax:complete", enableElement)
33
-
34
- delegate(document, formInputClickSelector, "click", preventInsignificantClick)
35
- delegate(document, formInputClickSelector, "click", handleDisabledElement)
36
- // delegate(document, formInputClickSelector, "click", handleConfirm)
37
- delegate(document, formInputClickSelector, "click", formSubmitButtonClick)
38
- }
@@ -1,78 +0,0 @@
1
- import { Snapshot, SnapshotRenderer, ErrorRenderer, uuid } from "turbolinks"
2
- import { formSubmitSelector } from "./selectors"
3
- import { matches } from "./utils/dom"
4
- import { delegate } from "./utils/event"
5
-
6
- const nullCallback = function () {}
7
- const nullDelegate = {
8
- viewInvalidated: nullCallback,
9
- viewWillRender: nullCallback,
10
- viewRendered: nullCallback,
11
- }
12
-
13
- const renderWithTurbolinks = (htmlContent) => {
14
- const currentSnapshot = Snapshot.fromHTMLElement(document.documentElement)
15
- const newSnapshot = Snapshot.fromHTMLString(htmlContent)
16
- let renderer = new SnapshotRenderer(currentSnapshot, newSnapshot, false)
17
-
18
- if (!renderer.shouldRender()) {
19
- renderer = new ErrorRenderer(htmlContent)
20
- }
21
-
22
- renderer.delegate = nullDelegate
23
- renderer.render(nullCallback)
24
- }
25
-
26
- const findActiveElement = (shadowRoot: ShadowRoot) => {
27
- let el = shadowRoot.activeElement
28
-
29
- while (el && el.shadowRoot && el.shadowRoot.activeElement) {
30
- el = el.shadowRoot.activeElement
31
- }
32
-
33
- return el
34
- }
35
-
36
- export const addShadowDomSupportToTurbolinks = (turbolinksController) => {
37
- // From https://github.com/turbolinks/turbolinks/blob/71b7a7d0546a573735af99113b622180e8a813c2/src/util.ts#L9
38
- // © 2019 Basecamp, LLC.
39
- const originalClosest: typeof turbolinksController.closest = turbolinksController.closest
40
-
41
- turbolinksController.closest = (node, selector) => {
42
- if (!!node.shadowRoot) {
43
- const rootActiveElement = findActiveElement(node.shadowRoot) || node
44
-
45
- if (matches(rootActiveElement, selector)) {
46
- return rootActiveElement
47
- }
48
- } else {
49
- return originalClosest(node, selector)
50
- }
51
- }
52
- }
53
-
54
- // Turbolinks does not automatically handle form responses. This creates a handler that does it.
55
- // From https://github.com/turbolinks/turbolinks/issues/85#issuecomment-299617076
56
- export const handleResponse = (turbolinksInstance) => {
57
- return (event: CustomEvent<[XMLHttpRequest, string]>) => {
58
- const [xhr, _status] = event.detail
59
-
60
- if (xhr.getResponseHeader("Content-Type").startsWith("text/html")) {
61
- turbolinksInstance.restorationIdentifier = uuid()
62
- turbolinksInstance.clearCache()
63
- turbolinksInstance.dispatch("turbolinks:before-cache")
64
- turbolinksInstance.controller.pushHistoryWithLocationAndRestorationIdentifier(
65
- xhr.responseURL,
66
- turbolinksInstance.restorationIdentifier
67
- )
68
- renderWithTurbolinks(xhr.responseText)
69
- window.scroll(0, 0)
70
- turbolinksInstance.dispatch("turbolinks:load")
71
- }
72
- }
73
- }
74
-
75
- export const startTurbolinks = (turbolinksInstance) => {
76
- delegate(document, formSubmitSelector, "ajax:complete", handleResponse(turbolinksInstance))
77
- addShadowDomSupportToTurbolinks(turbolinksInstance)
78
- }
@@ -1,146 +0,0 @@
1
- // This code was heavily inspired by the rails-ujs project.
2
- // Copyright (c) 2007-2021 Rails Core team.
3
- import { cspNonce } from "./csp"
4
- import { CSRFProtection } from "./csrf"
5
-
6
- const AcceptHeaders = {
7
- "*": "*/*",
8
- text: "text/plain",
9
- html: "text/html",
10
- xml: "application/xml, text/xml",
11
- json: "application/json, text/javascript",
12
- script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript",
13
- }
14
-
15
- export const ajax = (options) => {
16
- options = prepareOptions(options)
17
- var xhr = createXHR(options, function () {
18
- const response = processResponse(
19
- xhr.response != null ? xhr.response : xhr.responseText,
20
- xhr.getResponseHeader("Content-Type")
21
- )
22
-
23
- if (Math.floor(xhr.status / 100) === 2) {
24
- if (typeof options.success === "function") {
25
- options.success(response, xhr.statusText, xhr)
26
- }
27
- } else {
28
- if (typeof options.error === "function") {
29
- options.error(response, xhr.statusText, xhr)
30
- }
31
- }
32
- return typeof options.complete === "function" ? options.complete(xhr, xhr.statusText) : undefined
33
- })
34
-
35
- if (options.beforeSend != null && !options.beforeSend(xhr, options)) {
36
- return false
37
- }
38
-
39
- if (xhr.readyState === XMLHttpRequest.OPENED) {
40
- return xhr.send(options.data)
41
- }
42
- }
43
-
44
- const prepareOptions = (options) => {
45
- options.url = options.url || location.href
46
- options.type = options.type.toUpperCase()
47
- // append data to url if it's a GET request
48
- if (options.type === "GET" && options.data) {
49
- if (options.url.indexOf("?") < 0) {
50
- options.url += "?" + options.data
51
- } else {
52
- options.url += "&" + options.data
53
- }
54
- }
55
-
56
- // Use "*" as default dataType
57
- if (AcceptHeaders[options.dataType] == null) {
58
- options.dataType = "*"
59
- }
60
-
61
- options.accept = AcceptHeaders[options.dataType]
62
- if (options.dataType !== "*") {
63
- options.accept += ", */*; q=0.01"
64
- }
65
-
66
- return options
67
- }
68
-
69
- const createXHR = (options, done) => {
70
- const xhr = new XMLHttpRequest()
71
-
72
- // Open and set up xhr
73
- xhr.open(options.type, options.url, true)
74
- xhr.setRequestHeader("Accept", options.accept)
75
-
76
- // Set Content-Type only when sending a string
77
- // Sending FormData will automatically set Content-Type to multipart/form-data
78
- if (typeof options.data === "string") {
79
- xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
80
- }
81
-
82
- if (!options.crossDomain) {
83
- xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest")
84
- CSRFProtection(xhr)
85
- }
86
-
87
- xhr.withCredentials = !!options.withCredentials
88
- xhr.onreadystatechange = function () {
89
- if (xhr.readyState === XMLHttpRequest.DONE) {
90
- return done(xhr)
91
- }
92
- }
93
- return xhr
94
- }
95
-
96
- const processResponse = (response, type) => {
97
- if (typeof response === "string" && typeof type === "string") {
98
- if (type.match(/\bjson\b/)) {
99
- try {
100
- response = JSON.parse(response)
101
- } catch (error) {
102
- // no-op...
103
- }
104
- } else if (type.match(/\b(?:java|ecma)script\b/)) {
105
- const script = document.createElement("script")
106
- script.setAttribute("nonce", cspNonce())
107
- script.text = response
108
- document.head.appendChild(script).parentNode.removeChild(script)
109
- } else if (type.match(/\b(xml|html|svg)\b/)) {
110
- const parser = new DOMParser()
111
- type = type.replace(/;.+/, "") // remove something like ';charset=utf-8'
112
-
113
- try {
114
- response = parser.parseFromString(response, type)
115
- } catch (error1) {}
116
- }
117
- }
118
-
119
- return response
120
- }
121
-
122
- // Default way to get an element's href. May be overridden at Rails.href.
123
- export const href = (element) => element.href
124
-
125
- // Determines if the request is a cross domain request.
126
- export const isCrossDomain = (url) => {
127
- const originAnchor = document.createElement("a")
128
- originAnchor.href = location.href
129
- const urlAnchor = document.createElement("a")
130
-
131
- try {
132
- urlAnchor.href = url
133
- // If URL protocol is false or is a string containing a single colon
134
- // *and* host are false, assume it is not a cross-domain request
135
- // (should only be the case for IE7 and IE compatibility mode).
136
- // Otherwise, evaluate protocol and host of the URL against the origin
137
- // protocol and host.
138
- return !(
139
- ((!urlAnchor.protocol || urlAnchor.protocol === ":") && !urlAnchor.host) ||
140
- originAnchor.protocol + "//" + originAnchor.host === urlAnchor.protocol + "//" + urlAnchor.host
141
- )
142
- } catch (e) {
143
- // If there is an error parsing the URL, assume it is crossDomain.
144
- return true
145
- }
146
- }
@@ -1,20 +0,0 @@
1
- // This code was heavily inspired by the rails-ujs project.
2
- // Copyright (c) 2007-2021 Rails Core team.
3
- let nonce = null
4
-
5
- const loadCSPNonce = () => {
6
- if (nonce) {
7
- return nonce
8
- }
9
-
10
- const cspMetaTag: HTMLMetaElement = document.querySelector("meta[name=csp-nonce]")
11
-
12
- if (cspMetaTag) {
13
- nonce = cspMetaTag.content
14
- }
15
-
16
- return nonce
17
- }
18
-
19
- // Returns the Content-Security-Policy nonce for inline scripts.
20
- export const cspNonce = () => (nonce != null ? nonce : loadCSPNonce())
@@ -1,33 +0,0 @@
1
- // This code was heavily inspired by the rails-ujs project.
2
- // Copyright (c) 2007-2021 Rails Core team.
3
- const $ = (selector) => Array.prototype.slice.call(document.querySelectorAll(selector))
4
-
5
- // Up-to-date Cross-Site Request Forgery token
6
- export const csrfToken = () => {
7
- const meta: HTMLMetaElement = document.querySelector("meta[name=csrf-token]")
8
- return meta && meta.content
9
- }
10
-
11
- // URL param that must contain the CSRF token
12
- export const csrfParam = () => {
13
- const meta: HTMLMetaElement = document.querySelector("meta[name=csrf-param]")
14
- return meta && meta.content
15
- }
16
-
17
- // Make sure that every Ajax request sends the CSRF token
18
- export const CSRFProtection = (xhr) => {
19
- const token = csrfToken()
20
- if (token != null) {
21
- return xhr.setRequestHeader("X-CSRF-Token", token)
22
- }
23
- }
24
-
25
- // Make sure that all forms have actual up-to-date tokens (cached forms contain old ones)
26
- export const refreshCSRFTokens = () => {
27
- const token = csrfToken()
28
- const param = csrfParam()
29
-
30
- if (token != null && param != null) {
31
- return $('sl-form input[name="' + param + '"]').forEach((input) => (input.value = token))
32
- }
33
- }
@@ -1,40 +0,0 @@
1
- // This code was heavily inspired by the rails-ujs project.
2
- // Copyright (c) 2007-2021 Rails Core team.
3
- const elementPrototype = Element.prototype as any
4
-
5
- const m: (this: Element, selector: string) => boolean =
6
- elementPrototype.matches ||
7
- elementPrototype.matchesSelector ||
8
- elementPrototype.mozMatchesSelector ||
9
- elementPrototype.msMatchesSelector ||
10
- elementPrototype.oMatchesSelector ||
11
- elementPrototype.webkitMatchesSelector
12
-
13
- // Checks if the given native dom element matches the selector
14
- // element::
15
- // native DOM element
16
- // selector::
17
- // CSS selector string or
18
- // a JavaScript object with `selector` and `exclude` properties
19
- // Examples: "form", { selector: "form", exclude: "form[data-remote='true']"}
20
- export const matches = (element, selector) => {
21
- if (selector.exclude != null) {
22
- return m.call(element, selector.selector) && !m.call(element, selector.exclude)
23
- } else {
24
- return m.call(element, selector)
25
- }
26
- }
27
-
28
- // get and set data on a given element using "expando properties"
29
- // See: https://developer.mozilla.org/en-US/docs/Glossary/Expando
30
- const expando = "_ujsData"
31
-
32
- export const getData = (element, key) => (element[expando] != null ? element[expando][key] : undefined)
33
-
34
- export const setData = (element, key, value) => {
35
- if (element[expando] == null) {
36
- element[expando] = {}
37
- }
38
-
39
- return (element[expando][key] = value)
40
- }
@@ -1,57 +0,0 @@
1
- // This code was heavily inspired by the rails-ujs project.
2
- // Copyright (c) 2007-2021 Rails Core team.
3
- import { matches } from "./dom"
4
-
5
- // Triggers a custom event on an element and returns false if the event result is false
6
- // obj::
7
- // a native DOM element
8
- // name::
9
- // string that corresponds to the event you want to trigger
10
- // e.g. 'click', 'submit'
11
- // data::
12
- // data you want to pass when you dispatch an event
13
- export const fire = (obj, name, data = {}) => {
14
- const event = new CustomEvent(name, {
15
- bubbles: true,
16
- cancelable: true,
17
- detail: data,
18
- })
19
-
20
- obj.dispatchEvent(event)
21
- return !event.defaultPrevented
22
- }
23
-
24
- // Helper function, needed to provide consistent behavior in IE
25
- export const stopEverything = (event) => {
26
- fire(event.target, "ujs:everythingStopped")
27
-
28
- event.preventDefault()
29
- event.stopPropagation()
30
-
31
- return event.stopImmediatePropagation()
32
- }
33
-
34
- // Delegates events
35
- // to a specified parent `element`, which fires event `handler`
36
- // for the specified `selector` when an event of `eventType` is triggered
37
- // element::
38
- // parent element that will listen for events e.g. document
39
- // selector::
40
- // CSS selector; or an object that has `selector` and `exclude` properties (see: Rails.matches)
41
- // eventType::
42
- // string representing the event e.g. 'submit', 'click'
43
- // handler::
44
- // the event handler to be called
45
- export const delegate = (element, selector, eventType, handler) =>
46
- element.addEventListener(eventType, function (event) {
47
- let { target } = event
48
-
49
- while (!!(target instanceof Element) && !matches(target, selector)) {
50
- target = target.parentNode
51
- }
52
-
53
- if (target instanceof Element && handler.call(target, event) === false) {
54
- event.preventDefault()
55
- return event.stopPropagation()
56
- }
57
- })