turbograft 0.4.2 → 0.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/lib/assets/javascripts/turbograft/document.coffee +11 -0
- data/lib/assets/javascripts/turbograft/page.coffee +10 -10
- data/lib/assets/javascripts/turbograft/remote.coffee +10 -5
- data/lib/assets/javascripts/turbograft/response.coffee +31 -0
- data/lib/assets/javascripts/turbograft/turbohead.coffee +7 -2
- data/lib/assets/javascripts/turbograft/turbolinks.coffee +76 -90
- data/lib/turbograft.rb +12 -10
- data/lib/turbograft/redirection.rb +1 -2
- data/lib/turbograft/version.rb +1 -1
- data/lib/turbograft/xhr_headers.rb +24 -15
- metadata +13 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 151e2b8deeb6da6a8e1b5c732e236c8db0df7427c3bf7f763b9eecf863d1bbe6
|
|
4
|
+
data.tar.gz: 4ffff77fc18c82fb496446492617a6acab056e767afc77c660208eb6cd26630f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fbe47332102f1fff3ebd2deb9c706e47f463f99d51fdb7ba35c9acceaca030e2c04a50eb227671c47a7c4231086e403f2fbcafaabfdb034129f92453f0ea01b2
|
|
7
|
+
data.tar.gz: d10f83071df4a30ae08b8cf1327e20119bbba40297eac5496e32cd3f84bcf00be2bdd330d0dda5d7ba96e629dc4cf5b42b48868b7679ffe94a2bc6af9e31948e
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
TurboGraft.Document =
|
|
2
|
+
create: (html) ->
|
|
3
|
+
if /<(html|body)/i.test(html)
|
|
4
|
+
doc = document.documentElement.cloneNode()
|
|
5
|
+
doc.innerHTML = html
|
|
6
|
+
else
|
|
7
|
+
doc = document.documentElement.cloneNode(true)
|
|
8
|
+
doc.querySelector('body').innerHTML = html
|
|
9
|
+
doc.head = doc.querySelector('head')
|
|
10
|
+
doc.body = doc.querySelector('body')
|
|
11
|
+
doc
|
|
@@ -16,18 +16,18 @@ Page.refresh = (options = {}, callback) ->
|
|
|
16
16
|
else
|
|
17
17
|
location.href
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
options.
|
|
19
|
+
turboOptions = {
|
|
20
|
+
partialReplace: true,
|
|
21
|
+
exceptKeys: options.exceptKeys,
|
|
22
|
+
onlyKeys: options.onlyKeys,
|
|
23
|
+
updatePushState: options.updatePushState,
|
|
24
|
+
callback: callback
|
|
25
|
+
}
|
|
22
26
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
Turbolinks.loadPage null, xhr, options
|
|
27
|
+
if xhr = options.response
|
|
28
|
+
Turbolinks.loadPage null, xhr, turboOptions
|
|
26
29
|
else
|
|
27
|
-
|
|
28
|
-
options.callback = callback if callback
|
|
29
|
-
|
|
30
|
-
Turbolinks.visit newUrl, options
|
|
30
|
+
Turbolinks.visit newUrl, turboOptions
|
|
31
31
|
|
|
32
32
|
Page.open = ->
|
|
33
33
|
window.open(arguments...)
|
|
@@ -134,16 +134,19 @@ class TurboGraft.Remote
|
|
|
134
134
|
else if @opts.fullRefresh
|
|
135
135
|
Page.refresh()
|
|
136
136
|
else if @refreshOnSuccess
|
|
137
|
-
Page.refresh
|
|
137
|
+
Page.refresh(
|
|
138
138
|
response: xhr
|
|
139
139
|
onlyKeys: @refreshOnSuccess
|
|
140
|
+
)
|
|
140
141
|
else if @refreshOnSuccessExcept
|
|
141
|
-
Page.refresh
|
|
142
|
+
Page.refresh(
|
|
142
143
|
response: xhr
|
|
143
144
|
exceptKeys: @refreshOnSuccessExcept
|
|
145
|
+
)
|
|
144
146
|
else
|
|
145
|
-
Page.refresh
|
|
147
|
+
Page.refresh(
|
|
146
148
|
response: xhr
|
|
149
|
+
)
|
|
147
150
|
|
|
148
151
|
onError: (ev) =>
|
|
149
152
|
@opts.fail?()
|
|
@@ -162,13 +165,15 @@ class TurboGraft.Remote
|
|
|
162
165
|
else if @opts.fullRefresh
|
|
163
166
|
Page.refresh()
|
|
164
167
|
else if @refreshOnError
|
|
165
|
-
Page.refresh
|
|
168
|
+
Page.refresh(
|
|
166
169
|
response: xhr
|
|
167
170
|
onlyKeys: @refreshOnError
|
|
171
|
+
)
|
|
168
172
|
else if @refreshOnErrorExcept
|
|
169
|
-
Page.refresh
|
|
173
|
+
Page.refresh(
|
|
170
174
|
response: xhr
|
|
171
175
|
exceptKeys: @refreshOnErrorExcept
|
|
176
|
+
)
|
|
172
177
|
else
|
|
173
178
|
triggerEventFor 'turbograft:remote:fail:unhandled', @initiator,
|
|
174
179
|
xhr: xhr
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
class TurboGraft.Response
|
|
2
|
+
constructor: (@xhr, intendedURL) ->
|
|
3
|
+
if intendedURL && intendedURL.withoutHash() != @xhr.responseURL
|
|
4
|
+
redirectedTo = @xhr.responseURL
|
|
5
|
+
else
|
|
6
|
+
redirectedTo = @xhr.getResponseHeader('X-XHR-Redirected-To')
|
|
7
|
+
|
|
8
|
+
@finalURL = redirectedTo || intendedURL
|
|
9
|
+
|
|
10
|
+
valid: -> @hasRenderableHttpStatus() && @hasValidContent()
|
|
11
|
+
|
|
12
|
+
document: ->
|
|
13
|
+
if @valid()
|
|
14
|
+
TurboGraft.Document.create(@xhr.responseText)
|
|
15
|
+
|
|
16
|
+
hasRenderableHttpStatus: ->
|
|
17
|
+
return true if @xhr.status == 422 # we want to render form validations
|
|
18
|
+
!(400 <= @xhr.status < 600)
|
|
19
|
+
|
|
20
|
+
hasValidContent: ->
|
|
21
|
+
if contentType = @xhr.getResponseHeader('Content-Type')
|
|
22
|
+
contentType.match(/^(?:text\/html|application\/xhtml\+xml|application\/xml)(?:;|$)/)
|
|
23
|
+
else
|
|
24
|
+
throw new Error("Error encountered for XHR Response: #{this}")
|
|
25
|
+
|
|
26
|
+
toString: () ->
|
|
27
|
+
"URL: #{@xhr.responseURL}, " +
|
|
28
|
+
"ReadyState: #{@xhr.readyState}, " +
|
|
29
|
+
"Headers: #{@xhr.getAllResponseHeaders()}"
|
|
30
|
+
|
|
31
|
+
TurboGraft.location = () -> location.href
|
|
@@ -10,7 +10,7 @@ waitForCompleteDownloads = ->
|
|
|
10
10
|
scriptPromises[url]
|
|
11
11
|
Promise.all(loadingPromises)
|
|
12
12
|
|
|
13
|
-
class
|
|
13
|
+
class TurboGraft.TurboHead
|
|
14
14
|
constructor: (@activeDocument, @upstreamDocument) ->
|
|
15
15
|
@activeAssets = extractTrackedAssets(@activeDocument)
|
|
16
16
|
@upstreamAssets = extractTrackedAssets(@upstreamDocument)
|
|
@@ -44,6 +44,9 @@ class window.TurboHead
|
|
|
44
44
|
noMatchingSrc(node) || noMatchingHref(node)
|
|
45
45
|
)
|
|
46
46
|
|
|
47
|
+
movingFromTrackedToUntracked: () ->
|
|
48
|
+
@upstreamAssets.length == 0 && @activeAssets.length > 0
|
|
49
|
+
|
|
47
50
|
hasNamedAssetConflicts: () ->
|
|
48
51
|
@newScripts
|
|
49
52
|
.concat(@newLinks)
|
|
@@ -51,7 +54,9 @@ class window.TurboHead
|
|
|
51
54
|
.some(datasetMatchesIn(TRACKED_ATTRIBUTE_NAME, @activeAssets))
|
|
52
55
|
|
|
53
56
|
hasAssetConflicts: () ->
|
|
54
|
-
@
|
|
57
|
+
@movingFromTrackedToUntracked() ||
|
|
58
|
+
@hasNamedAssetConflicts() ||
|
|
59
|
+
@hasChangedAnonymousAssets()
|
|
55
60
|
|
|
56
61
|
waitForAssets: () ->
|
|
57
62
|
resolvePreviousRequest?(isCanceled: true)
|
|
@@ -1,14 +1,19 @@
|
|
|
1
|
+
Response = TurboGraft.Response
|
|
2
|
+
TurboHead = TurboGraft.TurboHead
|
|
3
|
+
jQuery = window.jQuery
|
|
4
|
+
|
|
1
5
|
xhr = null
|
|
6
|
+
activeDocument = document
|
|
2
7
|
|
|
3
8
|
installDocumentReadyPageEventTriggers = ->
|
|
4
|
-
|
|
9
|
+
activeDocument.addEventListener 'DOMContentLoaded', ( ->
|
|
5
10
|
triggerEvent 'page:change'
|
|
6
11
|
triggerEvent 'page:update'
|
|
7
12
|
), true
|
|
8
13
|
|
|
9
14
|
installJqueryAjaxSuccessPageUpdateTrigger = ->
|
|
10
15
|
if typeof jQuery isnt 'undefined'
|
|
11
|
-
jQuery(
|
|
16
|
+
jQuery(activeDocument).on 'ajaxSuccess', (event, xhr, settings) ->
|
|
12
17
|
return unless jQuery.trim xhr.responseText
|
|
13
18
|
triggerEvent 'page:update'
|
|
14
19
|
|
|
@@ -20,20 +25,20 @@ browserSupportsPushState =
|
|
|
20
25
|
window.history and window.history.pushState and window.history.replaceState and historyStateIsDefined
|
|
21
26
|
|
|
22
27
|
window.triggerEvent = (name, data) ->
|
|
23
|
-
event =
|
|
28
|
+
event = activeDocument.createEvent 'Events'
|
|
24
29
|
event.data = data if data
|
|
25
30
|
event.initEvent name, true, true
|
|
26
|
-
|
|
31
|
+
activeDocument.dispatchEvent event
|
|
27
32
|
|
|
28
33
|
window.triggerEventFor = (name, node, data) ->
|
|
29
|
-
event =
|
|
34
|
+
event = activeDocument.createEvent 'Events'
|
|
30
35
|
event.data = data if data
|
|
31
36
|
event.initEvent name, true, true
|
|
32
37
|
node.dispatchEvent event
|
|
33
38
|
|
|
34
39
|
popCookie = (name) ->
|
|
35
|
-
value =
|
|
36
|
-
|
|
40
|
+
value = activeDocument.cookie.match(new RegExp(name+"=(\\w+)"))?[1].toUpperCase() or ''
|
|
41
|
+
activeDocument.cookie = name + '=; expires=Thu, 01-Jan-70 00:00:01 GMT; path=/'
|
|
37
42
|
value
|
|
38
43
|
|
|
39
44
|
requestMethodIsSafe =
|
|
@@ -42,7 +47,7 @@ requestMethodIsSafe =
|
|
|
42
47
|
browserSupportsTurbolinks = browserSupportsPushState and requestMethodIsSafe
|
|
43
48
|
|
|
44
49
|
browserSupportsCustomEvents =
|
|
45
|
-
|
|
50
|
+
activeDocument.addEventListener and activeDocument.createEvent
|
|
46
51
|
|
|
47
52
|
if browserSupportsCustomEvents
|
|
48
53
|
installDocumentReadyPageEventTriggers()
|
|
@@ -61,27 +66,30 @@ removeNode = (node) ->
|
|
|
61
66
|
# TODO: clean up everything above me ^
|
|
62
67
|
# TODO: decide on the public API
|
|
63
68
|
class window.Turbolinks
|
|
64
|
-
createDocument = null
|
|
65
69
|
currentState = null
|
|
66
70
|
referer = null
|
|
67
71
|
|
|
68
72
|
fetch = (url, options = {}) ->
|
|
69
73
|
return if pageChangePrevented(url)
|
|
70
|
-
url = new ComponentUrl
|
|
74
|
+
url = new ComponentUrl(url)
|
|
71
75
|
|
|
72
76
|
rememberReferer()
|
|
73
77
|
|
|
74
|
-
options
|
|
75
|
-
options.onlyKeys ?= []
|
|
76
|
-
options.onLoadFunction = ->
|
|
77
|
-
resetScrollPosition() unless options.onlyKeys.length
|
|
78
|
-
options.callback?()
|
|
78
|
+
fetchReplacement(url, options)
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
isPartialReplace = (response, options) ->
|
|
81
|
+
Boolean(
|
|
82
|
+
options.partialReplace ||
|
|
83
|
+
options.onlyKeys?.length ||
|
|
84
|
+
options.exceptKeys?.length
|
|
85
|
+
)
|
|
81
86
|
|
|
82
87
|
@fullPageNavigate: (url) ->
|
|
83
|
-
|
|
84
|
-
|
|
88
|
+
if url?
|
|
89
|
+
url = (new ComponentUrl(url)).absolute
|
|
90
|
+
triggerEvent('page:before-full-refresh', url: url)
|
|
91
|
+
activeDocument.location.href = url
|
|
92
|
+
return
|
|
85
93
|
|
|
86
94
|
@pushState: (state, title, url) ->
|
|
87
95
|
window.history.pushState(state, title, url)
|
|
@@ -89,6 +97,10 @@ class window.Turbolinks
|
|
|
89
97
|
@replaceState: (state, title, url) ->
|
|
90
98
|
window.history.replaceState(state, title, url)
|
|
91
99
|
|
|
100
|
+
@document: (documentToUse) ->
|
|
101
|
+
activeDocument = documentToUse if documentToUse
|
|
102
|
+
activeDocument
|
|
103
|
+
|
|
92
104
|
fetchReplacement = (url, options) ->
|
|
93
105
|
triggerEvent 'page:fetch', url: url.absolute
|
|
94
106
|
|
|
@@ -111,7 +123,7 @@ class window.Turbolinks
|
|
|
111
123
|
|
|
112
124
|
xhr.onload = ->
|
|
113
125
|
if xhr.status >= 500
|
|
114
|
-
Turbolinks.fullPageNavigate(url
|
|
126
|
+
Turbolinks.fullPageNavigate(url)
|
|
115
127
|
else
|
|
116
128
|
Turbolinks.loadPage(url, xhr, options)
|
|
117
129
|
xhr = null
|
|
@@ -121,7 +133,7 @@ class window.Turbolinks
|
|
|
121
133
|
if xhr.statusText == "abort"
|
|
122
134
|
xhr = null
|
|
123
135
|
return
|
|
124
|
-
Turbolinks.fullPageNavigate(url
|
|
136
|
+
Turbolinks.fullPageNavigate(url)
|
|
125
137
|
|
|
126
138
|
xhr.send()
|
|
127
139
|
|
|
@@ -129,24 +141,28 @@ class window.Turbolinks
|
|
|
129
141
|
|
|
130
142
|
@loadPage: (url, xhr, options = {}) ->
|
|
131
143
|
triggerEvent 'page:receive'
|
|
144
|
+
response = new Response(xhr, url)
|
|
132
145
|
options.updatePushState ?= true
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
updateBody(upstreamDocument, xhr, options)
|
|
137
|
-
else
|
|
138
|
-
turbohead = new TurboHead(document, upstreamDocument)
|
|
139
|
-
if turbohead.hasAssetConflicts()
|
|
140
|
-
return Turbolinks.fullPageNavigate(url.absolute)
|
|
141
|
-
reflectNewUrl url if options.updatePushState
|
|
142
|
-
turbohead.waitForAssets().then((result) ->
|
|
143
|
-
updateBody(upstreamDocument, xhr, options) unless result?.isCanceled
|
|
144
|
-
)
|
|
145
|
-
else
|
|
146
|
+
options.partialReplace = isPartialReplace(response, options)
|
|
147
|
+
|
|
148
|
+
unless upstreamDocument = response.document()
|
|
146
149
|
triggerEvent 'page:error', xhr
|
|
147
|
-
Turbolinks.fullPageNavigate(
|
|
150
|
+
Turbolinks.fullPageNavigate(response.finalURL)
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
if options.partialReplace
|
|
154
|
+
updateBody(upstreamDocument, response, options)
|
|
155
|
+
return
|
|
156
|
+
|
|
157
|
+
turbohead = new TurboHead(activeDocument, upstreamDocument)
|
|
158
|
+
if turbohead.hasAssetConflicts()
|
|
159
|
+
return Turbolinks.fullPageNavigate(response.finalURL)
|
|
160
|
+
|
|
161
|
+
turbohead.waitForAssets().then((result) ->
|
|
162
|
+
updateBody(upstreamDocument, response, options) unless result?.isCanceled
|
|
163
|
+
)
|
|
148
164
|
|
|
149
|
-
updateBody = (upstreamDocument,
|
|
165
|
+
updateBody = (upstreamDocument, response, options) ->
|
|
150
166
|
nodes = changePage(
|
|
151
167
|
upstreamDocument.querySelector('title')?.textContent,
|
|
152
168
|
removeNoscriptTags(upstreamDocument.querySelector('body')),
|
|
@@ -154,16 +170,17 @@ class window.Turbolinks
|
|
|
154
170
|
'runScripts',
|
|
155
171
|
options
|
|
156
172
|
)
|
|
157
|
-
|
|
158
|
-
|
|
173
|
+
reflectNewUrl(response.finalURL) if options.updatePushState
|
|
174
|
+
|
|
175
|
+
Turbolinks.resetScrollPosition() unless options.partialReplace
|
|
176
|
+
|
|
177
|
+
options.callback?()
|
|
159
178
|
triggerEvent 'page:load', nodes
|
|
160
179
|
|
|
161
180
|
changePage = (title, body, csrfToken, runScripts, options = {}) ->
|
|
162
|
-
|
|
163
|
-
options.onlyKeys ?= []
|
|
164
|
-
options.exceptKeys ?= []
|
|
181
|
+
activeDocument.title = title if title
|
|
165
182
|
|
|
166
|
-
if options.onlyKeys
|
|
183
|
+
if options.onlyKeys?.length
|
|
167
184
|
nodesToRefresh = [].concat(getNodesWithRefreshAlways(), getNodesMatchingRefreshKeys(options.onlyKeys))
|
|
168
185
|
nodes = refreshNodes(nodesToRefresh, body)
|
|
169
186
|
setAutofocusElement() if anyAutofocusElement(nodes)
|
|
@@ -171,13 +188,13 @@ class window.Turbolinks
|
|
|
171
188
|
else
|
|
172
189
|
refreshNodes(getNodesWithRefreshAlways(), body)
|
|
173
190
|
persistStaticElements(body)
|
|
174
|
-
if options.exceptKeys
|
|
191
|
+
if options.exceptKeys?.length
|
|
175
192
|
refreshAllExceptWithKeys(options.exceptKeys, body)
|
|
176
193
|
else
|
|
177
194
|
deleteRefreshNeverNodes(body)
|
|
178
195
|
|
|
179
196
|
triggerEvent 'page:before-replace'
|
|
180
|
-
replaceNode(body,
|
|
197
|
+
replaceNode(body, activeDocument.body)
|
|
181
198
|
CSRFToken.update csrfToken if csrfToken?
|
|
182
199
|
setAutofocusElement()
|
|
183
200
|
executeScriptTags() if runScripts
|
|
@@ -190,14 +207,14 @@ class window.Turbolinks
|
|
|
190
207
|
getNodesMatchingRefreshKeys = (keys) ->
|
|
191
208
|
matchingNodes = []
|
|
192
209
|
for key in keys
|
|
193
|
-
for node in TurboGraft.querySelectorAllTGAttribute(
|
|
210
|
+
for node in TurboGraft.querySelectorAllTGAttribute(activeDocument, 'refresh', key)
|
|
194
211
|
matchingNodes.push(node)
|
|
195
212
|
|
|
196
213
|
return matchingNodes
|
|
197
214
|
|
|
198
215
|
getNodesWithRefreshAlways = ->
|
|
199
216
|
matchingNodes = []
|
|
200
|
-
for node in TurboGraft.querySelectorAllTGAttribute(
|
|
217
|
+
for node in TurboGraft.querySelectorAllTGAttribute(activeDocument, 'refresh-always')
|
|
201
218
|
matchingNodes.push(node)
|
|
202
219
|
|
|
203
220
|
return matchingNodes
|
|
@@ -210,8 +227,8 @@ class window.Turbolinks
|
|
|
210
227
|
false
|
|
211
228
|
|
|
212
229
|
setAutofocusElement = ->
|
|
213
|
-
autofocusElement = (list =
|
|
214
|
-
if autofocusElement and
|
|
230
|
+
autofocusElement = (list = activeDocument.querySelectorAll 'input[autofocus], textarea[autofocus]')[list.length - 1]
|
|
231
|
+
if autofocusElement and activeDocument.activeElement isnt autofocusElement
|
|
215
232
|
autofocusElement.focus()
|
|
216
233
|
|
|
217
234
|
deleteRefreshNeverNodes = (body) ->
|
|
@@ -260,7 +277,7 @@ class window.Turbolinks
|
|
|
260
277
|
persistStaticElements = (body) ->
|
|
261
278
|
allNodesToKeep = []
|
|
262
279
|
|
|
263
|
-
nodes = TurboGraft.querySelectorAllTGAttribute(
|
|
280
|
+
nodes = TurboGraft.querySelectorAllTGAttribute(activeDocument, 'tg-static')
|
|
264
281
|
allNodesToKeep.push(node) for node in nodes
|
|
265
282
|
|
|
266
283
|
keepNodes(body, allNodesToKeep)
|
|
@@ -270,22 +287,22 @@ class window.Turbolinks
|
|
|
270
287
|
allNodesToKeep = []
|
|
271
288
|
|
|
272
289
|
for key in keys
|
|
273
|
-
for node in TurboGraft.querySelectorAllTGAttribute(
|
|
290
|
+
for node in TurboGraft.querySelectorAllTGAttribute(activeDocument, 'refresh', key)
|
|
274
291
|
allNodesToKeep.push(node)
|
|
275
292
|
|
|
276
293
|
keepNodes(body, allNodesToKeep)
|
|
277
294
|
return
|
|
278
295
|
|
|
279
296
|
executeScriptTags = ->
|
|
280
|
-
scripts = Array::slice.call
|
|
297
|
+
scripts = Array::slice.call activeDocument.body.querySelectorAll 'script:not([data-turbolinks-eval="false"])'
|
|
281
298
|
for script in scripts when script.type in ['', 'text/javascript']
|
|
282
299
|
executeScriptTag(script)
|
|
283
300
|
return
|
|
284
301
|
|
|
285
302
|
executeScriptTag = (script) ->
|
|
286
|
-
copy =
|
|
303
|
+
copy = activeDocument.createElement 'script'
|
|
287
304
|
copy.setAttribute attr.name, attr.value for attr in script.attributes
|
|
288
|
-
copy.appendChild
|
|
305
|
+
copy.appendChild activeDocument.createTextNode script.innerHTML
|
|
289
306
|
{ parentNode, nextSibling } = script
|
|
290
307
|
parentNode.removeChild script
|
|
291
308
|
parentNode.insertBefore copy, nextSibling
|
|
@@ -300,19 +317,11 @@ class window.Turbolinks
|
|
|
300
317
|
Turbolinks.pushState { turbolinks: true, url: url.absolute }, '', url.absolute
|
|
301
318
|
return
|
|
302
319
|
|
|
303
|
-
reflectRedirectedUrl = (xhr) ->
|
|
304
|
-
if location = xhr.getResponseHeader 'X-XHR-Redirected-To'
|
|
305
|
-
location = new ComponentUrl location
|
|
306
|
-
preservedHash = if location.hasNoHash() then document.location.hash else ''
|
|
307
|
-
Turbolinks.replaceState currentState, '', location.href + preservedHash
|
|
308
|
-
|
|
309
|
-
return
|
|
310
|
-
|
|
311
320
|
rememberReferer = ->
|
|
312
|
-
referer =
|
|
321
|
+
referer = activeDocument.location.href
|
|
313
322
|
|
|
314
323
|
@rememberCurrentUrl: ->
|
|
315
|
-
Turbolinks.replaceState { turbolinks: true, url:
|
|
324
|
+
Turbolinks.replaceState { turbolinks: true, url: activeDocument.location.href }, '', activeDocument.location.href
|
|
316
325
|
|
|
317
326
|
@rememberCurrentState: ->
|
|
318
327
|
currentState = window.history.state
|
|
@@ -320,27 +329,15 @@ class window.Turbolinks
|
|
|
320
329
|
recallScrollPosition = (page) ->
|
|
321
330
|
window.scrollTo page.positionX, page.positionY
|
|
322
331
|
|
|
323
|
-
resetScrollPosition
|
|
324
|
-
if
|
|
325
|
-
|
|
332
|
+
@resetScrollPosition: ->
|
|
333
|
+
if activeDocument.location.hash
|
|
334
|
+
activeDocument.location.href = activeDocument.location.href
|
|
326
335
|
else
|
|
327
336
|
window.scrollTo 0, 0
|
|
328
337
|
|
|
329
338
|
pageChangePrevented = (url) ->
|
|
330
339
|
!triggerEvent('page:before-change', url)
|
|
331
340
|
|
|
332
|
-
processResponse = (xhr) ->
|
|
333
|
-
clientOrServerError = ->
|
|
334
|
-
return false if xhr.status == 422 # we want to render form validations
|
|
335
|
-
400 <= xhr.status < 600
|
|
336
|
-
|
|
337
|
-
validContent = ->
|
|
338
|
-
xhr.getResponseHeader('Content-Type').match /^(?:text\/html|application\/xhtml\+xml|application\/xml)(?:;|$)/
|
|
339
|
-
|
|
340
|
-
if !clientOrServerError() && validContent()
|
|
341
|
-
upstreamDocument = createDocument(xhr.responseText)
|
|
342
|
-
return upstreamDocument
|
|
343
|
-
|
|
344
341
|
installHistoryChangeHandler = (event) ->
|
|
345
342
|
if event.state?.turbolinks
|
|
346
343
|
Turbolinks.visit event.target.location.href
|
|
@@ -350,26 +347,15 @@ class window.Turbolinks
|
|
|
350
347
|
bypassOnLoadPopstate = (fn) ->
|
|
351
348
|
setTimeout fn, 500
|
|
352
349
|
|
|
353
|
-
createDocument = (html) ->
|
|
354
|
-
if /<(html|body)/i.test(html)
|
|
355
|
-
doc = document.documentElement.cloneNode()
|
|
356
|
-
doc.innerHTML = html
|
|
357
|
-
else
|
|
358
|
-
doc = document.documentElement.cloneNode(true)
|
|
359
|
-
doc.querySelector('body').innerHTML = html
|
|
360
|
-
doc.head = doc.querySelector('head')
|
|
361
|
-
doc.body = doc.querySelector('body')
|
|
362
|
-
doc
|
|
363
|
-
|
|
364
350
|
if browserSupportsTurbolinks
|
|
365
351
|
@visit = fetch
|
|
366
352
|
@rememberCurrentUrl()
|
|
367
353
|
@rememberCurrentState()
|
|
368
354
|
|
|
369
|
-
|
|
355
|
+
activeDocument.addEventListener 'click', Click.installHandlerLast, true
|
|
370
356
|
|
|
371
357
|
bypassOnLoadPopstate ->
|
|
372
358
|
window.addEventListener 'popstate', installHistoryChangeHandler, false
|
|
373
359
|
|
|
374
360
|
else
|
|
375
|
-
@visit = (url) ->
|
|
361
|
+
@visit = (url) -> activeDocument.location.href = url
|
data/lib/turbograft.rb
CHANGED
|
@@ -14,16 +14,20 @@ module TurboGraft
|
|
|
14
14
|
self.controllers = ["ActionController::Base"]
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
+
def self.included(controller)
|
|
18
|
+
controller.class_eval do
|
|
19
|
+
include XHRHeaders, Cookies, XDomainBlocker, Redirection
|
|
20
|
+
before_action :set_xhr_redirected_to, :set_request_method_cookie
|
|
21
|
+
after_action :abort_xdomain_redirect
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
17
25
|
class Engine < ::Rails::Engine
|
|
18
26
|
|
|
19
27
|
initializer :turbograft do |config|
|
|
20
28
|
ActiveSupport.on_load(:action_controller) do
|
|
21
|
-
Config.controllers.each do |
|
|
22
|
-
|
|
23
|
-
include XHRHeaders, Cookies, XDomainBlocker, Redirection
|
|
24
|
-
before_action :set_xhr_redirected_to, :set_request_method_cookie
|
|
25
|
-
after_action :abort_xdomain_redirect
|
|
26
|
-
end
|
|
29
|
+
Config.controllers.each do |class_name|
|
|
30
|
+
class_name.constantize.include(::TurboGraft)
|
|
27
31
|
end
|
|
28
32
|
|
|
29
33
|
ActionDispatch::Request.class_eval do
|
|
@@ -35,10 +39,8 @@ module TurboGraft
|
|
|
35
39
|
end
|
|
36
40
|
|
|
37
41
|
ActiveSupport.on_load(:action_view) do
|
|
38
|
-
(ActionView::RoutingUrlFor rescue ActionView::Helpers::UrlHelper).
|
|
39
|
-
|
|
40
|
-
end
|
|
41
|
-
end unless RUBY_VERSION =~ /^1\.8/
|
|
42
|
+
(ActionView::RoutingUrlFor rescue ActionView::Helpers::UrlHelper).prepend(XHRUrlFor)
|
|
43
|
+
end
|
|
42
44
|
end
|
|
43
45
|
end
|
|
44
46
|
end
|
|
@@ -7,10 +7,9 @@ module TurboGraft
|
|
|
7
7
|
private
|
|
8
8
|
def redirect_via_turbolinks_to(url = {}, response_status = {})
|
|
9
9
|
redirect_to(url, response_status)
|
|
10
|
-
|
|
11
10
|
self.status = 200
|
|
12
11
|
self.response_body = "Turbolinks.visit('#{location}');"
|
|
13
|
-
response.content_type = Mime
|
|
12
|
+
response.content_type = Mime[:js]
|
|
14
13
|
end
|
|
15
14
|
end
|
|
16
15
|
end
|
data/lib/turbograft/version.rb
CHANGED
|
@@ -25,23 +25,32 @@ module TurboGraft
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
private
|
|
28
|
-
def store_for_turbolinks(url)
|
|
29
|
-
session[:_turbolinks_redirect_to] = url if session && request.headers["X-XHR-Referer"]
|
|
30
|
-
url
|
|
31
|
-
end
|
|
32
28
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
# Ensure backwards compatibility
|
|
30
|
+
# Rails < 4.2: _compute_redirect_to_location(options)
|
|
31
|
+
# Rails >= 4.2: _compute_redirect_to_location(request, options)
|
|
32
|
+
def _normalize_redirect_params(args)
|
|
33
|
+
options, req = args.reverse
|
|
34
|
+
[options, req || request]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def store_for_turbolinks(url)
|
|
38
|
+
session[:_turbolinks_redirect_to] = url if session && request.headers["X-XHR-Referer"]
|
|
39
|
+
url
|
|
40
|
+
end
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def _normalize_redirect_params(args)
|
|
43
|
-
options, req = args.reverse
|
|
44
|
-
[options, req || request]
|
|
42
|
+
def set_xhr_redirected_to
|
|
43
|
+
if turbolinks_redirected? && request_matches_redirect?
|
|
44
|
+
response.headers['X-XHR-Redirected-To'] = session.delete(:_turbolinks_redirect_to)
|
|
45
45
|
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def turbolinks_redirected?
|
|
49
|
+
session && session[:_turbolinks_redirect_to]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def request_matches_redirect?
|
|
53
|
+
session[:_turbolinks_redirect_to] == request.original_url
|
|
54
|
+
end
|
|
46
55
|
end
|
|
47
56
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: turbograft
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kristian Plettenberg-Dussault
|
|
@@ -10,10 +10,12 @@ authors:
|
|
|
10
10
|
- Tyler Mercier
|
|
11
11
|
- Anthony Cameron
|
|
12
12
|
- Patrick Donovan
|
|
13
|
+
- Mathew Allen
|
|
14
|
+
- Gord Pearson
|
|
13
15
|
autorequire:
|
|
14
16
|
bindir: bin
|
|
15
17
|
cert_chain: []
|
|
16
|
-
date:
|
|
18
|
+
date: 2020-09-01 00:00:00.000000000 Z
|
|
17
19
|
dependencies:
|
|
18
20
|
- !ruby/object:Gem::Dependency
|
|
19
21
|
name: coffee-rails
|
|
@@ -211,9 +213,11 @@ dependencies:
|
|
|
211
213
|
- - ">="
|
|
212
214
|
- !ruby/object:Gem::Version
|
|
213
215
|
version: '0'
|
|
214
|
-
description:
|
|
216
|
+
description: Turbograft is a hard fork of Turbolinks, allowing you to perform partial
|
|
217
|
+
page refreshes and offering ajax form utilities.
|
|
215
218
|
email:
|
|
216
219
|
- tylermercier@gmail.com
|
|
220
|
+
- mathew.allen@shopify.com
|
|
217
221
|
executables: []
|
|
218
222
|
extensions: []
|
|
219
223
|
extra_rdoc_files: []
|
|
@@ -224,10 +228,12 @@ files:
|
|
|
224
228
|
- lib/assets/javascripts/turbograft/click.coffee
|
|
225
229
|
- lib/assets/javascripts/turbograft/component_url.coffee
|
|
226
230
|
- lib/assets/javascripts/turbograft/csrf_token.coffee
|
|
231
|
+
- lib/assets/javascripts/turbograft/document.coffee
|
|
227
232
|
- lib/assets/javascripts/turbograft/initializers.coffee
|
|
228
233
|
- lib/assets/javascripts/turbograft/link.coffee
|
|
229
234
|
- lib/assets/javascripts/turbograft/page.coffee
|
|
230
235
|
- lib/assets/javascripts/turbograft/remote.coffee
|
|
236
|
+
- lib/assets/javascripts/turbograft/response.coffee
|
|
231
237
|
- lib/assets/javascripts/turbograft/turbohead.coffee
|
|
232
238
|
- lib/assets/javascripts/turbograft/turbolinks.coffee
|
|
233
239
|
- lib/turbograft.rb
|
|
@@ -240,7 +246,8 @@ files:
|
|
|
240
246
|
homepage: https://github.com/Shopify/turbograft
|
|
241
247
|
licenses:
|
|
242
248
|
- MIT
|
|
243
|
-
metadata:
|
|
249
|
+
metadata:
|
|
250
|
+
allowed_push_host: https://rubygems.org
|
|
244
251
|
post_install_message:
|
|
245
252
|
rdoc_options: []
|
|
246
253
|
require_paths:
|
|
@@ -249,15 +256,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
249
256
|
requirements:
|
|
250
257
|
- - ">="
|
|
251
258
|
- !ruby/object:Gem::Version
|
|
252
|
-
version: '
|
|
259
|
+
version: '2.1'
|
|
253
260
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
254
261
|
requirements:
|
|
255
262
|
- - ">="
|
|
256
263
|
- !ruby/object:Gem::Version
|
|
257
264
|
version: '0'
|
|
258
265
|
requirements: []
|
|
259
|
-
|
|
260
|
-
rubygems_version: 2.5.1
|
|
266
|
+
rubygems_version: 3.0.3
|
|
261
267
|
signing_key:
|
|
262
268
|
specification_version: 4
|
|
263
269
|
summary: turbolinks with partial page replacement
|