Skip to content
This repository has been archived by the owner on Jul 28, 2018. It is now read-only.

Attach affected nodes to lifecycle events #537

Merged
merged 3 commits into from
May 26, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@
Turbolinks.visit url, scroll: false
```

* Attach affected nodes to the `page:before-unload`, `page:change` and `page:load` events (in `event.data`).

*Thibaut Courouble*


## Turbolinks 2.5.3 (December 8, 2014)

* Prevent the progress bar from filling the entire screen in older versions of Safari.
Expand Down
38 changes: 24 additions & 14 deletions lib/assets/javascripts/turbolinks.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,12 @@ fetchReplacement = (url, options) ->
if doc = processResponse()
reflectNewUrl url
reflectRedirectedUrl()
changePage doc, options
loadedNodes = changePage doc, options
if options.showProgressBar
progressBar?.done()
manuallyTriggerHashChangeForFirefox()
updateScrollPosition(options.scroll)
triggerEvent EVENTS.LOAD
triggerEvent EVENTS.LOAD, loadedNodes
else
progressBar?.done()
document.location.href = crossOriginRedirect() or url.absolute
Expand Down Expand Up @@ -125,36 +125,44 @@ constrainPageCacheTo = (limit) ->
delete pageCache[key]

replace = (html, options = {}) ->
changePage createDocument(html), options
loadedNodes = changePage createDocument(html), options
triggerEvent EVENTS.LOAD, loadedNodes

changePage = (doc, options) ->
[title, targetBody, csrfToken] = extractTitleAndBody(doc)
title ?= options.title
currentBody = document.body

triggerEvent EVENTS.BEFORE_UNLOAD
if options.change
nodesToChange = findNodes(currentBody, '[data-turbolinks-temporary]')
nodesToChange.push(findNodesMatchingKeys(currentBody, options.change)...)
else
nodesToChange = [currentBody]

triggerEvent EVENTS.BEFORE_UNLOAD, nodesToChange
document.title = title

if options.change
swapNodes(targetBody, findNodes(document.body, '[data-turbolinks-temporary]'), keep: false)
swapNodes(targetBody, findNodesMatchingKeys(document.body, options.change), keep: false)
changedNodes = swapNodes(targetBody, nodesToChange, keep: false)
else
unless options.flush
nodesToBeKept = findNodes(document.body, '[data-turbolinks-permanent]')
nodesToBeKept.push(findNodesMatchingKeys(document.body, options.keep)...) if options.keep
swapNodes(targetBody, nodesToBeKept, keep: true)
nodesToKeep = findNodes(currentBody, '[data-turbolinks-permanent]')
nodesToKeep.push(findNodesMatchingKeys(currentBody, options.keep)...) if options.keep
swapNodes(targetBody, nodesToKeep, keep: true)

existingBody = document.body
document.body = targetBody
onNodeRemoved(existingBody)
onNodeRemoved(currentBody)
CSRFToken.update csrfToken if csrfToken?
setAutofocusElement()
changedNodes = [targetBody]

scriptsToRun = if options.runScripts is false then 'script[data-turbolinks-eval="always"]' else 'script:not([data-turbolinks-eval="false"])'
executeScriptTags(scriptsToRun)
currentState = window.history.state

triggerEvent EVENTS.CHANGE
triggerEvent EVENTS.CHANGE, changedNodes
triggerEvent EVENTS.UPDATE
return changedNodes

findNodes = (body, selector) ->
Array::slice.apply(body.querySelectorAll(selector))
Expand All @@ -167,6 +175,7 @@ findNodesMatchingKeys = (body, keys) ->
return matchingNodes

swapNodes = (targetBody, existingNodes, options) ->
changedNodes = []
for existingNode in existingNodes
unless nodeId = existingNode.getAttribute('id')
throw new Error("Turbolinks partial replace: turbolinks elements must have an id.")
Expand All @@ -181,7 +190,8 @@ swapNodes = (targetBody, existingNodes, options) ->
targetNode = existingNode.ownerDocument.importNode(targetNode, true)
existingNode.parentNode.replaceChild(targetNode, existingNode)
onNodeRemoved(existingNode)
return
changedNodes.push(targetNode)
return changedNodes

onNodeRemoved = (node) ->
if typeof jQuery isnt 'undefined'
Expand Down Expand Up @@ -572,7 +582,7 @@ ProgressBarAPI =

installDocumentReadyPageEventTriggers = ->
document.addEventListener 'DOMContentLoaded', ( ->
triggerEvent EVENTS.CHANGE
triggerEvent EVENTS.CHANGE, [document.body]
triggerEvent EVENTS.UPDATE
), true

Expand Down
6 changes: 4 additions & 2 deletions test/javascript/turbolinks_replace_test.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ suite 'Turbolinks.replace()', ->
assert.equal event.data, body
assert.notEqual permanent, event.data.querySelector('#permanent')
afterRemoveFired = true
@document.addEventListener 'page:change', =>
@document.addEventListener 'page:load', (event) =>
assert.ok beforeUnloadFired
assert.ok afterRemoveFired
assert.deepEqual event.data, [@document.body]
assert.equal @window.j, 1
assert.isUndefined @window.headScript
assert.isUndefined @window.bodyScriptEvalFalse
Expand Down Expand Up @@ -170,9 +171,10 @@ suite 'Turbolinks.replace()', ->
@document.addEventListener 'page:after-remove', (event) =>
assert.isNull event.data.parentNode
assert.equal event.data, afterRemoveNodes.shift()
@document.addEventListener 'page:change', =>
@document.addEventListener 'page:load', (event) =>
assert.ok beforeUnloadFired
assert.equal afterRemoveNodes.length, 0
assert.deepEqual event.data, [@$('#temporary'), @$('#change'), @$('[id="change:key"]')]
assert.equal @window.i, 2 # scripts are re-run
assert.isUndefined @window.bodyScript
assert.isUndefined @window.headScript
Expand Down
25 changes: 19 additions & 6 deletions test/javascript/turbolinks_visit_test.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@ suite 'Turbolinks.visit()', ->
body = @$('body')
permanent = @$('#permanent')
permanent.addEventListener 'click', -> done()
pageReceivedFired = beforeUnloadFired = afterRemoveFired = false
pageReceivedFired = beforeUnloadFired = afterRemoveFired = pageChangeFired = false
@document.addEventListener 'page:receive', =>
state = turbolinks: true, url: "#{location.protocol}//#{location.host}/javascript/iframe.html"
assert.deepEqual @history.state, state
pageReceivedFired = true
@document.addEventListener 'page:before-unload', =>
@document.addEventListener 'page:before-unload', (event) =>
assert.isUndefined @window.j
assert.deepEqual event.data, [body]
assert.notOk @$('#new-div')
assert.notOk @$('body').hasAttribute('new-attribute')
assert.ok @$('#div')
Expand All @@ -43,10 +44,15 @@ suite 'Turbolinks.visit()', ->
assert.equal event.data, body
assert.notEqual permanent, event.data.querySelector('#permanent')
afterRemoveFired = true
@document.addEventListener 'page:load', =>
@document.addEventListener 'page:change', (event) =>
assert.deepEqual event.data, [@document.body]
pageChangeFired = true
@document.addEventListener 'page:load', (event) =>
assert.ok pageReceivedFired
assert.ok beforeUnloadFired
assert.ok afterRemoveFired
assert.ok pageChangeFired
assert.deepEqual event.data, [@document.body]
assert.equal @window.i, 1
assert.equal @window.j, 1
assert.isUndefined @window.headScript
Expand All @@ -71,17 +77,24 @@ suite 'Turbolinks.visit()', ->
test "successful with :change", (done) ->
body = @$('body')
change = @$('#change')
change2 = @$('[id="change:key"]')
temporary = @$('#temporary')
beforeUnloadFired = false
@document.addEventListener 'page:before-unload', =>
beforeUnloadFired = pageChangeFired = false
@document.addEventListener 'page:before-unload', (event) =>
assert.deepEqual event.data, [temporary, change, change2]
assert.equal @window.i, 1
assert.equal @$('#change').textContent, 'change content'
assert.equal @$('[id="change:key"]').textContent, 'change content'
assert.equal @$('#temporary').textContent, 'temporary content'
assert.equal @document.title, 'title'
beforeUnloadFired = true
@document.addEventListener 'page:load', =>
@document.addEventListener 'page:change', (event) =>
assert.deepEqual event.data, [@$('#temporary'), @$('#change'), @$('[id="change:key"]')]
pageChangeFired = true
@document.addEventListener 'page:load', (event) =>
assert.ok beforeUnloadFired
assert.ok pageChangeFired
assert.deepEqual event.data, [@$('#temporary'), @$('#change'), @$('[id="change:key"]')]
assert.equal @window.i, 2
assert.isUndefined @window.j
assert.isUndefined @window.headScript
Expand Down