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.
- data/lib/assets/javascripts/turbolinks.js +117 -31
- data/lib/turbolinks.rb +28 -1
- data/test/index.html +2 -0
- data/test/reload.html +18 -0
- metadata +4 -3
@@ -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,
|
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
|
-
|
6
|
+
initialized = false;
|
6
7
|
|
7
8
|
currentState = null;
|
8
9
|
|
9
|
-
|
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
|
-
|
29
|
-
|
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
|
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
|
-
|
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
|
-
|
114
|
-
var
|
115
|
-
|
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
|
-
|
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
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
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
|
-
|
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
|
-
|
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
|
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.
|
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-
|
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
|
-
|
30
|
+
- test/reload.html
|
31
|
+
homepage: https://github.com/epiclabs/turbolinks-js/
|
31
32
|
licenses: []
|
32
33
|
post_install_message:
|
33
34
|
rdoc_options: []
|