turbolinks-js 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,12 +1,19 @@
1
1
  // Generated by CoffeeScript 1.3.3
2
2
  (function() {
3
- var anchoredLink, browserSupportsPushState, cacheCurrentPage, changePage, constrainPageCacheTo, createDocument, crossOriginLink, currentState, extractLink, extractTitleAndBody, fetchHistory, fetchReplacement, handleClick, ignoreClick, initialized, newTabClick, noTurbolink, nonHtmlLink, pageCache, recallScrollPosition, reflectNewUrl, rememberCurrentState, rememberCurrentUrl, rememberInitialPage, remoteLink, samePageLink, triggerEvent, visit;
3
+ var anchoredLink, assets, assetsChanged, browserCompatibleDocumentParser, browserSupportsPushState, cacheCurrentPage, changePage, constrainPageCacheTo, createDocument, crossOriginLink, currentState, executeScriptTags, extractAssets, extractLink, extractTitleAndBody, fetchHistory, fetchReplacement, handleClick, ignoreClick, initialized, installClickHandlerLast, intersection, noTurbolink, nonHtmlLink, nonStandardClick, pageCache, recallScrollPosition, referer, reflectNewUrl, reflectRedirectedUrl, rememberCurrentAssets, rememberCurrentState, rememberCurrentUrl, rememberInitialPage, resetScrollPosition, samePageLink, triggerEvent, visit,
4
+ __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
4
5
 
5
- pageCache = [];
6
+ initialized = false;
6
7
 
7
8
  currentState = null;
8
9
 
9
- initialized = false;
10
+ referer = document.location.href;
11
+
12
+ assets = [];
13
+
14
+ pageCache = [];
15
+
16
+ createDocument = null;
10
17
 
11
18
  visit = function(url) {
12
19
  if (browserSupportsPushState) {
@@ -19,14 +26,24 @@
19
26
  };
20
27
 
21
28
  fetchReplacement = function(url) {
22
- var xhr;
29
+ var xhr,
30
+ _this = this;
23
31
  triggerEvent('page:fetch');
24
32
  xhr = new XMLHttpRequest;
25
33
  xhr.open('GET', url, true);
26
34
  xhr.setRequestHeader('Accept', 'text/html, application/xhtml+xml, application/xml');
35
+ xhr.setRequestHeader('X-XHR-Referer', referer);
27
36
  xhr.onload = function() {
28
- changePage.apply(null, extractTitleAndBody(xhr.responseText));
29
- return triggerEvent('page:load');
37
+ var doc;
38
+ doc = createDocument(xhr.responseText);
39
+ if (assetsChanged(doc)) {
40
+ return document.location.href = url;
41
+ } else {
42
+ changePage.apply(null, extractTitleAndBody(doc));
43
+ reflectRedirectedUrl(xhr);
44
+ resetScrollPosition();
45
+ return triggerEvent('page:load');
46
+ }
30
47
  };
31
48
  xhr.onabort = function() {
32
49
  return console.log('Aborted turbolink fetch!');
@@ -38,7 +55,7 @@
38
55
  var page;
39
56
  cacheCurrentPage();
40
57
  if (page = pageCache[state.position]) {
41
- changePage(page.title, page.body.cloneNode(true));
58
+ changePage(page.title, page.body);
42
59
  recallScrollPosition(page);
43
60
  return triggerEvent('page:restore');
44
61
  } else {
@@ -59,20 +76,33 @@
59
76
  };
60
77
 
61
78
  constrainPageCacheTo = function(limit) {
62
- if (currentState.position === window.history.length - 1) {
63
- return delete pageCache[currentState.position - limit];
64
- }
79
+ return delete pageCache[currentState.position - limit];
65
80
  };
66
81
 
67
82
  changePage = function(title, body) {
68
83
  document.title = title;
69
84
  document.documentElement.replaceChild(body, document.body);
85
+ executeScriptTags();
70
86
  currentState = window.history.state;
71
87
  return triggerEvent('page:change');
72
88
  };
73
89
 
90
+ executeScriptTags = function() {
91
+ var script, _i, _len, _ref, _ref1, _results;
92
+ _ref = document.body.getElementsByTagName('script');
93
+ _results = [];
94
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
95
+ script = _ref[_i];
96
+ if ((_ref1 = script.type) === '' || _ref1 === 'text/javascript') {
97
+ _results.push(eval(script.innerHTML));
98
+ }
99
+ }
100
+ return _results;
101
+ };
102
+
74
103
  reflectNewUrl = function(url) {
75
104
  if (url !== document.location.href) {
105
+ referer = document.location.href;
76
106
  return window.history.pushState({
77
107
  turbolinks: true,
78
108
  position: currentState.position + 1
@@ -80,6 +110,13 @@
80
110
  }
81
111
  };
82
112
 
113
+ reflectRedirectedUrl = function(xhr) {
114
+ var location;
115
+ if ((location = xhr.getResponseHeader('X-XHR-Current-Location'))) {
116
+ return window.history.replaceState(currentState, '', location);
117
+ }
118
+ };
119
+
83
120
  rememberCurrentUrl = function() {
84
121
  return window.history.replaceState({
85
122
  turbolinks: true,
@@ -91,10 +128,16 @@
91
128
  return currentState = window.history.state;
92
129
  };
93
130
 
131
+ rememberCurrentAssets = function() {
132
+ return assets = extractAssets(document);
133
+ };
134
+
94
135
  rememberInitialPage = function() {
95
136
  if (!initialized) {
96
137
  rememberCurrentUrl();
97
138
  rememberCurrentState();
139
+ rememberCurrentAssets();
140
+ createDocument = browserCompatibleDocumentParser();
98
141
  return initialized = true;
99
142
  }
100
143
  };
@@ -103,6 +146,10 @@
103
146
  return window.scrollTo(page.positionX, page.positionY);
104
147
  };
105
148
 
149
+ resetScrollPosition = function() {
150
+ return window.scrollTo(0, 0);
151
+ };
152
+
106
153
  triggerEvent = function(name) {
107
154
  var event;
108
155
  event = document.createEvent('Events');
@@ -110,14 +157,45 @@
110
157
  return document.dispatchEvent(event);
111
158
  };
112
159
 
113
- extractTitleAndBody = function(html) {
114
- var doc, title;
115
- doc = createDocument(html);
160
+ extractAssets = function(doc) {
161
+ var node, _i, _len, _ref, _results;
162
+ _ref = doc.head.childNodes;
163
+ _results = [];
164
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
165
+ node = _ref[_i];
166
+ if (node.src || node.href) {
167
+ _results.push(node.src || node.href);
168
+ }
169
+ }
170
+ return _results;
171
+ };
172
+
173
+ assetsChanged = function(doc) {
174
+ return intersection(extractAssets(doc), assets).length !== assets.length;
175
+ };
176
+
177
+ intersection = function(a, b) {
178
+ var value, _i, _len, _ref, _results;
179
+ if (a.length > b.length) {
180
+ _ref = [b, a], a = _ref[0], b = _ref[1];
181
+ }
182
+ _results = [];
183
+ for (_i = 0, _len = a.length; _i < _len; _i++) {
184
+ value = a[_i];
185
+ if (__indexOf.call(b, value) >= 0) {
186
+ _results.push(value);
187
+ }
188
+ }
189
+ return _results;
190
+ };
191
+
192
+ extractTitleAndBody = function(doc) {
193
+ var title;
116
194
  title = doc.querySelector('title');
117
195
  return [title != null ? title.textContent : void 0, doc.body];
118
196
  };
119
197
 
120
- createDocument = (function() {
198
+ browserCompatibleDocumentParser = function() {
121
199
  var createDocumentUsingParser, createDocumentUsingWrite, testDoc, _ref;
122
200
  createDocumentUsingParser = function(html) {
123
201
  return (new DOMParser).parseFromString(html, 'text/html');
@@ -138,14 +216,23 @@
138
216
  } else {
139
217
  return createDocumentUsingWrite;
140
218
  }
141
- })();
219
+ };
220
+
221
+ installClickHandlerLast = function(event) {
222
+ if (!event.defaultPrevented) {
223
+ document.removeEventListener('click', handleClick);
224
+ return document.addEventListener('click', handleClick);
225
+ }
226
+ };
142
227
 
143
228
  handleClick = function(event) {
144
229
  var link;
145
- link = extractLink(event);
146
- if (link.nodeName === 'A' && !ignoreClick(event, link)) {
147
- visit(link.href);
148
- return event.preventDefault();
230
+ if (!event.defaultPrevented) {
231
+ link = extractLink(event);
232
+ if (link.nodeName === 'A' && !ignoreClick(event, link)) {
233
+ visit(link.href);
234
+ return event.preventDefault();
235
+ }
149
236
  }
150
237
  };
151
238
 
@@ -174,34 +261,33 @@
174
261
  return link.href.match(/\.[a-z]+(\?.*)?$/g) && !link.href.match(/\.html?(\?.*)?$/g);
175
262
  };
176
263
 
177
- remoteLink = function(link) {
178
- return link.getAttribute('data-remote') != null;
179
- };
180
-
181
264
  noTurbolink = function(link) {
182
- return link.getAttribute('data-no-turbolink') != null;
265
+ var ignore;
266
+ while (!(ignore || link === document)) {
267
+ ignore = link.getAttribute('data-no-turbolink') != null;
268
+ link = link.parentNode;
269
+ }
270
+ return ignore;
183
271
  };
184
272
 
185
- newTabClick = function(event) {
186
- return event.which > 1 || event.metaKey || event.ctrlKey;
273
+ nonStandardClick = function(event) {
274
+ return event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;
187
275
  };
188
276
 
189
277
  ignoreClick = function(event, link) {
190
- return samePageLink(link) || crossOriginLink(link) || anchoredLink(link) || nonHtmlLink(link) || remoteLink(link) || noTurbolink(link) || newTabClick(event);
278
+ return crossOriginLink(link) || anchoredLink(link) || nonHtmlLink(link) || noTurbolink(link) || nonStandardClick(event);
191
279
  };
192
280
 
193
- browserSupportsPushState = window.history && window.history.pushState && window.history.replaceState;
281
+ browserSupportsPushState = window.history && window.history.pushState && window.history.replaceState && window.history.state !== void 0;
194
282
 
195
283
  if (browserSupportsPushState) {
284
+ document.addEventListener('click', installClickHandlerLast, true);
196
285
  window.addEventListener('popstate', function(event) {
197
286
  var _ref;
198
287
  if ((_ref = event.state) != null ? _ref.turbolinks : void 0) {
199
288
  return fetchHistory(event.state);
200
289
  }
201
290
  });
202
- document.addEventListener('click', function(event) {
203
- return handleClick(event);
204
- });
205
291
  }
206
292
 
207
293
  this.Turbolinks = {
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,8 @@
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>
19
+ <li><a href="/reload.html"><span>Asset Change</span></a></li>
18
20
  <li><a href="/dummy.gif?12345">Query Param Image Link</a></li>
19
21
  <li><a href="#">Hash link</a></li>
20
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-js
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-09-28 00:00:00.000000000 Z
13
+ date: 2012-10-03 00:00:00.000000000 Z
14
14
  dependencies: []
15
15
  description:
16
16
  email:
@@ -27,7 +27,8 @@ files:
27
27
  - test/dummy.gif
28
28
  - test/index.html
29
29
  - test/other.html
30
- homepage:
30
+ - test/reload.html
31
+ homepage: https://github.com/epiclabs/turbolinks-js/
31
32
  licenses: []
32
33
  post_install_message:
33
34
  rdoc_options: []