turbolinks 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  Turbolinks
2
2
  ===========
3
3
 
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.
4
+ Turbolinks makes following links in your web application faster. Instead of letting the browser recompile the JavaScript and CSS between each page change, it keeps the current page instance alive and replaces only the body and the title in the head. Think CGI vs persistent process.
5
5
 
6
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
- By default, all internal links will be funneled through Turbolinks, but you can opt out by marking links with data-no-turbolink.
8
+ Do note that this of course means that you'll have a long-running, persistent session with maintained state. That's what's making it so fast. But it also means that you may have to pay additional care not to leak memory or otherwise bloat that long-running state. That should rarely be a problem unless you're doing something really funky, but you do have to be aware of it. Your memory leaking sins will not be swept away automatically by the cleansing page change any more.
9
9
 
10
10
 
11
11
  How much faster is it really?
@@ -15,6 +15,8 @@ It depends. The more CSS and JavaScript you have, the bigger the benefit of not
15
15
 
16
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
17
 
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
+
18
20
 
19
21
  No jQuery or any other framework
20
22
  --------------------------------
@@ -25,24 +27,48 @@ Turbolinks is designed to be as light-weight as possible (so you won't think twi
25
27
  Events
26
28
  ------
27
29
 
28
- Since pages will change without a full reload with Turbolinks, you can't by default rely on `dom:loaded` to trigger your JavaScript code. Instead, Turbolinks gives you a range of events to deal with the lifecycle of the page:
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 gives you a range of events to deal with the lifecycle of the page:
29
31
 
30
- * `page:fetch` ...starting to fetch the target page.
31
- * `page:load` ...fetched page is being retrieved fresh from the server.
32
- * `page:restore` ...fetched page is being retrieved from the 10-slot client-side cache.
33
- * `page:change` ...page has changed to the newly fetched version.
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.
34
36
 
35
37
  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).
36
38
 
37
39
 
40
+ Opting out of Turbolinks
41
+ ------------------------
42
+
43
+ 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.
44
+
45
+ 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.
46
+
47
+ 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.
48
+
49
+
50
+ Asset change detection
51
+ ----------------------
52
+
53
+ Turbolinks will remember what assets were linked or referenced in the head of the initial page. If those assets change, either more or added or existing ones have a new URL, the page will do a full reload instead of going through Turbolinks. This ensures that all Turbolinks sessions will always be running off your latest JavaScript and CSS.
54
+
55
+ When this happens, you'll technically be requesting the same page twice. Once through Turbolinks to detect that the assets changed, and then again when we do a full redirect to that page.
56
+
57
+
58
+ Evaluating script tags
59
+ ----------------------
60
+
61
+ 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.
62
+
63
+
38
64
  Triggering a Turbolinks visit manually
39
65
  ---------------------------------------
40
66
 
41
67
  You can use `Turbolinks.visit(path)` to go to a URL through Turbolinks.
42
68
 
43
69
 
44
- Available only for pushState browsers
45
- -------------------------------------
70
+ Full speed for pushState browsers, graceful fallback for everything else
71
+ ------------------------------------------------------------------------
46
72
 
47
73
  Like pjax, this naturally only works with browsers capable of pushState. But of course we fall back gracefully to full page reloads for browsers that do not support it.
48
74
 
@@ -56,8 +82,7 @@ Installation
56
82
  1. Restart your server and you're now using turbolinks!
57
83
 
58
84
 
59
- Work left to do
60
- ---------------
85
+ Credits
86
+ -------
61
87
 
62
- * CSS/JS asset change detection and reload
63
- * Add proper unit tests
88
+ 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!
@@ -1,7 +1,8 @@
1
- pageCache = []
2
- currentState = null
3
1
  initialized = false
2
+ currentState = null
4
3
  referer = document.location.href
4
+ assets = []
5
+ pageCache = []
5
6
 
6
7
 
7
8
  visit = (url) ->
@@ -15,15 +16,25 @@ visit = (url) ->
15
16
 
16
17
  fetchReplacement = (url) ->
17
18
  triggerEvent 'page:fetch'
19
+
18
20
  xhr = new XMLHttpRequest
19
21
  xhr.open 'GET', url, true
20
22
  xhr.setRequestHeader 'Accept', 'text/html, application/xhtml+xml, application/xml'
21
23
  xhr.setRequestHeader 'X-XHR-Referer', referer
22
- xhr.onload = ->
23
- changePage extractTitleAndBody(xhr.responseText)...
24
- reflectRedirectedUrl xhr
25
- triggerEvent 'page:load'
24
+
25
+ xhr.onload = =>
26
+ doc = createDocument xhr.responseText
27
+
28
+ if assetsChanged doc
29
+ document.location.href = url
30
+ else
31
+ changePage extractTitleAndBody(doc)...
32
+ reflectRedirectedUrl xhr
33
+ resetScrollPosition()
34
+ triggerEvent 'page:load'
35
+
26
36
  xhr.onabort = -> console.log 'Aborted turbolink fetch!'
37
+
27
38
  xhr.send()
28
39
 
29
40
  fetchHistory = (state) ->
@@ -60,7 +71,7 @@ changePage = (title, body) ->
60
71
  triggerEvent 'page:change'
61
72
 
62
73
  executeScriptTags = ->
63
- eval(script.innerHTML) for script in document.body.getElementsByTagName 'script'
74
+ eval(script.innerHTML) for script in document.body.getElementsByTagName 'script' when script.type in ['', 'text/javascript']
64
75
 
65
76
 
66
77
  reflectNewUrl = (url) ->
@@ -78,15 +89,22 @@ rememberCurrentUrl = ->
78
89
  rememberCurrentState = ->
79
90
  currentState = window.history.state
80
91
 
92
+ rememberCurrentAssets = ->
93
+ assets = extractAssets document
94
+
81
95
  rememberInitialPage = ->
82
96
  unless initialized
83
97
  rememberCurrentUrl()
84
98
  rememberCurrentState()
99
+ rememberCurrentAssets()
85
100
  initialized = true
86
101
 
87
102
  recallScrollPosition = (page) ->
88
103
  window.scrollTo page.positionX, page.positionY
89
104
 
105
+ resetScrollPosition = ->
106
+ window.scrollTo 0, 0
107
+
90
108
 
91
109
  triggerEvent = (name) ->
92
110
  event = document.createEvent 'Events'
@@ -94,8 +112,17 @@ triggerEvent = (name) ->
94
112
  document.dispatchEvent event
95
113
 
96
114
 
97
- extractTitleAndBody = (html) ->
98
- doc = createDocument html
115
+ extractAssets = (doc) ->
116
+ (node.src || node.href) for node in doc.head.childNodes when node.src or node.href
117
+
118
+ assetsChanged = (doc)->
119
+ intersection(extractAssets(doc), assets).length != assets.length
120
+
121
+ intersection = (a, b) ->
122
+ [a, b] = [b, a] if a.length > b.length
123
+ value for value in a when value in b
124
+
125
+ extractTitleAndBody = (doc) ->
99
126
  title = doc.querySelector 'title'
100
127
  [ title?.textContent, doc.body ]
101
128
 
@@ -128,7 +155,6 @@ handleClick = (event) ->
128
155
  unless event.defaultPrevented
129
156
  link = extractLink event
130
157
  if link.nodeName is 'A' and !ignoreClick(event, link)
131
- link = extractLink event
132
158
  visit link.href
133
159
  event.preventDefault()
134
160
 
@@ -161,18 +187,17 @@ nonStandardClick = (event) ->
161
187
  event.which > 1 or event.metaKey or event.ctrlKey or event.shiftKey or event.altKey
162
188
 
163
189
  ignoreClick = (event, link) ->
164
- crossOriginLink(link) or anchoredLink(link) or nonHtmlLink(link) or
165
- noTurbolink(link) or nonStandardClick(event)
190
+ crossOriginLink(link) or anchoredLink(link) or nonHtmlLink(link) or noTurbolink(link) or nonStandardClick(event)
166
191
 
167
192
 
168
193
  browserSupportsPushState =
169
194
  window.history and window.history.pushState and window.history.replaceState and window.history.state != undefined
170
195
 
171
196
  if browserSupportsPushState
197
+ document.addEventListener 'click', installClickHandlerLast, true
198
+
172
199
  window.addEventListener 'popstate', (event) ->
173
200
  fetchHistory event.state if event.state?.turbolinks
174
201
 
175
- document.addEventListener 'click', installClickHandlerLast, true
176
-
177
202
  # Call Turbolinks.visit(url) from client code
178
203
  @Turbolinks = { visit }
data/test/index.html CHANGED
@@ -16,6 +16,7 @@
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
18
  <li><a href="/other.html" onclick="if(!confirm('follow link?')) { return false}">Confirm Fire Order</a></li>
19
+ <li><a href="/reload.html"><span>Asset Change</span></a></li>
19
20
  <li><a href="/dummy.gif?12345">Query Param Image Link</a></li>
20
21
  <li><a href="#">Hash link</a></li>
21
22
  </ul>
data/test/reload.html ADDED
@@ -0,0 +1,18 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>Home</title>
6
+ <script type="text/javascript" src="/js/turbolinks.js?1"></script>
7
+ <script type="text/javascript">
8
+ document.addEventListener("page:change", function() {
9
+ console.log("page changed");
10
+ });
11
+ </script>
12
+ </head>
13
+ <body class="page-reload">
14
+ <ul>
15
+ <li><a href="/index.html">Home</a></li>
16
+ </ul>
17
+ </body>
18
+ </html>
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.0
4
+ version: 0.5.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-10-02 00:00:00.000000000 Z
12
+ date: 2012-10-03 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description:
15
15
  email: david@loudthinking.com
@@ -25,6 +25,7 @@ files:
25
25
  - test/dummy.gif
26
26
  - test/index.html
27
27
  - test/other.html
28
+ - test/reload.html
28
29
  homepage:
29
30
  licenses: []
30
31
  post_install_message: