turbolinks-js 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +9 -47
- data/lib/assets/javascripts/turbolinks.js +91 -35
- data/test/config.ru +1 -2
- data/test/dummy.gif +0 -0
- data/test/index.html +7 -2
- data/test/other.html +1 -1
- metadata +3 -3
data/README.md
CHANGED
@@ -1,52 +1,14 @@
|
|
1
1
|
Turbolinks-js
|
2
2
|
=============
|
3
3
|
|
4
|
-
Same as
|
5
|
-
|
4
|
+
Same as Turbolinks but without the CoffeeScript requirement.
|
5
|
+
Check [Turbolinks](https://github.com/rails/turbolinks) for
|
6
|
+
more information.
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
and potentially spend extra HTTP requests checking if the assets are up-to-date,
|
10
|
-
we keep the current instance alive and replace only the body and the title in the
|
11
|
-
head. Think CGI vs persistent process.
|
8
|
+
Installation
|
9
|
+
------------
|
12
10
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
works.
|
18
|
-
|
19
|
-
By default, all internal links will be funneled through Turbolinks, but you can opt
|
20
|
-
out by marking links with data-no-turbolink.
|
21
|
-
|
22
|
-
No jQuery or any other framework
|
23
|
-
--------------------------------
|
24
|
-
|
25
|
-
Turbolinks is designed to be as light-weight as possible (so you won't think twice about using it even for mobile stuff). It does not require jQuery or any other framework to work.
|
26
|
-
But it works great _with_ jQuery or Prototype or whatever else have you.
|
27
|
-
|
28
|
-
The page:update event
|
29
|
-
---------------------
|
30
|
-
|
31
|
-
Since pages will change without a full reload with Turbolinks, you can't by default
|
32
|
-
rely on `dom:loaded` to trigger your JavaScript code. Instead, Turbolinks uses the
|
33
|
-
`page:update` event.
|
34
|
-
|
35
|
-
Triggering a Turbolinks visit manually
|
36
|
-
---------------------------------------
|
37
|
-
|
38
|
-
You can use `Turbolinks.visit(path)` to go to a URL through Turbolinks.
|
39
|
-
|
40
|
-
Available only for pushState browsers
|
41
|
-
-------------------------------------
|
42
|
-
|
43
|
-
Like pjax, this naturally only works with browsers capable of `pushState`. But of
|
44
|
-
course we fall back gracefully to full page reloads for browsers that do not support
|
45
|
-
it.
|
46
|
-
|
47
|
-
Work left to do
|
48
|
-
---------------
|
49
|
-
|
50
|
-
* CSS/JS asset change detection and reload.
|
51
|
-
* Add a DOM cache for faster back button.
|
52
|
-
* Remember scroll position when using back button.
|
11
|
+
1. Add `gem 'turbolinks-js'` to your Gemfile.
|
12
|
+
2. Run `bundle install`.
|
13
|
+
3. Add `//= require turbolinks` to your Javascript manifest file (usually found at `app/assets/javascripts/application.js`).
|
14
|
+
4. Restart your server and you're now using turbolinks!
|
@@ -1,8 +1,16 @@
|
|
1
|
+
// Generated by CoffeeScript 1.3.3
|
1
2
|
(function() {
|
2
|
-
var anchoredLink, browserSupportsPushState, createDocument, crossOriginLink, extractLink, fetchReplacement,
|
3
|
+
var anchoredLink, browserSupportsPushState, cacheCurrentPage, changePage, createDocument, crossOriginLink, currentState, extractLink, extractTitleAndBody, fetchHistory, fetchReplacement, garbageCollectCache, handleClick, ignoreClick, initialized, newTabClick, noTurbolink, nonHtmlLink, pageCache, recallScrollPosition, reflectNewUrl, rememberCurrentState, rememberCurrentUrl, rememberInitialPage, samePageLink, triggerEvent, visit;
|
4
|
+
|
5
|
+
pageCache = [];
|
6
|
+
|
7
|
+
currentState = null;
|
8
|
+
|
9
|
+
initialized = false;
|
3
10
|
|
4
11
|
visit = function(url) {
|
5
|
-
if (
|
12
|
+
if (browserSupportsPushState) {
|
13
|
+
cacheCurrentPage();
|
6
14
|
reflectNewUrl(url);
|
7
15
|
return fetchReplacement(url);
|
8
16
|
} else {
|
@@ -16,47 +24,109 @@
|
|
16
24
|
xhr.open('GET', url, true);
|
17
25
|
xhr.setRequestHeader('Accept', 'text/html, application/xhtml+xml, application/xml');
|
18
26
|
xhr.onload = function() {
|
19
|
-
return
|
27
|
+
return changePage.apply(null, extractTitleAndBody(xhr.responseText));
|
20
28
|
};
|
21
29
|
xhr.onabort = function() {
|
22
|
-
return console.log(
|
30
|
+
return console.log('Aborted turbolink fetch!');
|
23
31
|
};
|
24
32
|
return xhr.send();
|
25
33
|
};
|
26
34
|
|
27
|
-
|
28
|
-
|
29
|
-
|
35
|
+
fetchHistory = function(state) {
|
36
|
+
var page;
|
37
|
+
cacheCurrentPage();
|
38
|
+
if (page = pageCache[state.position]) {
|
39
|
+
changePage(page.title, page.body.cloneNode(true));
|
40
|
+
return recallScrollPosition(page);
|
41
|
+
} else {
|
42
|
+
return fetchReplacement(document.location.href);
|
43
|
+
}
|
44
|
+
};
|
45
|
+
|
46
|
+
cacheCurrentPage = function() {
|
47
|
+
rememberInitialPage();
|
48
|
+
pageCache[currentState.position] = {
|
49
|
+
url: document.location.href,
|
50
|
+
body: document.body,
|
51
|
+
title: document.title,
|
52
|
+
positionY: window.pageYOffset,
|
53
|
+
positionX: window.pageXOffset
|
54
|
+
};
|
55
|
+
return garbageCollectCache();
|
56
|
+
};
|
57
|
+
|
58
|
+
garbageCollectCache = function() {
|
59
|
+
if (currentState.position === window.history.length - 1 && pageCache[currentState.position - 10] !== void 0) {
|
60
|
+
return delete pageCache[currentState.position - 10];
|
61
|
+
}
|
62
|
+
};
|
63
|
+
|
64
|
+
changePage = function(title, body) {
|
65
|
+
document.title = title;
|
66
|
+
document.documentElement.replaceChild(body, document.body);
|
67
|
+
currentState = window.history.state;
|
68
|
+
return triggerEvent('page:change');
|
30
69
|
};
|
31
70
|
|
32
71
|
reflectNewUrl = function(url) {
|
33
72
|
return window.history.pushState({
|
34
|
-
turbolinks: true
|
35
|
-
|
73
|
+
turbolinks: true,
|
74
|
+
position: window.history.length
|
75
|
+
}, '', url);
|
76
|
+
};
|
77
|
+
|
78
|
+
rememberCurrentUrl = function() {
|
79
|
+
return window.history.replaceState({
|
80
|
+
turbolinks: true,
|
81
|
+
position: window.history.length - 1
|
82
|
+
}, '', document.location.href);
|
36
83
|
};
|
37
84
|
|
38
|
-
|
85
|
+
rememberCurrentState = function() {
|
86
|
+
return currentState = window.history.state;
|
87
|
+
};
|
88
|
+
|
89
|
+
rememberInitialPage = function() {
|
90
|
+
if (!initialized) {
|
91
|
+
rememberCurrentUrl();
|
92
|
+
rememberCurrentState();
|
93
|
+
return initialized = true;
|
94
|
+
}
|
95
|
+
};
|
96
|
+
|
97
|
+
recallScrollPosition = function(page) {
|
98
|
+
return window.scrollTo(page.positionX, page.positionX);
|
99
|
+
};
|
100
|
+
|
101
|
+
triggerEvent = function(name) {
|
39
102
|
var event;
|
40
103
|
event = document.createEvent('Events');
|
41
|
-
event.initEvent(
|
104
|
+
event.initEvent(name, true, true);
|
42
105
|
return document.dispatchEvent(event);
|
43
106
|
};
|
44
107
|
|
108
|
+
extractTitleAndBody = function(html) {
|
109
|
+
var doc, title;
|
110
|
+
doc = createDocument(html);
|
111
|
+
title = doc.querySelector('title');
|
112
|
+
return [title != null ? title.textContent : void 0, doc.body];
|
113
|
+
};
|
114
|
+
|
45
115
|
createDocument = (function() {
|
46
116
|
var createDocumentUsingParser, createDocumentUsingWrite, testDoc, _ref;
|
47
117
|
createDocumentUsingParser = function(html) {
|
48
|
-
return (new DOMParser).parseFromString(html,
|
118
|
+
return (new DOMParser).parseFromString(html, 'text/html');
|
49
119
|
};
|
50
120
|
createDocumentUsingWrite = function(html) {
|
51
121
|
var doc;
|
52
|
-
doc = document.implementation.createHTMLDocument(
|
53
|
-
doc.open(
|
122
|
+
doc = document.implementation.createHTMLDocument('');
|
123
|
+
doc.open('replace');
|
54
124
|
doc.write(html);
|
55
125
|
doc.close;
|
56
126
|
return doc;
|
57
127
|
};
|
58
128
|
if (window.DOMParser) {
|
59
|
-
testDoc = createDocumentUsingParser(
|
129
|
+
testDoc = createDocumentUsingParser('<html><body><p>test');
|
60
130
|
}
|
61
131
|
if ((testDoc != null ? (_ref = testDoc.body) != null ? _ref.childNodes.length : void 0 : void 0) === 1) {
|
62
132
|
return createDocumentUsingParser;
|
@@ -65,17 +135,6 @@
|
|
65
135
|
}
|
66
136
|
})();
|
67
137
|
|
68
|
-
replaceHTML = function(html) {
|
69
|
-
var doc, originalBody, title;
|
70
|
-
doc = createDocument(html);
|
71
|
-
originalBody = document.body;
|
72
|
-
document.documentElement.appendChild(doc.body, originalBody);
|
73
|
-
document.documentElement.removeChild(originalBody);
|
74
|
-
if (title = doc.querySelector("title")) {
|
75
|
-
return document.title = title.textContent;
|
76
|
-
}
|
77
|
-
};
|
78
|
-
|
79
138
|
extractLink = function(event) {
|
80
139
|
var link;
|
81
140
|
link = event.target;
|
@@ -85,6 +144,10 @@
|
|
85
144
|
return link;
|
86
145
|
};
|
87
146
|
|
147
|
+
samePageLink = function(link) {
|
148
|
+
return link.href === document.location.href;
|
149
|
+
};
|
150
|
+
|
88
151
|
crossOriginLink = function(link) {
|
89
152
|
return location.protocol !== link.protocol || location.host !== link.host;
|
90
153
|
};
|
@@ -106,7 +169,7 @@
|
|
106
169
|
};
|
107
170
|
|
108
171
|
ignoreClick = function(event, link) {
|
109
|
-
return crossOriginLink(link) || anchoredLink(link) || nonHtmlLink(link) || noTurbolink(link) || newTabClick(event);
|
172
|
+
return samePageLink(link) || crossOriginLink(link) || anchoredLink(link) || nonHtmlLink(link) || noTurbolink(link) || newTabClick(event);
|
110
173
|
};
|
111
174
|
|
112
175
|
handleClick = function(event) {
|
@@ -120,18 +183,11 @@
|
|
120
183
|
|
121
184
|
browserSupportsPushState = window.history && window.history.pushState && window.history.replaceState;
|
122
185
|
|
123
|
-
rememberInitialPage = function() {
|
124
|
-
return window.history.replaceState({
|
125
|
-
turbolinks: true
|
126
|
-
}, "", document.location.href);
|
127
|
-
};
|
128
|
-
|
129
186
|
if (browserSupportsPushState) {
|
130
|
-
rememberInitialPage();
|
131
187
|
window.addEventListener('popstate', function(event) {
|
132
188
|
var _ref;
|
133
189
|
if ((_ref = event.state) != null ? _ref.turbolinks : void 0) {
|
134
|
-
return
|
190
|
+
return fetchHistory(event.state);
|
135
191
|
}
|
136
192
|
});
|
137
193
|
document.addEventListener('click', function(event) {
|
data/test/config.ru
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
require 'sprockets'
|
2
|
-
require 'coffee-script'
|
3
2
|
|
4
3
|
Root = File.expand_path("../..", __FILE__)
|
5
4
|
|
6
5
|
Assets = Sprockets::Environment.new do |env|
|
7
|
-
env.append_path File.join(Root,
|
6
|
+
env.append_path File.join(Root, 'lib', 'assets', 'javascripts')
|
8
7
|
end
|
9
8
|
|
10
9
|
map "/js" do
|
data/test/dummy.gif
ADDED
Binary file
|
data/test/index.html
CHANGED
@@ -10,11 +10,16 @@
|
|
10
10
|
});
|
11
11
|
</script>
|
12
12
|
</head>
|
13
|
-
<body>
|
14
|
-
<ul>
|
13
|
+
<body class="page-index">
|
14
|
+
<ul style="margin-top:200px;">
|
15
15
|
<li><a href="/test/other.html">Other page</a></li>
|
16
16
|
<li><a href="/test/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="/test/dummy.gif?12345">Query Param Image Link</a></li>
|
19
|
+
<li><a href="#">Hash link</a></li>
|
18
20
|
</ul>
|
21
|
+
|
22
|
+
<div style="background:#ccc;height:5000px;width:200px;">
|
23
|
+
</div>
|
19
24
|
</body>
|
20
25
|
</html>
|
data/test/other.html
CHANGED
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.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -24,6 +24,7 @@ files:
|
|
24
24
|
- README.md
|
25
25
|
- MIT-LICENSE
|
26
26
|
- test/config.ru
|
27
|
+
- test/dummy.gif
|
27
28
|
- test/index.html
|
28
29
|
- test/other.html
|
29
30
|
homepage:
|
@@ -49,7 +50,6 @@ rubyforge_project:
|
|
49
50
|
rubygems_version: 1.8.23
|
50
51
|
signing_key:
|
51
52
|
specification_version: 3
|
52
|
-
summary:
|
53
|
-
Rails Asset Pipeline). No CoffeeScript requirement.
|
53
|
+
summary: Same as Turbolinks but without the CoffeeScript requirement
|
54
54
|
test_files: []
|
55
55
|
has_rdoc:
|