turbolinks 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7189329adf13178c8d0734735e1affc9f7cabbcd
4
- data.tar.gz: ee154008520bd38704aa60fe9519fa71a74fb43b
3
+ metadata.gz: 81b3f537e66ca790fc7d3582de6eb2eab6d1b9b9
4
+ data.tar.gz: b9f0c3ed62c42062e678b92676012d08ad8decfc
5
5
  SHA512:
6
- metadata.gz: be97d3f28a06864e225e9d384d0aa714c175163410fd9079a4d3770e0d17d581b35bfa043b674480d4cf0ec17e76614615032d1fed2c4a652ce04e8ffdbe0248
7
- data.tar.gz: 6553c4009a6b45a5163456f31c6bab1dd33a3f49e7fa0513f346f4c781f8bee57660e5b4a8d7fbae7202a4b284b2e0a211a330bed69c73008d479aab7a66d5fb
6
+ metadata.gz: 34ec20469fdcfa96705c4b3b1639c963196568cf78c46f9d24083849d93c4193acb5cca47b28e2caf7edcc2907586e1793394aa04a80304afe607fd3d8f91429
7
+ data.tar.gz: faf3469bb52dc2adba9e96634946c5b5cc775c5be9a77ead70cde60a80a29b3737f3634b68d7e1d31a5bb6ab1bed3891bbd40219d99cdc26b89387fc08757892
data/README.md CHANGED
@@ -27,20 +27,26 @@ Turbolinks is designed to be as light-weight as possible (so you won't think twi
27
27
  Events
28
28
  ------
29
29
 
30
- Since pages will change without a full reload with Turbolinks, you can't by default rely on `DOMContentLoaded` to trigger your JavaScript code or jQuery.ready(). Instead, Turbolinks fires events on `document` to provide hooks into the lifecycle of the page:
30
+ With Turbolinks pages will change without a full reload, so you can't rely on `DOMContentLoaded` or `jQuery.ready()` to trigger your code. Instead Turbolinks fires events on `document` to provide hooks into the lifecycle of the page:
31
31
 
32
- * `page:fetch` starting to fetch the target page (only called if loading fresh, not from cache).
33
- * `page:load` fetched page is being retrieved fresh from the server.
34
- * `page:restore` fetched page is being retrieved from the 10-slot client-side cache.
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.
32
+ *Load* a fresh version of a page from the server:
33
+ * `page:fetch` starting to fetch a new target page
34
+ * `page:receive` the page has been fetched from the server, but not yet parsed
35
+ * `page:change` the page has been parsed and changed to the new version
36
+ * `page:load` is fired at the end of the loading process.
37
+
38
+ 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 restore page state from the cache when it's triggered. When `popstate` is fired the following process happens:
39
+
40
+ *Restore* a cached page from the client-side cache:
41
+ * `page:change` page has changed to the cached page.
42
+ * `page:restore` is fired at the end of restore process.
37
43
 
38
44
  So if you wanted to have a client-side spinner, you could listen for `page:fetch` to start it and `page:receive` to stop it.
39
45
 
40
46
  document.addEventListener("page:fetch", startSpinner);
41
47
  document.addEventListener("page:receive", stopSpinner);
42
48
 
43
- 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).
49
+ DOM transformations that are idempotent are best. If you have transformations that are not, hook them to happen only on `page:load` instead of `page:change` (as that would run them again on the cached pages).
44
50
 
45
51
  Initialization
46
52
  --------------
@@ -62,6 +68,13 @@ Opting out of Turbolinks
62
68
 
63
69
  By default, all internal HTML links will be funneled through Turbolinks, but you can opt out by marking links or their parent container with `data-no-turbolink`. For example, if you mark a div with `data-no-turbolink`, then all links inside of that div will be treated as regular links. If you mark the body, every link on that entire page will be treated as regular links.
64
70
 
71
+ ```html
72
+ <a href="/">Home (via Turbolinks)</a>
73
+ <div id="some-div" data-no-turbolink>
74
+ <a href="/">Home (without Turbolinks)</a>
75
+ </div>
76
+ ```
77
+
65
78
  Note that internal links to files not ending in .html, or having no extension, will automatically be opted out of Turbolinks. So links to /images/panda.gif will just work as expected.
66
79
 
67
80
  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.
@@ -97,7 +110,13 @@ Evaluating script tags
97
110
 
98
111
  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.
99
112
 
100
- 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.
113
+ 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. If you have any script tags in the body you do not want to be re-evaluated then you can set the `data-turbolinks-eval` attribute to `false`:
114
+
115
+ ```html
116
+ <script type="text/javascript" data-turbolinks-eval=false>
117
+ console.log("I'm only run once on the initial page load");
118
+ </script>
119
+ ```
101
120
 
102
121
  Triggering a Turbolinks visit manually
103
122
  ---------------------------------------
@@ -116,7 +135,7 @@ Compatibility
116
135
 
117
136
  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.
118
137
 
119
- 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.
138
+ 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.io/turbolinks-compatibility) project.
120
139
 
121
140
 
122
141
  Installation
@@ -28,14 +28,12 @@ fetchReplacement = (url) ->
28
28
  xhr.setRequestHeader 'Accept', 'text/html, application/xhtml+xml, application/xml'
29
29
  xhr.setRequestHeader 'X-XHR-Referer', referer
30
30
 
31
- xhr.onload = =>
31
+ xhr.onload = ->
32
32
  triggerEvent 'page:receive'
33
-
34
- if invalidContent(xhr) or assetsChanged (doc = createDocument xhr.responseText)
35
- document.location.reload()
36
- else
33
+
34
+ if doc = validateResponse()
37
35
  changePage extractTitleAndBody(doc)...
38
- reflectRedirectedUrl xhr
36
+ reflectRedirectedUrl()
39
37
  if document.location.hash
40
38
  document.location.href = document.location.href
41
39
  else
@@ -87,7 +85,7 @@ changePage = (title, body, csrfToken, runScripts) ->
87
85
  triggerEvent 'page:change'
88
86
 
89
87
  executeScriptTags = ->
90
- scripts = Array::slice.call document.body.getElementsByTagName 'script'
88
+ scripts = Array::slice.call document.body.querySelectorAll 'script:not([data-turbolinks-eval="false"])'
91
89
  for script in scripts when script.type in ['', 'text/javascript']
92
90
  copy = document.createElement 'script'
93
91
  copy.setAttribute attr.name, attr.value for attr in script.attributes
@@ -107,9 +105,10 @@ reflectNewUrl = (url) ->
107
105
  referer = document.location.href
108
106
  window.history.pushState { turbolinks: true, position: currentState.position + 1 }, '', url
109
107
 
110
- reflectRedirectedUrl = (xhr) ->
111
- if (location = xhr.getResponseHeader 'X-XHR-Current-Location') and location isnt document.location.pathname + document.location.search
112
- window.history.replaceState currentState, '', location + document.location.hash
108
+ reflectRedirectedUrl = ->
109
+ if location = xhr.getResponseHeader 'X-XHR-Redirected-To'
110
+ preservedHash = if removeHash(location) is location then document.location.hash else ''
111
+ window.history.replaceState currentState, '', location + preservedHash
113
112
 
114
113
  rememberCurrentUrl = ->
115
114
  window.history.replaceState { turbolinks: true, position: Date.now() }, '', document.location.href
@@ -137,27 +136,42 @@ removeHash = (url) ->
137
136
  link.href = url
138
137
  link.href.replace link.hash, ''
139
138
 
140
-
141
139
  triggerEvent = (name) ->
142
140
  event = document.createEvent 'Events'
143
141
  event.initEvent name, true, true
144
142
  document.dispatchEvent event
145
143
 
146
144
 
147
- invalidContent = (xhr) ->
148
- !xhr.getResponseHeader('Content-Type').match /^(?:text\/html|application\/xhtml\+xml|application\/xml)(?:;|$)/
145
+ validateResponse = ->
146
+ clientOrServerError = ->
147
+ 400 <= xhr.status < 600
148
+
149
+ invalidContent = ->
150
+ !xhr.getResponseHeader('Content-Type').match /^(?:text\/html|application\/xhtml\+xml|application\/xml)(?:;|$)/
151
+
152
+ extractTrackAssets = (doc) ->
153
+ (node.src || node.href) for node in doc.head.childNodes when node.getAttribute?('data-turbolinks-track')?
149
154
 
150
- extractTrackAssets = (doc) ->
151
- (node.src || node.href) for node in doc.head.childNodes when node.getAttribute?('data-turbolinks-track')?
155
+ assetsChanged = (doc) ->
156
+ loadedAssets ||= extractTrackAssets document
157
+ fetchedAssets = extractTrackAssets doc
158
+ fetchedAssets.length isnt loadedAssets.length or intersection(fetchedAssets, loadedAssets).length isnt loadedAssets.length
152
159
 
153
- assetsChanged = (doc) ->
154
- loadedAssets ||= extractTrackAssets document
155
- fetchedAssets = extractTrackAssets doc
156
- fetchedAssets.length isnt loadedAssets.length or intersection(fetchedAssets, loadedAssets).length isnt loadedAssets.length
160
+ intersection = (a, b) ->
161
+ [a, b] = [b, a] if a.length > b.length
162
+ value for value in a when value in b
157
163
 
158
- intersection = (a, b) ->
159
- [a, b] = [b, a] if a.length > b.length
160
- value for value in a when value in b
164
+ if clientOrServerError()
165
+ # Workaround for WebKit bug (https://bugs.webkit.org/show_bug.cgi?id=93506)
166
+ url = document.location.href
167
+ window.history.replaceState null, '', '#'
168
+ window.location.replace url
169
+ false
170
+ else if invalidContent() or assetsChanged (doc = createDocument xhr.responseText)
171
+ window.location.reload()
172
+ false
173
+ else
174
+ doc
161
175
 
162
176
  extractTitleAndBody = (doc) ->
163
177
  title = doc.querySelector 'title'
@@ -167,12 +181,12 @@ CSRFToken =
167
181
  get: (doc = document) ->
168
182
  node: tag = doc.querySelector 'meta[name="csrf-token"]'
169
183
  token: tag?.getAttribute? 'content'
170
-
184
+
171
185
  update: (latest) ->
172
186
  current = @get()
173
187
  if current.token? and latest? and current.token isnt latest
174
188
  current.node.setAttribute 'content', latest
175
-
189
+
176
190
  browserCompatibleDocumentParser = ->
177
191
  createDocumentUsingParser = (html) ->
178
192
  (new DOMParser).parseFromString html, 'text/html'
data/lib/turbolinks.rb CHANGED
@@ -8,15 +8,18 @@ module Turbolinks
8
8
 
9
9
  private
10
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
11
+ session[:_turbolinks_redirect_to] =
12
+ if options == :back && request.headers["X-XHR-Referer"]
13
+ _compute_redirect_to_location_without_xhr_referer(request.headers["X-XHR-Referer"])
14
+ else
15
+ _compute_redirect_to_location_without_xhr_referer(options)
16
+ end
16
17
  end
17
18
 
18
- def set_xhr_current_location
19
- response.headers['X-XHR-Current-Location'] = request.fullpath
19
+ def set_xhr_redirected_to
20
+ if session[:_turbolinks_redirect_to]
21
+ response.headers['X-XHR-Redirected-To'] = session.delete :_turbolinks_redirect_to
22
+ end
20
23
  end
21
24
  end
22
25
 
@@ -30,8 +33,8 @@ module Turbolinks
30
33
  module XDomainBlocker
31
34
  private
32
35
  def same_origin?(a, b)
33
- a = URI.parse(a)
34
- b = URI.parse(b)
36
+ a = URI.parse URI.escape(a)
37
+ b = URI.parse URI.escape(b)
35
38
  [a.scheme, a.host, a.port] == [b.scheme, b.host, b.port]
36
39
  end
37
40
 
@@ -48,10 +51,10 @@ module Turbolinks
48
51
  initializer :turbolinks_xhr_headers do |config|
49
52
  ActionController::Base.class_eval do
50
53
  include XHRHeaders, Cookies, XDomainBlocker
51
- before_filter :set_xhr_current_location, :set_request_method_cookie
54
+ before_filter :set_xhr_redirected_to, :set_request_method_cookie
52
55
  after_filter :abort_xdomain_redirect
53
56
  end
54
-
57
+
55
58
  ActionDispatch::Request.class_eval do
56
59
  def referer
57
60
  self.headers['X-XHR-Referer'] || super
data/test/config.ru CHANGED
@@ -11,4 +11,10 @@ map "/js" do
11
11
  run Assets
12
12
  end
13
13
 
14
- run Rack::Directory.new(File.join(Root, "test"))
14
+ map "/500" do
15
+ # throw Internal Server Error (500)
16
+ end
17
+
18
+ map "/" do
19
+ run Rack::Directory.new(File.join(Root, "test"))
20
+ end
data/test/index.html CHANGED
@@ -21,12 +21,17 @@
21
21
  <li><a href="#">Hash link</a></li>
22
22
  <li><a href="/reload.html#foo">New assets track with hash link</a></li>
23
23
  <li><h5>If you stop the server or go into airplane/offline mode</h5></li>
24
- <li><a href="/doesnotexist.html">A page that errors should error out</a></li>
24
+ <li><a href="/doesnotexist.html">A page with client error (4xx, rfc2616 sec. 10.4) should error out</a></li>
25
+ <li><a href="/500">Also server errors (5xx, rfc2616 sec. 10.5) should error out</a></li>
25
26
  <li><a href="/fallback.html">A page that has a fallback in appcache should fallback</a></li>
26
27
  </ul>
27
28
 
28
29
  <div style="background:#ccc;height:5000px;width:200px;">
29
30
  </div>
30
31
  <iframe height='1' scrolling='no' src='/offline.html' style='display: none;' width='1'></iframe>
32
+
33
+ <script type="text/javascript" data-turbolinks-eval=false>
34
+ console.log("turbolinks-eval-false script fired. This should only happen on the initial page load.");
35
+ </script>
31
36
  </body>
32
37
  </html>
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: 1.1.1
4
+ version: 1.2.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: 2013-04-03 00:00:00.000000000 Z
11
+ date: 2013-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: coffee-rails