turbolinks-js 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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:
|