turbolinks 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b2bb9ae664cef07039bfb5326ff85ebcfbbca0e8
4
+ data.tar.gz: 2b55c3358f99361d016329ccfa661872599069ba
5
+ SHA512:
6
+ metadata.gz: edd2489816d02cbfd4754b28f82a95d64c4b1290c41ee28984d3333c432c339c437d27c9d4cef1b526c0b9d076ea8d0c2f5fd50a0807cd5c98aed5cec8389af7
7
+ data.tar.gz: 1fbb34bbd7831d6e72ebfc36a9aba4a019535118e0fc95db586d445f74700261a6f6bf9f26936019726f8dad510aede638671245369e43c406cb3f6744033a07
data/README.md CHANGED
@@ -13,7 +13,7 @@ How much faster is it really?
13
13
 
14
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
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.
16
+ In any case, the benefit can be up to [twice as fast](https://github.com/steveklabnik/turbolinks_test/tree/all_the_assets) in apps with lots of JS and CSS. 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
17
 
18
18
  The best way to find out just how fast it is? Try it on your own application. It hardly takes any effort at all.
19
19
 
@@ -33,6 +33,7 @@ Since pages will change without a full reload with Turbolinks, you can't by defa
33
33
  * `page:load` fetched page is being retrieved fresh from the server.
34
34
  * `page:restore` fetched page is being retrieved from the 10-slot client-side cache.
35
35
  * `page:change` page has changed to the newly fetched version.
36
+ * `page:receive` page has been fetched from the server, but not yet parsed.
36
37
 
37
38
  So if you wanted to have a client-side spinner, you could listen for `page:fetch` to start it and `page:change` to stop it. If you have DOM transformation that are not idempotent (the best way), you can hook them to happen only on `page:load` instead of `page:change` (as that would run them again on the cached pages).
38
39
 
@@ -62,6 +63,19 @@ Note that internal links to files not ending in .html, or having no extension, w
62
63
  Also, Turbolinks is installed as the last click handler for links. So if you install another handler that calls event.preventDefault(), Turbolinks will not run. This ensures that you can safely use Turbolinks with stuff like `data-method`, `data-remote`, or `data-confirm` from Rails.
63
64
 
64
65
 
66
+ jquery.turbolinks
67
+ -----------------
68
+
69
+ If you have a lot of existing JavaScript that binds elements on jQuery.ready(), you can pull the [jquery.turbolinks](https://github.com/kossnocorp/jquery.turbolinks) library into your project that will trigger ready() when Turbolinks triggers the the `page:load` event. It may restore functionality of some libraries.
70
+
71
+ Add the gem to your project, then add the following line to your JavaScript manifest file, after `jquery.js` but before `turbolinks.js`:
72
+
73
+ ``` js
74
+ //= require jquery.turbolinks
75
+ ```
76
+
77
+ Additional details and configuration options can be found in the [jquery.turbolinks README](https://github.com/kossnocorp/jquery.turbolinks/blob/master/README.md).
78
+
65
79
  Asset change detection
66
80
  ----------------------
67
81
 
@@ -79,6 +93,7 @@ Evaluating script tags
79
93
 
80
94
  Turbolinks will evaluate any script tags in pages it visit, if those tags do not have a type or if the type is text/javascript. All other script tags will be ignored.
81
95
 
96
+ As a rule of thumb when switching to Turbolinks, move all of your javascript tags inside the `head` and then work backwards, only moving javascript code back to the body if absolutely necessary.
82
97
 
83
98
  Triggering a Turbolinks visit manually
84
99
  ---------------------------------------
@@ -97,7 +112,7 @@ Compatibility
97
112
 
98
113
  Turbolinks is designed to work with any browser that fully supports pushState and all the related APIs. This includes Safari 6.0+ (but not Safari 5.1.x!), IE10, and latest Chromes and Firefoxes.
99
114
 
100
- Do note that existing JavaScript libraries may not all be compatible with Turbolinks out of the box due to the change in instantiation cycle. You might very well have to modify them to work with Turbolinks' new set of events.
115
+ Do note that existing JavaScript libraries may not all be compatible with Turbolinks out of the box due to the change in instantiation cycle. You might very well have to modify them to work with Turbolinks' new set of events. For help with this, check out the [Turbolinks Compatibility](http://reed.github.com/turbolinks-compatibility) project.
101
116
 
102
117
 
103
118
  Installation
@@ -112,4 +127,4 @@ Installation
112
127
  Credits
113
128
  -------
114
129
 
115
- Thanks to Chris Wanstrath for his original work on Pjax. Thanks to Sam Stephenson and Josh Peek for their additional work on Pjax and Stacker and their help with getting Turbolinks released. Thanks to David Estes for handling the lion's share of post-release issues and feature requests. And thanks to everyone else who's fixed or reported an issue!
130
+ Thanks to Chris Wanstrath for his original work on Pjax. Thanks to Sam Stephenson and Josh Peek for their additional work on Pjax and Stacker and their help with getting Turbolinks released. Thanks to David Estes and Nick Reed for handling the lion's share of post-release issues and feature requests. And thanks to everyone else who's fixed or reported an issue!
@@ -22,17 +22,16 @@ fetchReplacement = (url) ->
22
22
  # Remove hash from url to ensure IE 10 compatibility
23
23
  safeUrl = removeHash url
24
24
 
25
- xhr.abort() if xhr
25
+ xhr?.abort()
26
26
  xhr = new XMLHttpRequest
27
27
  xhr.open 'GET', safeUrl, true
28
28
  xhr.setRequestHeader 'Accept', 'text/html, application/xhtml+xml, application/xml'
29
29
  xhr.setRequestHeader 'X-XHR-Referer', referer
30
- xhr.onabort = ->
31
- xhr = null
32
- xhr.onload = =>
33
- doc = createDocument xhr.responseText
34
30
 
35
- if assetsChanged doc
31
+ xhr.onload = =>
32
+ triggerEvent 'page:receive'
33
+
34
+ if invalidContent(xhr) or assetsChanged (doc = createDocument xhr.responseText)
36
35
  document.location.reload()
37
36
  else
38
37
  changePage extractTitleAndBody(doc)...
@@ -42,11 +41,10 @@ fetchReplacement = (url) ->
42
41
  else
43
42
  resetScrollPosition()
44
43
  triggerEvent 'page:load'
45
- xhr = null
46
44
 
47
- xhr.onerror = ->
48
- document.location.href = url
49
- xhr = null
45
+ xhr.onloadend = -> xhr = null
46
+ xhr.onabort = -> rememberCurrentUrl()
47
+ xhr.onerror = -> document.location.href = url
50
48
 
51
49
  xhr.send()
52
50
 
@@ -54,6 +52,7 @@ fetchHistory = (state) ->
54
52
  cacheCurrentPage()
55
53
 
56
54
  if page = pageCache[state.position]
55
+ xhr?.abort()
57
56
  changePage page.title, page.body
58
57
  recallScrollPosition page
59
58
  triggerEvent 'page:restore'
@@ -80,12 +79,14 @@ constrainPageCacheTo = (limit) ->
80
79
  changePage = (title, body, runScripts) ->
81
80
  document.title = title
82
81
  document.documentElement.replaceChild body, document.body
82
+ removeNoscriptTags()
83
83
  executeScriptTags() if runScripts
84
84
  currentState = window.history.state
85
85
  triggerEvent 'page:change'
86
86
 
87
87
  executeScriptTags = ->
88
- for script in document.body.getElementsByTagName 'script' when script.type in ['', 'text/javascript']
88
+ scripts = Array::slice.call document.body.getElementsByTagName 'script'
89
+ for script in scripts when script.type in ['', 'text/javascript']
89
90
  copy = document.createElement 'script'
90
91
  copy.setAttribute attr.name, attr.value for attr in script.attributes
91
92
  copy.appendChild document.createTextNode script.innerHTML
@@ -93,6 +94,9 @@ executeScriptTags = ->
93
94
  parentNode.removeChild script
94
95
  parentNode.insertBefore copy, nextSibling
95
96
 
97
+ removeNoscriptTags = ->
98
+ noscriptTags = Array::slice.call document.body.getElementsByTagName 'noscript'
99
+ noscript.parentNode.removeChild noscript for noscript in noscriptTags
96
100
 
97
101
  reflectNewUrl = (url) ->
98
102
  if url isnt document.location.href
@@ -136,6 +140,9 @@ triggerEvent = (name) ->
136
140
  document.dispatchEvent event
137
141
 
138
142
 
143
+ invalidContent = (xhr) ->
144
+ !xhr.getResponseHeader('Content-Type').match /^(?:text\/html|application\/xhtml\+xml|application\/xml)(?:;|$)/
145
+
139
146
  extractTrackAssets = (doc) ->
140
147
  (node.src || node.href) for node in doc.head.childNodes when node.getAttribute?('data-turbolinks-track')?
141
148
 
@@ -156,6 +163,11 @@ browserCompatibleDocumentParser = ->
156
163
  createDocumentUsingParser = (html) ->
157
164
  (new DOMParser).parseFromString html, 'text/html'
158
165
 
166
+ createDocumentUsingDOM = (html) ->
167
+ doc = document.implementation.createHTMLDocument ''
168
+ doc.documentElement.innerHTML = html
169
+ doc
170
+
159
171
  createDocumentUsingWrite = (html) ->
160
172
  doc = document.implementation.createHTMLDocument ''
161
173
  doc.open 'replace'
@@ -163,19 +175,32 @@ browserCompatibleDocumentParser = ->
163
175
  doc.close()
164
176
  doc
165
177
 
166
- if window.DOMParser
167
- testDoc = createDocumentUsingParser '<html><body><p>test'
168
-
169
- if testDoc?.body?.childNodes.length is 1
170
- createDocumentUsingParser
171
- else
172
- createDocumentUsingWrite
178
+ # Use createDocumentUsingParser if DOMParser is defined and natively
179
+ # supports 'text/html' parsing (Firefox 12+, IE 10)
180
+ #
181
+ # Use createDocumentUsingDOM if createDocumentUsingParser throws an exception
182
+ # due to unsupported type 'text/html' (Firefox < 12, Opera)
183
+ #
184
+ # Use createDocumentUsingWrite if:
185
+ # - DOMParser isn't defined
186
+ # - createDocumentUsingParser returns null due to unsupported type 'text/html' (Chrome, Safari)
187
+ # - createDocumentUsingDOM doesn't create a valid HTML document (safeguarding against potential edge cases)
188
+ try
189
+ if window.DOMParser
190
+ testDoc = createDocumentUsingParser '<html><body><p>test'
191
+ createDocumentUsingParser
192
+ catch e
193
+ testDoc = createDocumentUsingDOM '<html><body><p>test'
194
+ createDocumentUsingDOM
195
+ finally
196
+ unless testDoc?.body?.childNodes.length is 1
197
+ return createDocumentUsingWrite
173
198
 
174
199
 
175
200
  installClickHandlerLast = (event) ->
176
201
  unless event.defaultPrevented
177
- document.removeEventListener 'click', handleClick
178
- document.addEventListener 'click', handleClick
202
+ document.removeEventListener 'click', handleClick, false
203
+ document.addEventListener 'click', handleClick, false
179
204
 
180
205
  handleClick = (event) ->
181
206
  unless event.defaultPrevented
@@ -198,7 +223,8 @@ anchoredLink = (link) ->
198
223
  (link.href is location.href + '#')
199
224
 
200
225
  nonHtmlLink = (link) ->
201
- link.href.match(/\.[a-z]+(\?.*)?$/g) and not link.href.match(/\.html?(\?.*)?$/g)
226
+ url = removeHash link
227
+ url.match(/\.[a-z]+(\?.*)?$/g) and not url.match(/\.html?(\?.*)?$/g)
202
228
 
203
229
  noTurbolink = (link) ->
204
230
  until ignore or link is document
@@ -220,6 +246,7 @@ initializeTurbolinks = ->
220
246
  document.addEventListener 'click', installClickHandlerLast, true
221
247
  window.addEventListener 'popstate', (event) ->
222
248
  fetchHistory event.state if event.state?.turbolinks
249
+ , false
223
250
 
224
251
  browserSupportsPushState =
225
252
  window.history and window.history.pushState and window.history.replaceState and window.history.state != undefined
data/lib/turbolinks.rb CHANGED
@@ -26,12 +26,37 @@ module Turbolinks
26
26
  cookies[:request_method] = request.request_method
27
27
  end
28
28
  end
29
-
29
+
30
+ module XDomainBlocker
31
+ private
32
+ def same_origin?(a, b)
33
+ a = URI.parse(a)
34
+ b = URI.parse(b)
35
+ [a.scheme, a.host, a.port] == [b.scheme, b.host, b.port]
36
+ end
37
+
38
+ def abort_xdomain_redirect
39
+ to_uri = response.headers['Location'] || ""
40
+ current = request.headers['X-XHR-Referer'] || ""
41
+ unless to_uri.blank? || current.blank? || same_origin?(current, to_uri)
42
+ self.status = 403
43
+ end
44
+ end
45
+ end
46
+
30
47
  class Engine < ::Rails::Engine
31
48
  initializer :turbolinks_xhr_headers do |config|
32
49
  ActionController::Base.class_eval do
33
- include XHRHeaders, Cookies
50
+ include XHRHeaders, Cookies, XDomainBlocker
34
51
  before_filter :set_xhr_current_location, :set_request_method_cookie
52
+ after_filter :abort_xdomain_redirect
53
+ end
54
+
55
+ ActionDispatch::Request.class_eval do
56
+ def referer
57
+ self.headers['X-XHR-Referer'] || super
58
+ end
59
+ alias referrer referer
35
60
  end
36
61
  end
37
62
  end
metadata CHANGED
@@ -1,30 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: turbolinks
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
5
- prerelease:
4
+ version: 1.1.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - David Heinemeier Hansson
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-01-11 00:00:00.000000000 Z
11
+ date: 2013-03-24 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: coffee-rails
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0'
30
27
  description:
@@ -46,27 +43,26 @@ files:
46
43
  - test/reload.html
47
44
  homepage:
48
45
  licenses: []
46
+ metadata: {}
49
47
  post_install_message:
50
48
  rdoc_options: []
51
49
  require_paths:
52
50
  - lib
53
51
  required_ruby_version: !ruby/object:Gem::Requirement
54
- none: false
55
52
  requirements:
56
- - - ! '>='
53
+ - - '>='
57
54
  - !ruby/object:Gem::Version
58
55
  version: '0'
59
56
  required_rubygems_version: !ruby/object:Gem::Requirement
60
- none: false
61
57
  requirements:
62
- - - ! '>='
58
+ - - '>='
63
59
  - !ruby/object:Gem::Version
64
60
  version: '0'
65
61
  requirements: []
66
62
  rubyforge_project:
67
- rubygems_version: 1.8.23
63
+ rubygems_version: 2.0.0
68
64
  signing_key:
69
- specification_version: 3
65
+ specification_version: 4
70
66
  summary: Turbolinks makes following links in your web application faster (use with
71
67
  Rails Asset Pipeline)
72
68
  test_files: []