turbolinks 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +9 -1
- data/lib/assets/javascripts/turbolinks.js.coffee +39 -24
- data/lib/turbolinks.rb +28 -1
- data/test/index.html +1 -0
- metadata +3 -3
data/README.md
CHANGED
@@ -3,11 +3,19 @@ Turbolinks
|
|
3
3
|
|
4
4
|
Turbolinks makes following links in your web application faster. Instead of letting the browser recompile the JavaScript and CSS between each page change, we keep the current page instance alive and replace only the body and the title in the head (and potentially spend extra HTTP requests checking if the assets are up-to-date). Think CGI vs persistent process.
|
5
5
|
|
6
|
-
This is similar to pjax, but instead of worrying about what element on the page to replace, and tailoring the server-side response to fit, we replace the entire body. This means that you get the bulk of the speed benefits from pjax (no recompiling of the JavaScript or CSS) without having to tailor the server-side response. It just works.
|
6
|
+
This is similar to [pjax](https://github.com/defunkt/jquery-pjax), but instead of worrying about what element on the page to replace, and tailoring the server-side response to fit, we replace the entire body. This means that you get the bulk of the speed benefits from pjax (no recompiling of the JavaScript or CSS) without having to tailor the server-side response. It just works.
|
7
7
|
|
8
8
|
By default, all internal links will be funneled through Turbolinks, but you can opt out by marking links with data-no-turbolink.
|
9
9
|
|
10
10
|
|
11
|
+
How much faster is it really?
|
12
|
+
-----------------------------
|
13
|
+
|
14
|
+
It depends. The more CSS and JavaScript you have, the bigger the benefit of not throwing away the browser instance and recompiling all of it for every page. Just like a CGI script that says "hello world" will be fast, but a CGI script loading Rails on every request will not.
|
15
|
+
|
16
|
+
In any case, the benefit ranges from [twice as fast](https://github.com/steveklabnik/turbolinks_test) on apps with little JS/CSS, to [three times as fast](https://github.com/steveklabnik/turbolinks_test/tree/all_the_assets) in apps with lots of it. Of course, your mileage may vary, be dependent on your browser version, the moon cycle, and all other factors affecting performance testing. But at least it's a yardstick.
|
17
|
+
|
18
|
+
|
11
19
|
No jQuery or any other framework
|
12
20
|
--------------------------------
|
13
21
|
|
@@ -1,6 +1,8 @@
|
|
1
1
|
pageCache = []
|
2
2
|
currentState = null
|
3
3
|
initialized = false
|
4
|
+
referer = document.location.href
|
5
|
+
|
4
6
|
|
5
7
|
visit = (url) ->
|
6
8
|
if browserSupportsPushState
|
@@ -13,12 +15,13 @@ visit = (url) ->
|
|
13
15
|
|
14
16
|
fetchReplacement = (url) ->
|
15
17
|
triggerEvent 'page:fetch'
|
16
|
-
|
17
18
|
xhr = new XMLHttpRequest
|
18
19
|
xhr.open 'GET', url, true
|
19
20
|
xhr.setRequestHeader 'Accept', 'text/html, application/xhtml+xml, application/xml'
|
21
|
+
xhr.setRequestHeader 'X-XHR-Referer', referer
|
20
22
|
xhr.onload = ->
|
21
23
|
changePage extractTitleAndBody(xhr.responseText)...
|
24
|
+
reflectRedirectedUrl xhr
|
22
25
|
triggerEvent 'page:load'
|
23
26
|
xhr.onabort = -> console.log 'Aborted turbolink fetch!'
|
24
27
|
xhr.send()
|
@@ -47,21 +50,28 @@ cacheCurrentPage = ->
|
|
47
50
|
constrainPageCacheTo(10)
|
48
51
|
|
49
52
|
constrainPageCacheTo = (limit) ->
|
50
|
-
delete pageCache[currentState.position - limit]
|
51
|
-
|
53
|
+
delete pageCache[currentState.position - limit]
|
52
54
|
|
53
55
|
changePage = (title, body) ->
|
54
56
|
document.title = title
|
55
57
|
document.documentElement.replaceChild body, document.body
|
56
|
-
|
58
|
+
executeScriptTags()
|
57
59
|
currentState = window.history.state
|
58
60
|
triggerEvent 'page:change'
|
59
61
|
|
62
|
+
executeScriptTags = ->
|
63
|
+
eval(script.innerHTML) for script in document.body.getElementsByTagName 'script'
|
64
|
+
|
60
65
|
|
61
66
|
reflectNewUrl = (url) ->
|
62
67
|
if url isnt document.location.href
|
68
|
+
referer = document.location.href
|
63
69
|
window.history.pushState { turbolinks: true, position: currentState.position + 1 }, '', url
|
64
70
|
|
71
|
+
reflectRedirectedUrl = (xhr) ->
|
72
|
+
if (location = xhr.getResponseHeader('X-XHR-Current-Location'))
|
73
|
+
window.history.replaceState currentState, '', location
|
74
|
+
|
65
75
|
rememberCurrentUrl = ->
|
66
76
|
window.history.replaceState { turbolinks: true, position: window.history.length - 1 }, '', document.location.href
|
67
77
|
|
@@ -89,7 +99,7 @@ extractTitleAndBody = (html) ->
|
|
89
99
|
title = doc.querySelector 'title'
|
90
100
|
[ title?.textContent, doc.body ]
|
91
101
|
|
92
|
-
createDocument =
|
102
|
+
createDocument = (html) ->
|
93
103
|
createDocumentUsingParser = (html) ->
|
94
104
|
(new DOMParser).parseFromString html, 'text/html'
|
95
105
|
|
@@ -104,17 +114,24 @@ createDocument = do ->
|
|
104
114
|
testDoc = createDocumentUsingParser '<html><body><p>test'
|
105
115
|
|
106
116
|
if testDoc?.body?.childNodes.length is 1
|
107
|
-
createDocumentUsingParser
|
117
|
+
createDocumentUsingParser html
|
108
118
|
else
|
109
|
-
createDocumentUsingWrite
|
119
|
+
createDocumentUsingWrite html
|
110
120
|
|
111
121
|
|
122
|
+
installClickHandlerLast = (event) ->
|
123
|
+
unless event.defaultPrevented
|
124
|
+
document.removeEventListener 'click', handleClick
|
125
|
+
document.addEventListener 'click', handleClick
|
126
|
+
|
112
127
|
handleClick = (event) ->
|
113
|
-
|
128
|
+
unless event.defaultPrevented
|
129
|
+
link = extractLink event
|
130
|
+
if link.nodeName is 'A' and !ignoreClick(event, link)
|
131
|
+
link = extractLink event
|
132
|
+
visit link.href
|
133
|
+
event.preventDefault()
|
114
134
|
|
115
|
-
if link.nodeName is 'A' and !ignoreClick(event, link)
|
116
|
-
visit link.href
|
117
|
-
event.preventDefault()
|
118
135
|
|
119
136
|
extractLink = (event) ->
|
120
137
|
link = event.target
|
@@ -134,30 +151,28 @@ anchoredLink = (link) ->
|
|
134
151
|
nonHtmlLink = (link) ->
|
135
152
|
link.href.match(/\.[a-z]+(\?.*)?$/g) and not link.href.match(/\.html?(\?.*)?$/g)
|
136
153
|
|
137
|
-
remoteLink = (link) ->
|
138
|
-
link.getAttribute('data-remote')?
|
139
|
-
|
140
154
|
noTurbolink = (link) ->
|
141
|
-
link
|
155
|
+
until ignore or link is document
|
156
|
+
ignore = link.getAttribute('data-no-turbolink')?
|
157
|
+
link = link.parentNode
|
158
|
+
ignore
|
142
159
|
|
143
|
-
|
144
|
-
event.which > 1 or event.metaKey or event.ctrlKey
|
160
|
+
nonStandardClick = (event) ->
|
161
|
+
event.which > 1 or event.metaKey or event.ctrlKey or event.shiftKey or event.altKey
|
145
162
|
|
146
163
|
ignoreClick = (event, link) ->
|
147
|
-
|
148
|
-
|
149
|
-
newTabClick(event)
|
164
|
+
crossOriginLink(link) or anchoredLink(link) or nonHtmlLink(link) or
|
165
|
+
noTurbolink(link) or nonStandardClick(event)
|
150
166
|
|
151
167
|
|
152
168
|
browserSupportsPushState =
|
153
|
-
window.history and window.history.pushState and window.history.replaceState
|
169
|
+
window.history and window.history.pushState and window.history.replaceState and window.history.state != undefined
|
154
170
|
|
155
171
|
if browserSupportsPushState
|
156
172
|
window.addEventListener 'popstate', (event) ->
|
157
173
|
fetchHistory event.state if event.state?.turbolinks
|
158
174
|
|
159
|
-
document.addEventListener 'click',
|
160
|
-
handleClick event
|
175
|
+
document.addEventListener 'click', installClickHandlerLast, true
|
161
176
|
|
162
177
|
# Call Turbolinks.visit(url) from client code
|
163
|
-
@Turbolinks = visit
|
178
|
+
@Turbolinks = { visit }
|
data/lib/turbolinks.rb
CHANGED
@@ -1,4 +1,31 @@
|
|
1
1
|
module Turbolinks
|
2
|
+
module XHRHeaders
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
alias_method_chain :_compute_redirect_to_location, :xhr_referer
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
def _compute_redirect_to_location_with_xhr_referer(options)
|
11
|
+
if options == :back && request.headers["X-XHR-Referer"]
|
12
|
+
_compute_redirect_to_location_without_xhr_referer(request.headers["X-XHR-Referer"])
|
13
|
+
else
|
14
|
+
_compute_redirect_to_location_without_xhr_referer(options)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def set_xhr_current_location
|
19
|
+
response.headers['X-XHR-Current-Location'] = request.fullpath
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
2
23
|
class Engine < ::Rails::Engine
|
24
|
+
initializer :turbolinks_xhr_headers do |config|
|
25
|
+
ActionController::Base.class_eval do
|
26
|
+
include XHRHeaders
|
27
|
+
after_filter :set_xhr_current_location
|
28
|
+
end
|
29
|
+
end
|
3
30
|
end
|
4
|
-
end
|
31
|
+
end
|
data/test/index.html
CHANGED
@@ -15,6 +15,7 @@
|
|
15
15
|
<li><a href="/other.html">Other page</a></li>
|
16
16
|
<li><a href="/other.html"><span>Wrapped link</span></a></li>
|
17
17
|
<li><a href="http://www.google.com/">Cross origin</a></li>
|
18
|
+
<li><a href="/other.html" onclick="if(!confirm('follow link?')) { return false}">Confirm Fire Order</a></li>
|
18
19
|
<li><a href="/dummy.gif?12345">Query Param Image Link</a></li>
|
19
20
|
<li><a href="#">Hash link</a></li>
|
20
21
|
</ul>
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: turbolinks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-10-02 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description:
|
15
15
|
email: david@loudthinking.com
|
@@ -45,7 +45,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
45
45
|
version: '0'
|
46
46
|
requirements: []
|
47
47
|
rubyforge_project:
|
48
|
-
rubygems_version: 1.8.
|
48
|
+
rubygems_version: 1.8.23
|
49
49
|
signing_key:
|
50
50
|
specification_version: 3
|
51
51
|
summary: Turbolinks makes following links in your web application faster (use with
|