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 CHANGED
@@ -1,52 +1,14 @@
1
1
  Turbolinks-js
2
2
  =============
3
3
 
4
- Same as [Turbolinks](https://github.com/rails/turbolinks) but without CoffeeScript
5
- requirement.
4
+ Same as Turbolinks but without the CoffeeScript requirement.
5
+ Check [Turbolinks](https://github.com/rails/turbolinks) for
6
+ more information.
6
7
 
7
- Turbolinks makes following links in your web application faster. Instead of
8
- letting the browser recompile the JavaScript and CSS between each page change,
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
- This is similar to pjax, but instead of worrying about what element on the page to
14
- replace, and tailoring the server-side response to fit, we replace the entire body.
15
- This means that you get the bulk of the speed benefits from pjax (no recompiling of
16
- the JavaScript or CSS) without having to tailor the server-side response. It just
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, fullReplacement, handleClick, ignoreClick, newTabClick, noTurbolink, nonHtmlLink, reflectNewUrl, rememberInitialPage, replaceHTML, triggerPageChange, visit;
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 (typeof browserSupportsPushState !== "undefined" && browserSupportsPushState !== null) {
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 fullReplacement(xhr.responseText, url);
27
+ return changePage.apply(null, extractTitleAndBody(xhr.responseText));
20
28
  };
21
29
  xhr.onabort = function() {
22
- return console.log("Aborted turbolink fetch!");
30
+ return console.log('Aborted turbolink fetch!');
23
31
  };
24
32
  return xhr.send();
25
33
  };
26
34
 
27
- fullReplacement = function(html, url) {
28
- replaceHTML(html);
29
- return triggerPageChange();
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
- }, "", url);
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
- triggerPageChange = function() {
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('page:change', true, true);
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, "text/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("replace");
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("<html><body><p>test");
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 fetchReplacement(document.location.href);
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, "lib", "assets", "javascripts")
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
@@ -10,7 +10,7 @@
10
10
  });
11
11
  </script>
12
12
  </head>
13
- <body>
13
+ <body class="page-other">
14
14
  <ul>
15
15
  <li><a href="/test/index.html">Home</a></li>
16
16
  </ul>
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.1.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: Turbolinks-js makes following links in your web application faster (use with
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: