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 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] if currentState.position == window.history.length - 1
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 = do ->
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
- link = extractLink event
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.getAttribute('data-no-turbolink')?
155
+ until ignore or link is document
156
+ ignore = link.getAttribute('data-no-turbolink')?
157
+ link = link.parentNode
158
+ ignore
142
159
 
143
- newTabClick = (event) ->
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
- samePageLink(link) or crossOriginLink(link) or anchoredLink(link) or
148
- nonHtmlLink(link) or remoteLink(link) or noTurbolink(link) or
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', (event) ->
160
- handleClick event
175
+ document.addEventListener 'click', installClickHandlerLast, true
161
176
 
162
177
  # Call Turbolinks.visit(url) from client code
163
- @Turbolinks = visit: 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.3.3
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-09-28 00:00:00.000000000 Z
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.7
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