turbolinks 2.3.0 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|