turbolinks 2.3.0 → 2.4.0
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 +4 -4
- data/README.md +4 -2
- data/lib/assets/javascripts/turbolinks.js.coffee +83 -26
- data/lib/turbolinks/cookies.rb +9 -4
- data/lib/turbolinks/version.rb +1 -1
- data/lib/turbolinks/xhr_headers.rb +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5fba03f6769899590a73f8d2f6f1e2b1756335cc
|
4
|
+
data.tar.gz: fcb4ee4559b15a8d3037ccd75636a8de9513365e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0cf63e984ac0cfed94831716ecfaf3db1be2045e83e07d9b8e4304ce6639135ddee36876b6c945fcec564730d33bb5f00e1c66d456218bc0db054568a2ef7790
|
7
|
+
data.tar.gz: 6733c1c8dc08e44e4271962e50ed4891ed96e0ba30a531c08025b001ecf700d0b4c5ef082891d62391d91c9a8f0bf81f7ce3e1838bf8774b582302289d42660c
|
data/README.md
CHANGED
@@ -33,8 +33,9 @@ With Turbolinks pages will change without a full reload, so you can't rely on `D
|
|
33
33
|
* `page:before-change` a Turbolinks-enabled link has been clicked *(see below for more details)*
|
34
34
|
* `page:fetch` starting to fetch a new target page
|
35
35
|
* `page:receive` the page has been fetched from the server, but not yet parsed
|
36
|
-
* `page:
|
37
|
-
* `page:
|
36
|
+
* `page:before-unload` the page has been parsed and is about to be changed
|
37
|
+
* `page:change` the page has been changed to the new version (and on DOMContentLoaded)
|
38
|
+
* `page:update` is triggered alongside both page:change and jQuery's ajaxSuccess (if jQuery is available - otherwise you can manually trigger it when calling XMLHttpRequest in your own code)
|
38
39
|
* `page:load` is fired at the end of the loading process.
|
39
40
|
|
40
41
|
Handlers bound to the `page:before-change` event may return `false`, which will cancel the Turbolinks process.
|
@@ -42,6 +43,7 @@ Handlers bound to the `page:before-change` event may return `false`, which will
|
|
42
43
|
By default, Turbolinks caches 10 of these page loads. It listens to the [popstate](https://developer.mozilla.org/en-US/docs/DOM/Manipulating_the_browser_history#The_popstate_event) event and attempts to restore page state from the cache when it's triggered. When `popstate` is fired the following process happens:
|
43
44
|
|
44
45
|
***Restore* a cached page from the client-side cache:**
|
46
|
+
* `page:before-unload` page has been fetched from the cache and is about to be changed
|
45
47
|
* `page:change` page has changed to the cached page.
|
46
48
|
* `page:restore` is fired at the end of restore process.
|
47
49
|
|
@@ -10,6 +10,16 @@ referer = null
|
|
10
10
|
createDocument = null
|
11
11
|
xhr = null
|
12
12
|
|
13
|
+
EVENTS =
|
14
|
+
BEFORE_CHANGE: 'page:before-change'
|
15
|
+
FETCH: 'page:fetch'
|
16
|
+
RECEIVE: 'page:receive'
|
17
|
+
CHANGE: 'page:change'
|
18
|
+
UPDATE: 'page:update'
|
19
|
+
LOAD: 'page:load'
|
20
|
+
RESTORE: 'page:restore'
|
21
|
+
BEFORE_UNLOAD: 'page:before-unload'
|
22
|
+
EXPIRE: 'page:expire'
|
13
23
|
|
14
24
|
fetch = (url) ->
|
15
25
|
url = new ComponentUrl url
|
@@ -30,8 +40,8 @@ transitionCacheFor = (url) ->
|
|
30
40
|
enableTransitionCache = (enable = true) ->
|
31
41
|
transitionCacheEnabled = enable
|
32
42
|
|
33
|
-
fetchReplacement = (url, onLoadFunction = =>) ->
|
34
|
-
triggerEvent
|
43
|
+
fetchReplacement = (url, onLoadFunction = =>) ->
|
44
|
+
triggerEvent EVENTS.FETCH, url: url.absolute
|
35
45
|
|
36
46
|
xhr?.abort()
|
37
47
|
xhr = new XMLHttpRequest
|
@@ -40,7 +50,7 @@ fetchReplacement = (url, onLoadFunction = =>) ->
|
|
40
50
|
xhr.setRequestHeader 'X-XHR-Referer', referer
|
41
51
|
|
42
52
|
xhr.onload = ->
|
43
|
-
triggerEvent
|
53
|
+
triggerEvent EVENTS.RECEIVE, url: url.absolute
|
44
54
|
|
45
55
|
if doc = processResponse()
|
46
56
|
reflectNewUrl url
|
@@ -48,7 +58,7 @@ fetchReplacement = (url, onLoadFunction = =>) ->
|
|
48
58
|
manuallyTriggerHashChangeForFirefox()
|
49
59
|
reflectRedirectedUrl()
|
50
60
|
onLoadFunction()
|
51
|
-
triggerEvent
|
61
|
+
triggerEvent EVENTS.LOAD
|
52
62
|
else
|
53
63
|
document.location.href = url.absolute
|
54
64
|
|
@@ -61,7 +71,7 @@ fetchHistory = (cachedPage) ->
|
|
61
71
|
xhr?.abort()
|
62
72
|
changePage cachedPage.title, cachedPage.body
|
63
73
|
recallScrollPosition cachedPage
|
64
|
-
triggerEvent
|
74
|
+
triggerEvent EVENTS.RESTORE
|
65
75
|
|
66
76
|
|
67
77
|
cacheCurrentPage = ->
|
@@ -89,24 +99,26 @@ constrainPageCacheTo = (limit) ->
|
|
89
99
|
.sort (a, b) -> b - a
|
90
100
|
|
91
101
|
for key in pageCacheKeys when pageCache[key].cachedAt <= cacheTimesRecentFirst[limit]
|
92
|
-
triggerEvent
|
102
|
+
triggerEvent EVENTS.EXPIRE, pageCache[key]
|
93
103
|
delete pageCache[key]
|
94
104
|
|
95
105
|
changePage = (title, body, csrfToken, runScripts) ->
|
106
|
+
triggerEvent EVENTS.BEFORE_UNLOAD
|
96
107
|
document.title = title
|
97
108
|
document.documentElement.replaceChild body, document.body
|
98
109
|
CSRFToken.update csrfToken if csrfToken?
|
99
110
|
setAutofocusElement()
|
100
111
|
executeScriptTags() if runScripts
|
101
112
|
currentState = window.history.state
|
102
|
-
triggerEvent
|
103
|
-
triggerEvent
|
113
|
+
triggerEvent EVENTS.CHANGE
|
114
|
+
triggerEvent EVENTS.UPDATE
|
104
115
|
|
105
116
|
executeScriptTags = ->
|
106
117
|
scripts = Array::slice.call document.body.querySelectorAll 'script:not([data-turbolinks-eval="false"])'
|
107
118
|
for script in scripts when script.type in ['', 'text/javascript']
|
108
119
|
copy = document.createElement 'script'
|
109
120
|
copy.setAttribute attr.name, attr.value for attr in script.attributes
|
121
|
+
copy.async = false unless script.hasAttribute 'async'
|
110
122
|
copy.appendChild document.createTextNode script.innerHTML
|
111
123
|
{ parentNode, nextSibling } = script
|
112
124
|
parentNode.removeChild script
|
@@ -164,29 +176,39 @@ resetScrollPosition = ->
|
|
164
176
|
window.scrollTo 0, 0
|
165
177
|
|
166
178
|
|
179
|
+
clone = (original) ->
|
180
|
+
return original if not original? or typeof original isnt 'object'
|
181
|
+
copy = new original.constructor()
|
182
|
+
copy[key] = clone value for key, value of original
|
183
|
+
copy
|
184
|
+
|
167
185
|
popCookie = (name) ->
|
168
186
|
value = document.cookie.match(new RegExp(name+"=(\\w+)"))?[1].toUpperCase() or ''
|
169
187
|
document.cookie = name + '=; expires=Thu, 01-Jan-70 00:00:01 GMT; path=/'
|
170
188
|
value
|
171
189
|
|
172
190
|
triggerEvent = (name, data) ->
|
191
|
+
if typeof Prototype isnt 'undefined'
|
192
|
+
Event.fire document, name, data, true
|
193
|
+
|
173
194
|
event = document.createEvent 'Events'
|
174
195
|
event.data = data if data
|
175
196
|
event.initEvent name, true, true
|
176
197
|
document.dispatchEvent event
|
177
198
|
|
178
|
-
pageChangePrevented = ->
|
179
|
-
!triggerEvent
|
199
|
+
pageChangePrevented = (url) ->
|
200
|
+
!triggerEvent EVENTS.BEFORE_CHANGE, url: url
|
180
201
|
|
181
202
|
processResponse = ->
|
182
203
|
clientOrServerError = ->
|
183
204
|
400 <= xhr.status < 600
|
184
205
|
|
185
206
|
validContent = ->
|
186
|
-
xhr.getResponseHeader('Content-Type')
|
207
|
+
(contentType = xhr.getResponseHeader('Content-Type'))? and
|
208
|
+
contentType.match /^(?:text\/html|application\/xhtml\+xml|application\/xml)(?:;|$)/
|
187
209
|
|
188
210
|
extractTrackAssets = (doc) ->
|
189
|
-
for node in doc.head.childNodes when node.getAttribute?('data-turbolinks-track')?
|
211
|
+
for node in doc.querySelector('head').childNodes when node.getAttribute?('data-turbolinks-track')?
|
190
212
|
node.getAttribute('src') or node.getAttribute('href')
|
191
213
|
|
192
214
|
assetsChanged = (doc) ->
|
@@ -205,7 +227,7 @@ processResponse = ->
|
|
205
227
|
|
206
228
|
extractTitleAndBody = (doc) ->
|
207
229
|
title = doc.querySelector 'title'
|
208
|
-
[ title?.textContent, removeNoscriptTags(doc.body), CSRFToken.get(doc).token, 'runScripts' ]
|
230
|
+
[ title?.textContent, removeNoscriptTags(doc.querySelector('body')), CSRFToken.get(doc).token, 'runScripts' ]
|
209
231
|
|
210
232
|
CSRFToken =
|
211
233
|
get: (doc = document) ->
|
@@ -233,6 +255,15 @@ browserCompatibleDocumentParser = ->
|
|
233
255
|
doc.close()
|
234
256
|
doc
|
235
257
|
|
258
|
+
createDocumentUsingFragment = (html) ->
|
259
|
+
head = html.match(/<head[^>]*>([\s\S.]*)<\/head>/i)?[0] or '<head></head>'
|
260
|
+
body = html.match(/<body[^>]*>([\s\S.]*)<\/body>/i)?[0] or '<body></body>'
|
261
|
+
htmlWrapper = document.createElement 'html'
|
262
|
+
htmlWrapper.innerHTML = head + body
|
263
|
+
doc = document.createDocumentFragment()
|
264
|
+
doc.appendChild htmlWrapper
|
265
|
+
doc
|
266
|
+
|
236
267
|
# Use createDocumentUsingParser if DOMParser is defined and natively
|
237
268
|
# supports 'text/html' parsing (Firefox 12+, IE 10)
|
238
269
|
#
|
@@ -243,16 +274,32 @@ browserCompatibleDocumentParser = ->
|
|
243
274
|
# - DOMParser isn't defined
|
244
275
|
# - createDocumentUsingParser returns null due to unsupported type 'text/html' (Chrome, Safari)
|
245
276
|
# - createDocumentUsingDOM doesn't create a valid HTML document (safeguarding against potential edge cases)
|
277
|
+
#
|
278
|
+
# Use createDocumentUsingFragment if the previously selected parser does not
|
279
|
+
# correctly parse <form> tags. (Safari 7.1+ - see github.com/rails/turbolinks/issues/408)
|
280
|
+
buildTestsUsing = (createMethod) ->
|
281
|
+
buildTest = (fallback, passes) ->
|
282
|
+
passes: passes()
|
283
|
+
fallback: fallback
|
284
|
+
|
285
|
+
structureTest = buildTest createDocumentUsingWrite, =>
|
286
|
+
(createMethod '<html><body><p>test')?.body?.childNodes.length is 1
|
287
|
+
|
288
|
+
formNestingTest = buildTest createDocumentUsingFragment, =>
|
289
|
+
(createMethod '<html><body><form></form><div></div></body></html>')?.body?.childNodes.length is 2
|
290
|
+
|
291
|
+
[structureTest, formNestingTest]
|
292
|
+
|
246
293
|
try
|
247
294
|
if window.DOMParser
|
248
|
-
|
295
|
+
docTests = buildTestsUsing createDocumentUsingParser
|
249
296
|
createDocumentUsingParser
|
250
297
|
catch e
|
251
|
-
|
298
|
+
docTests = buildTestsUsing createDocumentUsingDOM
|
252
299
|
createDocumentUsingDOM
|
253
300
|
finally
|
254
|
-
|
255
|
-
return
|
301
|
+
for docTest in docTests
|
302
|
+
return docTest.fallback unless docTest.passes
|
256
303
|
|
257
304
|
|
258
305
|
# The ComponentUrl class converts a basic URL string into an object
|
@@ -265,7 +312,7 @@ class ComponentUrl
|
|
265
312
|
return @original if @original.constructor is ComponentUrl
|
266
313
|
@_parse()
|
267
314
|
|
268
|
-
withoutHash: -> @href.replace
|
315
|
+
withoutHash: -> @href.replace(@hash, '').replace('#', '')
|
269
316
|
|
270
317
|
# Intention revealing function alias
|
271
318
|
withoutHashForIE10compatibility: -> @withoutHash()
|
@@ -293,6 +340,8 @@ class Link extends ComponentUrl
|
|
293
340
|
constructor: (@link) ->
|
294
341
|
return @link if @link.constructor is Link
|
295
342
|
@original = @link.href
|
343
|
+
@originalElement = @link
|
344
|
+
@link = @link.cloneNode false
|
296
345
|
super
|
297
346
|
|
298
347
|
shouldIgnore: ->
|
@@ -306,14 +355,14 @@ class Link extends ComponentUrl
|
|
306
355
|
@origin isnt (new ComponentUrl).origin
|
307
356
|
|
308
357
|
_anchored: ->
|
309
|
-
(
|
310
|
-
(@
|
358
|
+
(@hash.length > 0 or @href.charAt(@href.length - 1) is '#') and
|
359
|
+
(@withoutHash() is (new ComponentUrl).withoutHash())
|
311
360
|
|
312
361
|
_nonHtml: ->
|
313
362
|
@pathname.match(/\.[a-z]+$/g) and not @pathname.match(new RegExp("\\.(?:#{Link.HTML_EXTENSIONS.join('|')})?$", 'g'))
|
314
363
|
|
315
364
|
_optOut: ->
|
316
|
-
link = @
|
365
|
+
link = @originalElement
|
317
366
|
until ignore or link is document
|
318
367
|
ignore = link.getAttribute('data-no-turbolink')?
|
319
368
|
link = link.parentNode
|
@@ -340,7 +389,7 @@ class Click
|
|
340
389
|
return if @event.defaultPrevented
|
341
390
|
@_extractLink()
|
342
391
|
if @_validForTurbolinks()
|
343
|
-
visit @link.href unless pageChangePrevented()
|
392
|
+
visit @link.href unless pageChangePrevented(@link.absolute)
|
344
393
|
@event.preventDefault()
|
345
394
|
|
346
395
|
_extractLink: ->
|
@@ -366,15 +415,15 @@ bypassOnLoadPopstate = (fn) ->
|
|
366
415
|
|
367
416
|
installDocumentReadyPageEventTriggers = ->
|
368
417
|
document.addEventListener 'DOMContentLoaded', ( ->
|
369
|
-
triggerEvent
|
370
|
-
triggerEvent
|
418
|
+
triggerEvent EVENTS.CHANGE
|
419
|
+
triggerEvent EVENTS.UPDATE
|
371
420
|
), true
|
372
421
|
|
373
422
|
installJqueryAjaxSuccessPageUpdateTrigger = ->
|
374
423
|
if typeof jQuery isnt 'undefined'
|
375
424
|
jQuery(document).on 'ajaxSuccess', (event, xhr, settings) ->
|
376
425
|
return unless jQuery.trim xhr.responseText
|
377
|
-
triggerEvent
|
426
|
+
triggerEvent EVENTS.UPDATE
|
378
427
|
|
379
428
|
installHistoryChangeHandler = (event) ->
|
380
429
|
if event.state?.turbolinks
|
@@ -433,4 +482,12 @@ else
|
|
433
482
|
# Turbolinks.enableTransitionCache()
|
434
483
|
# Turbolinks.allowLinkExtensions('md')
|
435
484
|
# Turbolinks.supported
|
436
|
-
|
485
|
+
# Turbolinks.EVENTS
|
486
|
+
@Turbolinks = {
|
487
|
+
visit,
|
488
|
+
pagesCached,
|
489
|
+
enableTransitionCache,
|
490
|
+
allowLinkExtensions: Link.allowExtensions,
|
491
|
+
supported: browserSupportsTurbolinks,
|
492
|
+
EVENTS: clone(EVENTS)
|
493
|
+
}
|
data/lib/turbolinks/cookies.rb
CHANGED
@@ -1,10 +1,15 @@
|
|
1
1
|
module Turbolinks
|
2
|
-
#
|
3
|
-
#
|
2
|
+
# For non-GET requests, sets a request_method cookie containing
|
3
|
+
# the request method of the current request. The Turbolinks script
|
4
|
+
# will not initialize if this cookie is set.
|
4
5
|
module Cookies
|
5
6
|
private
|
6
7
|
def set_request_method_cookie
|
7
|
-
|
8
|
+
if request.get?
|
9
|
+
cookies.delete(:request_method)
|
10
|
+
else
|
11
|
+
cookies[:request_method] = request.request_method
|
12
|
+
end
|
8
13
|
end
|
9
14
|
end
|
10
|
-
end
|
15
|
+
end
|
data/lib/turbolinks/version.rb
CHANGED
@@ -25,12 +25,12 @@ module Turbolinks
|
|
25
25
|
|
26
26
|
private
|
27
27
|
def store_for_turbolinks(url)
|
28
|
-
session[:_turbolinks_redirect_to] = url if request.headers["X-XHR-Referer"]
|
28
|
+
session[:_turbolinks_redirect_to] = url if session && request.headers["X-XHR-Referer"]
|
29
29
|
url
|
30
30
|
end
|
31
31
|
|
32
32
|
def set_xhr_redirected_to
|
33
|
-
if session[:_turbolinks_redirect_to]
|
33
|
+
if session && session[:_turbolinks_redirect_to]
|
34
34
|
response.headers['X-XHR-Redirected-To'] = session.delete :_turbolinks_redirect_to
|
35
35
|
end
|
36
36
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: turbolinks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Heinemeier Hansson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-10-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: coffee-rails
|