turbograft 0.4.8 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -4
  3. data/lib/assets/javascripts/turbograft/click.js +44 -0
  4. data/lib/assets/javascripts/turbograft/component_url.js +53 -0
  5. data/lib/assets/javascripts/turbograft/csrf_token.js +23 -0
  6. data/lib/assets/javascripts/turbograft/document.js +20 -0
  7. data/lib/assets/javascripts/turbograft/initializers.js +83 -0
  8. data/lib/assets/javascripts/turbograft/link.js +58 -0
  9. data/lib/assets/javascripts/turbograft/page.js +105 -0
  10. data/lib/assets/javascripts/turbograft/remote.js +248 -0
  11. data/lib/assets/javascripts/turbograft/response.js +48 -0
  12. data/lib/assets/javascripts/turbograft/turbohead.js +159 -0
  13. data/lib/assets/javascripts/turbograft/turbolinks.js +504 -0
  14. data/lib/assets/javascripts/turbograft.js +37 -0
  15. data/lib/turbograft/version.rb +1 -1
  16. metadata +27 -27
  17. data/lib/assets/javascripts/turbograft/click.coffee +0 -34
  18. data/lib/assets/javascripts/turbograft/component_url.coffee +0 -24
  19. data/lib/assets/javascripts/turbograft/csrf_token.coffee +0 -9
  20. data/lib/assets/javascripts/turbograft/document.coffee +0 -11
  21. data/lib/assets/javascripts/turbograft/initializers.coffee +0 -67
  22. data/lib/assets/javascripts/turbograft/link.coffee +0 -41
  23. data/lib/assets/javascripts/turbograft/page.coffee +0 -77
  24. data/lib/assets/javascripts/turbograft/remote.coffee +0 -179
  25. data/lib/assets/javascripts/turbograft/response.coffee +0 -31
  26. data/lib/assets/javascripts/turbograft/turbohead.coffee +0 -142
  27. data/lib/assets/javascripts/turbograft/turbolinks.coffee +0 -361
  28. data/lib/assets/javascripts/turbograft.coffee +0 -30
@@ -0,0 +1,48 @@
1
+ /*
2
+ * decaffeinate suggestions:
3
+ * DS102: Remove unnecessary code created because of implicit returns
4
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
5
+ */
6
+ TurboGraft.Response = class Response {
7
+ constructor(xhr, intendedURL) {
8
+ let redirectedTo;
9
+ this.xhr = xhr;
10
+ if (intendedURL && (intendedURL.withoutHash() !== this.xhr.responseURL)) {
11
+ redirectedTo = this.xhr.responseURL;
12
+ } else {
13
+ redirectedTo = this.xhr.getResponseHeader('X-XHR-Redirected-To');
14
+ }
15
+
16
+ this.finalURL = redirectedTo || intendedURL;
17
+ }
18
+
19
+ valid() { return this.hasRenderableHttpStatus() && this.hasValidContent(); }
20
+
21
+ document() {
22
+ if (this.valid()) {
23
+ return TurboGraft.Document.create(this.xhr.responseText);
24
+ }
25
+ }
26
+
27
+ hasRenderableHttpStatus() {
28
+ if (this.xhr.status === 422) { return true; } // we want to render form validations
29
+ return !(400 <= this.xhr.status && this.xhr.status < 600);
30
+ }
31
+
32
+ hasValidContent() {
33
+ let contentType;
34
+ if (contentType = this.xhr.getResponseHeader('Content-Type')) {
35
+ return contentType.match(/^(?:text\/html|application\/xhtml\+xml|application\/xml)(?:;|$)/);
36
+ } else {
37
+ throw new Error(`Error encountered for XHR Response: ${this}`);
38
+ }
39
+ }
40
+
41
+ toString() {
42
+ return `URL: ${this.xhr.responseURL}, ` +
43
+ `ReadyState: ${this.xhr.readyState}, ` +
44
+ `Headers: ${this.xhr.getAllResponseHeaders()}`;
45
+ }
46
+ };
47
+
48
+ TurboGraft.location = () => location.href;
@@ -0,0 +1,159 @@
1
+ /*
2
+ * decaffeinate suggestions:
3
+ * DS101: Remove unnecessary use of Array.from
4
+ * DS102: Remove unnecessary code created because of implicit returns
5
+ * DS206: Consider reworking classes to avoid initClass
6
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
7
+ */
8
+ const TRACKED_ASSET_SELECTOR = '[data-turbolinks-track]';
9
+ const TRACKED_ATTRIBUTE_NAME = 'turbolinksTrack';
10
+ const ANONYMOUS_TRACK_VALUE = 'true';
11
+
12
+ let scriptPromises = {};
13
+ let resolvePreviousRequest = null;
14
+
15
+ const waitForCompleteDownloads = function() {
16
+ const loadingPromises = Object.keys(scriptPromises).map(url => scriptPromises[url]);
17
+ return Promise.all(loadingPromises);
18
+ };
19
+
20
+ const Cls = (TurboGraft.TurboHead = class TurboHead {
21
+ static initClass() {
22
+
23
+ this._testAPI = {
24
+ reset() {
25
+ scriptPromises = {};
26
+ return resolvePreviousRequest = null;
27
+ }
28
+ };
29
+ }
30
+ constructor(activeDocument, upstreamDocument) {
31
+ this._insertNewAssets = this._insertNewAssets.bind(this);
32
+ this.activeDocument = activeDocument;
33
+ this.upstreamDocument = upstreamDocument;
34
+ this.activeAssets = extractTrackedAssets(this.activeDocument);
35
+ this.upstreamAssets = extractTrackedAssets(this.upstreamDocument);
36
+ this.newScripts = this.upstreamAssets
37
+ .filter(attributeMatches('nodeName', 'SCRIPT'))
38
+ .filter(noAttributeMatchesIn('src', this.activeAssets));
39
+
40
+ this.newLinks = this.upstreamAssets
41
+ .filter(attributeMatches('nodeName', 'LINK'))
42
+ .filter(noAttributeMatchesIn('href', this.activeAssets));
43
+ }
44
+
45
+ hasChangedAnonymousAssets() {
46
+ const anonymousUpstreamAssets = this.upstreamAssets
47
+ .filter(datasetMatches(TRACKED_ATTRIBUTE_NAME, ANONYMOUS_TRACK_VALUE));
48
+ const anonymousActiveAssets = this.activeAssets
49
+ .filter(datasetMatches(TRACKED_ATTRIBUTE_NAME, ANONYMOUS_TRACK_VALUE));
50
+
51
+ if (anonymousActiveAssets.length !== anonymousUpstreamAssets.length) {
52
+ return true;
53
+ }
54
+
55
+ const noMatchingSrc = noAttributeMatchesIn('src', anonymousUpstreamAssets);
56
+ const noMatchingHref = noAttributeMatchesIn('href', anonymousUpstreamAssets);
57
+
58
+ return anonymousActiveAssets.some(node => noMatchingSrc(node) || noMatchingHref(node));
59
+ }
60
+
61
+ movingFromTrackedToUntracked() {
62
+ return (this.upstreamAssets.length === 0) && (this.activeAssets.length > 0);
63
+ }
64
+
65
+ hasNamedAssetConflicts() {
66
+ return this.newScripts
67
+ .concat(this.newLinks)
68
+ .filter(noDatasetMatches(TRACKED_ATTRIBUTE_NAME, ANONYMOUS_TRACK_VALUE))
69
+ .some(datasetMatchesIn(TRACKED_ATTRIBUTE_NAME, this.activeAssets));
70
+ }
71
+
72
+ hasAssetConflicts() {
73
+ return this.movingFromTrackedToUntracked() ||
74
+ this.hasNamedAssetConflicts() ||
75
+ this.hasChangedAnonymousAssets();
76
+ }
77
+
78
+ waitForAssets() {
79
+ if (typeof resolvePreviousRequest === 'function') {
80
+ resolvePreviousRequest({isCanceled: true});
81
+ }
82
+
83
+ return new Promise(resolve => {
84
+ resolvePreviousRequest = resolve;
85
+ return waitForCompleteDownloads()
86
+ .then(this._insertNewAssets)
87
+ .then(waitForCompleteDownloads)
88
+ .then(resolve);
89
+ });
90
+ }
91
+
92
+ _insertNewAssets() {
93
+ updateLinkTags(this.activeDocument, this.newLinks);
94
+ return updateScriptTags(this.activeDocument, this.newScripts);
95
+ }
96
+ });
97
+ Cls.initClass();
98
+
99
+ var extractTrackedAssets = doc => [].slice.call(doc.querySelectorAll(TRACKED_ASSET_SELECTOR));
100
+
101
+ var attributeMatches = (attribute, value) => node => node[attribute] === value;
102
+
103
+ const attributeMatchesIn = (attribute, collection) => node => collection.some(nodeFromCollection => node[attribute] === nodeFromCollection[attribute]);
104
+
105
+ var noAttributeMatchesIn = (attribute, collection) => node => !collection.some(nodeFromCollection => node[attribute] === nodeFromCollection[attribute]);
106
+
107
+ var datasetMatches = (attribute, value) => node => node.dataset[attribute] === value;
108
+
109
+ var noDatasetMatches = (attribute, value) => node => node.dataset[attribute] !== value;
110
+
111
+ var datasetMatchesIn = (attribute, collection) => (function(node) {
112
+ const value = node.dataset[attribute];
113
+ return collection.some(datasetMatches(attribute, value));
114
+ });
115
+
116
+ const noDatasetMatchesIn = (attribute, collection) => (function(node) {
117
+ const value = node.dataset[attribute];
118
+ return !collection.some(datasetMatches(attribute, value));
119
+ });
120
+
121
+ var updateLinkTags = (activeDocument, newLinks) => // style tag load events don't work in all browsers
122
+ // as such we just hope they load ¯\_(ツ)_/¯
123
+ newLinks.forEach(function(linkNode) {
124
+ const newNode = linkNode.cloneNode();
125
+ activeDocument.head.appendChild(newNode);
126
+ return triggerEvent("page:after-link-inserted", newNode);
127
+ });
128
+
129
+ var updateScriptTags = function(activeDocument, newScripts) {
130
+ let promise = Promise.resolve();
131
+ newScripts.forEach(scriptNode => promise = promise.then(() => insertScript(activeDocument, scriptNode)));
132
+ return promise;
133
+ };
134
+
135
+ var insertScript = function(activeDocument, scriptNode) {
136
+ const url = scriptNode.src;
137
+ if (scriptPromises[url]) {
138
+ return scriptPromises[url];
139
+ }
140
+
141
+ // Clone script tags to guarantee browser execution.
142
+ const newNode = activeDocument.createElement('SCRIPT');
143
+ for (var attr of Array.from(scriptNode.attributes)) { newNode.setAttribute(attr.name, attr.value); }
144
+ newNode.appendChild(activeDocument.createTextNode(scriptNode.innerHTML));
145
+
146
+ return scriptPromises[url] = new Promise(function(resolve) {
147
+ var onAssetEvent = function(event) {
148
+ if (event.type === 'error') { triggerEvent("page:#script-error", event); }
149
+ newNode.removeEventListener('load', onAssetEvent);
150
+ newNode.removeEventListener('error', onAssetEvent);
151
+ return resolve();
152
+ };
153
+
154
+ newNode.addEventListener('load', onAssetEvent);
155
+ newNode.addEventListener('error', onAssetEvent);
156
+ activeDocument.head.appendChild(newNode);
157
+ return triggerEvent("page:after-script-inserted", newNode);
158
+ });
159
+ };
@@ -0,0 +1,504 @@
1
+ (function() {
2
+ var Response, TurboHead, activeDocument, browserSupportsCustomEvents, browserSupportsPushState, browserSupportsTurbolinks, historyStateIsDefined, installDocumentReadyPageEventTriggers, installJqueryAjaxSuccessPageUpdateTrigger, jQuery, popCookie, ref, removeNode, replaceNode, requestMethodIsSafe, xhr;
3
+
4
+ Response = TurboGraft.Response;
5
+
6
+ TurboHead = TurboGraft.TurboHead;
7
+
8
+ jQuery = window.jQuery;
9
+
10
+ xhr = null;
11
+
12
+ activeDocument = document;
13
+
14
+ installDocumentReadyPageEventTriggers = function() {
15
+ return activeDocument.addEventListener('DOMContentLoaded', (function() {
16
+ triggerEvent('page:change');
17
+ return triggerEvent('page:update');
18
+ }), true);
19
+ };
20
+
21
+ installJqueryAjaxSuccessPageUpdateTrigger = function() {
22
+ if (typeof jQuery !== 'undefined') {
23
+ return jQuery(activeDocument).on('ajaxSuccess', function(event, xhr, settings) {
24
+ if (!jQuery.trim(xhr.responseText)) {
25
+ return;
26
+ }
27
+ return triggerEvent('page:update');
28
+ });
29
+ }
30
+ };
31
+
32
+ historyStateIsDefined = window.history.state !== void 0 || navigator.userAgent.match(/Firefox\/2[6|7]/);
33
+
34
+ browserSupportsPushState = window.history && window.history.pushState && window.history.replaceState && historyStateIsDefined;
35
+
36
+ window.triggerEvent = function(name, data) {
37
+ var event;
38
+ event = activeDocument.createEvent('Events');
39
+ if (data) {
40
+ event.data = data;
41
+ }
42
+ event.initEvent(name, true, true);
43
+ return activeDocument.dispatchEvent(event);
44
+ };
45
+
46
+ window.triggerEventFor = function(name, node, data) {
47
+ var event;
48
+ event = activeDocument.createEvent('Events');
49
+ if (data) {
50
+ event.data = data;
51
+ }
52
+ event.initEvent(name, true, true);
53
+ return node.dispatchEvent(event);
54
+ };
55
+
56
+ popCookie = function(name) {
57
+ var ref, value;
58
+ value = ((ref = activeDocument.cookie.match(new RegExp(name + "=(\\w+)"))) != null ? ref[1].toUpperCase() : void 0) || '';
59
+ activeDocument.cookie = name + '=; expires=Thu, 01-Jan-70 00:00:01 GMT; path=/';
60
+ return value;
61
+ };
62
+
63
+ requestMethodIsSafe = (ref = popCookie('request_method')) === 'GET' || ref === '';
64
+
65
+ browserSupportsTurbolinks = browserSupportsPushState && requestMethodIsSafe;
66
+
67
+ browserSupportsCustomEvents = activeDocument.addEventListener && activeDocument.createEvent;
68
+
69
+ if (browserSupportsCustomEvents) {
70
+ installDocumentReadyPageEventTriggers();
71
+ installJqueryAjaxSuccessPageUpdateTrigger();
72
+ }
73
+
74
+ replaceNode = function(newNode, oldNode) {
75
+ var replacedNode;
76
+ replacedNode = oldNode.parentNode.replaceChild(newNode, oldNode);
77
+ return triggerEvent('page:after-node-removed', replacedNode);
78
+ };
79
+
80
+ removeNode = function(node) {
81
+ var removedNode;
82
+ removedNode = node.parentNode.removeChild(node);
83
+ return triggerEvent('page:after-node-removed', removedNode);
84
+ };
85
+
86
+ /* TODO: triggerEvent should be accessible to all these guys
87
+ * on some kind of eventbus
88
+ * TODO: clean up everything above me ^
89
+ * TODO: decide on the public API
90
+ */
91
+ window.Turbolinks = (function() {
92
+ var anyAutofocusElement, bypassOnLoadPopstate, changePage, currentState, deleteRefreshNeverNodes, executeScriptTag, executeScriptTags, fetch, fetchReplacement, getNodesMatchingRefreshKeys, getNodesWithRefreshAlways, installHistoryChangeHandler, isPartialReplace, keepNodes, pageChangePrevented, persistStaticElements, recallScrollPosition, referer, reflectNewUrl, refreshAllExceptWithKeys, refreshNodes, rememberReferer, removeNoscriptTags, setAutofocusElement, updateBody;
93
+
94
+ function Turbolinks() {}
95
+
96
+ currentState = null;
97
+
98
+ referer = null;
99
+
100
+ fetch = function(url, options) {
101
+ if (options == null) {
102
+ options = {};
103
+ }
104
+ if (pageChangePrevented(url)) {
105
+ return;
106
+ }
107
+ url = new ComponentUrl(url);
108
+ rememberReferer();
109
+ return fetchReplacement(url, options);
110
+ };
111
+
112
+ isPartialReplace = function(response, options) {
113
+ var ref1, ref2;
114
+ return Boolean(options.partialReplace || ((ref1 = options.onlyKeys) != null ? ref1.length : void 0) || ((ref2 = options.exceptKeys) != null ? ref2.length : void 0));
115
+ };
116
+
117
+ Turbolinks.fullPageNavigate = function(url) {
118
+ if (url != null) {
119
+ url = (new ComponentUrl(url)).absolute;
120
+ triggerEvent('page:before-full-refresh', {
121
+ url: url
122
+ });
123
+ activeDocument.location.href = url;
124
+ }
125
+ };
126
+
127
+ Turbolinks.pushState = function(state, title, url) {
128
+ return window.history.pushState(state, title, url);
129
+ };
130
+
131
+ Turbolinks.replaceState = function(state, title, url) {
132
+ return window.history.replaceState(state, title, url);
133
+ };
134
+
135
+ Turbolinks.document = function(documentToUse) {
136
+ if (documentToUse) {
137
+ activeDocument = documentToUse;
138
+ }
139
+ return activeDocument;
140
+ };
141
+
142
+ fetchReplacement = function(url, options) {
143
+ var k, ref1, v;
144
+ triggerEvent('page:fetch', {
145
+ url: url.absolute
146
+ });
147
+ if (xhr != null) {
148
+ // Workaround for sinon xhr.abort()
149
+ // https://github.com/sinonjs/sinon/issues/432#issuecomment-216917023
150
+ xhr.readyState = 0;
151
+ xhr.statusText = "abort";
152
+ xhr.abort();
153
+ }
154
+ xhr = new XMLHttpRequest;
155
+ xhr.open('GET', url.withoutHashForIE10compatibility(), true);
156
+ xhr.setRequestHeader('Accept', 'text/html, application/xhtml+xml, application/xml');
157
+ xhr.setRequestHeader('X-XHR-Referer', referer);
158
+ if (options.headers == null) {
159
+ options.headers = {};
160
+ }
161
+ ref1 = options.headers;
162
+ for (k in ref1) {
163
+ v = ref1[k];
164
+ xhr.setRequestHeader(k, v);
165
+ }
166
+ xhr.onload = function() {
167
+ if (xhr.status >= 500) {
168
+ Turbolinks.fullPageNavigate(url);
169
+ } else {
170
+ Turbolinks.loadPage(url, xhr, options);
171
+ }
172
+ return xhr = null;
173
+ };
174
+ xhr.onerror = function() {
175
+ // Workaround for sinon xhr.abort()
176
+ if (xhr.statusText === "abort") {
177
+ xhr = null;
178
+ return;
179
+ }
180
+ return Turbolinks.fullPageNavigate(url);
181
+ };
182
+ xhr.send();
183
+ };
184
+
185
+ Turbolinks.loadPage = function(url, xhr, options) {
186
+ var response, turbohead, upstreamDocument;
187
+ if (options == null) {
188
+ options = {};
189
+ }
190
+ triggerEvent('page:receive');
191
+ response = new Response(xhr, url);
192
+ if (options.updatePushState == null) {
193
+ options.updatePushState = true;
194
+ }
195
+ options.partialReplace = isPartialReplace(response, options);
196
+ if (!(upstreamDocument = response.document())) {
197
+ triggerEvent('page:error', xhr);
198
+ Turbolinks.fullPageNavigate(response.finalURL);
199
+ return;
200
+ }
201
+ if (options.partialReplace) {
202
+ updateBody(upstreamDocument, response, options);
203
+ return;
204
+ }
205
+ turbohead = new TurboHead(activeDocument, upstreamDocument);
206
+ if (turbohead.hasAssetConflicts()) {
207
+ return Turbolinks.fullPageNavigate(response.finalURL);
208
+ }
209
+ return turbohead.waitForAssets().then(function(result) {
210
+ if (!(result != null ? result.isCanceled : void 0)) {
211
+ return updateBody(upstreamDocument, response, options);
212
+ }
213
+ });
214
+ };
215
+
216
+ updateBody = function(upstreamDocument, response, options) {
217
+ var nodes, ref1;
218
+ nodes = changePage((ref1 = upstreamDocument.querySelector('title')) != null ? ref1.textContent : void 0, removeNoscriptTags(upstreamDocument.querySelector('body')), CSRFToken.get(upstreamDocument).token, 'runScripts', options);
219
+ if (options.updatePushState) {
220
+ reflectNewUrl(response.finalURL);
221
+ }
222
+ if (!options.partialReplace) {
223
+ Turbolinks.resetScrollPosition();
224
+ }
225
+ if (typeof options.callback === "function") {
226
+ options.callback();
227
+ }
228
+ return triggerEvent('page:load', nodes);
229
+ };
230
+
231
+ changePage = function(title, body, csrfToken, runScripts, options) {
232
+ var nodes, nodesToRefresh, ref1, ref2;
233
+ if (options == null) {
234
+ options = {};
235
+ }
236
+ if (title) {
237
+ activeDocument.title = title;
238
+ }
239
+ if ((ref1 = options.onlyKeys) != null ? ref1.length : void 0) {
240
+ nodesToRefresh = [].concat(getNodesWithRefreshAlways(), getNodesMatchingRefreshKeys(options.onlyKeys));
241
+ nodes = refreshNodes(nodesToRefresh, body);
242
+ if (anyAutofocusElement(nodes)) {
243
+ setAutofocusElement();
244
+ }
245
+ return nodes;
246
+ } else {
247
+ refreshNodes(getNodesWithRefreshAlways(), body);
248
+ persistStaticElements(body);
249
+ if ((ref2 = options.exceptKeys) != null ? ref2.length : void 0) {
250
+ refreshAllExceptWithKeys(options.exceptKeys, body);
251
+ } else {
252
+ deleteRefreshNeverNodes(body);
253
+ }
254
+ triggerEvent('page:before-replace');
255
+ replaceNode(body, activeDocument.body);
256
+ if (csrfToken != null) {
257
+ CSRFToken.update(csrfToken);
258
+ }
259
+ setAutofocusElement();
260
+ if (runScripts) {
261
+ executeScriptTags();
262
+ }
263
+ currentState = window.history.state;
264
+ triggerEvent('page:change');
265
+ triggerEvent('page:update');
266
+ }
267
+ };
268
+
269
+ getNodesMatchingRefreshKeys = function(keys) {
270
+ var i, j, key, len, len1, matchingNodes, node, ref1;
271
+ matchingNodes = [];
272
+ for (i = 0, len = keys.length; i < len; i++) {
273
+ key = keys[i];
274
+ ref1 = TurboGraft.querySelectorAllTGAttribute(activeDocument, 'refresh', key);
275
+ for (j = 0, len1 = ref1.length; j < len1; j++) {
276
+ node = ref1[j];
277
+ matchingNodes.push(node);
278
+ }
279
+ }
280
+ return matchingNodes;
281
+ };
282
+
283
+ getNodesWithRefreshAlways = function() {
284
+ var i, len, matchingNodes, node, ref1;
285
+ matchingNodes = [];
286
+ ref1 = TurboGraft.querySelectorAllTGAttribute(activeDocument, 'refresh-always');
287
+ for (i = 0, len = ref1.length; i < len; i++) {
288
+ node = ref1[i];
289
+ matchingNodes.push(node);
290
+ }
291
+ return matchingNodes;
292
+ };
293
+
294
+ anyAutofocusElement = function(nodes) {
295
+ var i, len, node;
296
+ for (i = 0, len = nodes.length; i < len; i++) {
297
+ node = nodes[i];
298
+ if (node.querySelectorAll('input[autofocus], textarea[autofocus]').length > 0) {
299
+ return true;
300
+ }
301
+ }
302
+ return false;
303
+ };
304
+
305
+ setAutofocusElement = function() {
306
+ var autofocusElement, list;
307
+ autofocusElement = (list = activeDocument.querySelectorAll('input[autofocus], textarea[autofocus]'))[list.length - 1];
308
+ if (autofocusElement && activeDocument.activeElement !== autofocusElement) {
309
+ return autofocusElement.focus();
310
+ }
311
+ };
312
+
313
+ deleteRefreshNeverNodes = function(body) {
314
+ var i, len, node, ref1;
315
+ ref1 = TurboGraft.querySelectorAllTGAttribute(body, 'refresh-never');
316
+ for (i = 0, len = ref1.length; i < len; i++) {
317
+ node = ref1[i];
318
+ removeNode(node);
319
+ }
320
+ };
321
+
322
+ refreshNodes = function(allNodesToBeRefreshed, body) {
323
+ var existingNode, i, len, newNode, nodeId, parentIsRefreshing, refreshedNodes;
324
+ triggerEvent('page:before-partial-replace', allNodesToBeRefreshed);
325
+ parentIsRefreshing = function(node) {
326
+ var i, len, potentialParent;
327
+ for (i = 0, len = allNodesToBeRefreshed.length; i < len; i++) {
328
+ potentialParent = allNodesToBeRefreshed[i];
329
+ if (node !== potentialParent) {
330
+ if (potentialParent.contains(node)) {
331
+ return true;
332
+ }
333
+ }
334
+ }
335
+ return false;
336
+ };
337
+ refreshedNodes = [];
338
+ for (i = 0, len = allNodesToBeRefreshed.length; i < len; i++) {
339
+ existingNode = allNodesToBeRefreshed[i];
340
+ if (parentIsRefreshing(existingNode)) {
341
+ continue;
342
+ }
343
+ if (!(nodeId = existingNode.getAttribute('id'))) {
344
+ throw new Error("Turbolinks refresh: Refresh key elements must have an id.");
345
+ }
346
+ if (newNode = body.querySelector("#" + nodeId)) {
347
+ newNode = newNode.cloneNode(true);
348
+ replaceNode(newNode, existingNode);
349
+ if (newNode.nodeName === 'SCRIPT' && newNode.dataset.turbolinksEval !== "false") {
350
+ executeScriptTag(newNode);
351
+ } else {
352
+ refreshedNodes.push(newNode);
353
+ }
354
+ } else if (!TurboGraft.hasTGAttribute(existingNode, "refresh-always")) {
355
+ removeNode(existingNode);
356
+ }
357
+ }
358
+ return refreshedNodes;
359
+ };
360
+
361
+ keepNodes = function(body, allNodesToKeep) {
362
+ var existingNode, i, len, nodeId, remoteNode, results;
363
+ results = [];
364
+ for (i = 0, len = allNodesToKeep.length; i < len; i++) {
365
+ existingNode = allNodesToKeep[i];
366
+ if (!(nodeId = existingNode.getAttribute('id'))) {
367
+ throw new Error("TurboGraft refresh: Kept nodes must have an id.");
368
+ }
369
+ if (remoteNode = body.querySelector("#" + nodeId)) {
370
+ results.push(replaceNode(existingNode, remoteNode));
371
+ } else {
372
+ results.push(void 0);
373
+ }
374
+ }
375
+ return results;
376
+ };
377
+
378
+ persistStaticElements = function(body) {
379
+ var allNodesToKeep, i, len, node, nodes;
380
+ allNodesToKeep = [];
381
+ nodes = TurboGraft.querySelectorAllTGAttribute(activeDocument, 'tg-static');
382
+ for (i = 0, len = nodes.length; i < len; i++) {
383
+ node = nodes[i];
384
+ allNodesToKeep.push(node);
385
+ }
386
+ keepNodes(body, allNodesToKeep);
387
+ };
388
+
389
+ refreshAllExceptWithKeys = function(keys, body) {
390
+ var allNodesToKeep, i, j, key, len, len1, node, ref1;
391
+ allNodesToKeep = [];
392
+ for (i = 0, len = keys.length; i < len; i++) {
393
+ key = keys[i];
394
+ ref1 = TurboGraft.querySelectorAllTGAttribute(activeDocument, 'refresh', key);
395
+ for (j = 0, len1 = ref1.length; j < len1; j++) {
396
+ node = ref1[j];
397
+ allNodesToKeep.push(node);
398
+ }
399
+ }
400
+ keepNodes(body, allNodesToKeep);
401
+ };
402
+
403
+ executeScriptTags = function() {
404
+ var i, len, ref1, script, scripts;
405
+ scripts = Array.prototype.slice.call(activeDocument.body.querySelectorAll('script:not([data-turbolinks-eval="false"])'));
406
+ for (i = 0, len = scripts.length; i < len; i++) {
407
+ script = scripts[i];
408
+ if ((ref1 = script.type) === '' || ref1 === 'text/javascript') {
409
+ executeScriptTag(script);
410
+ }
411
+ }
412
+ };
413
+
414
+ executeScriptTag = function(script) {
415
+ var attr, copy, i, len, nextSibling, parentNode, ref1;
416
+ copy = activeDocument.createElement('script');
417
+ ref1 = script.attributes;
418
+ for (i = 0, len = ref1.length; i < len; i++) {
419
+ attr = ref1[i];
420
+ copy.setAttribute(attr.name, attr.value);
421
+ }
422
+ copy.appendChild(activeDocument.createTextNode(script.innerHTML));
423
+ parentNode = script.parentNode, nextSibling = script.nextSibling;
424
+ parentNode.removeChild(script);
425
+ parentNode.insertBefore(copy, nextSibling);
426
+ };
427
+
428
+ removeNoscriptTags = function(node) {
429
+ node.innerHTML = node.innerHTML.replace(/<noscript[\S\s]*?<\/noscript>/ig, '');
430
+ return node;
431
+ };
432
+
433
+ reflectNewUrl = function(url) {
434
+ if ((url = new ComponentUrl(url)).absolute !== referer) {
435
+ Turbolinks.pushState({
436
+ turbolinks: true,
437
+ url: url.absolute
438
+ }, '', url.absolute);
439
+ }
440
+ };
441
+
442
+ rememberReferer = function() {
443
+ return referer = activeDocument.location.href;
444
+ };
445
+
446
+ Turbolinks.rememberCurrentUrl = function() {
447
+ return Turbolinks.replaceState({
448
+ turbolinks: true,
449
+ url: activeDocument.location.href
450
+ }, '', activeDocument.location.href);
451
+ };
452
+
453
+ Turbolinks.rememberCurrentState = function() {
454
+ return currentState = window.history.state;
455
+ };
456
+
457
+ recallScrollPosition = function(page) {
458
+ return window.scrollTo(page.positionX, page.positionY);
459
+ };
460
+
461
+ Turbolinks.resetScrollPosition = function() {
462
+ if (activeDocument.location.hash) {
463
+ return activeDocument.location.href = activeDocument.location.href;
464
+ } else {
465
+ return window.scrollTo(0, 0);
466
+ }
467
+ };
468
+
469
+ pageChangePrevented = function(url) {
470
+ return !triggerEvent('page:before-change', url);
471
+ };
472
+
473
+ installHistoryChangeHandler = function(event) {
474
+ var ref1;
475
+ if ((ref1 = event.state) != null ? ref1.turbolinks : void 0) {
476
+ return Turbolinks.visit(event.target.location.href);
477
+ }
478
+ };
479
+
480
+ // Delay execution of function long enough to miss the popstate event
481
+ // some browsers fire on the initial page load.
482
+ bypassOnLoadPopstate = function(fn) {
483
+ return setTimeout(fn, 500);
484
+ };
485
+
486
+ if (browserSupportsTurbolinks) {
487
+ Turbolinks.visit = fetch;
488
+ Turbolinks.rememberCurrentUrl();
489
+ Turbolinks.rememberCurrentState();
490
+ activeDocument.addEventListener('click', Click.installHandlerLast, true);
491
+ bypassOnLoadPopstate(function() {
492
+ return window.addEventListener('popstate', installHistoryChangeHandler, false);
493
+ });
494
+ } else {
495
+ Turbolinks.visit = function(url) {
496
+ return activeDocument.location.href = url;
497
+ };
498
+ }
499
+
500
+ return Turbolinks;
501
+
502
+ })();
503
+
504
+ }).call(this);