turbolinks 2.4.0 → 2.5.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.
- checksums.yaml +4 -4
- data/README.md +22 -0
- data/lib/assets/javascripts/turbolinks.js.coffee +142 -25
- data/lib/turbolinks/version.rb +1 -1
- data/test/config.ru +27 -0
- data/test/index.html +4 -1
- metadata +9 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: abb3fc6406cda2909edbf7936a255b9c9b7ca806
|
4
|
+
data.tar.gz: 99aea405491c3b4a640d9c85d03331d545c62cfa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf05a3b36102d4487d763e244d69c7365bf6c1f42a944a928244d05a2d61ff4b7b89d02ede941cca5ba45efd3924878218d26bd64f1a5af859091f4dc992f874
|
7
|
+
data.tar.gz: 41bfb7a7287ccc9090ac53161365465fbba1effbb19cc77bb6ce8b1438f07d9bad2263d4164bcbd3404b7e42722e24df9389f33914a21c0d0aab77004f9c3542
|
data/README.md
CHANGED
@@ -86,6 +86,28 @@ The one drawback is that dramatic differences in appearence between a cached cop
|
|
86
86
|
|
87
87
|
If you find that a page is causing problems, you can have Turbolinks skip displaying the cached copy by adding `data-no-transition-cache` to any DOM element on the offending page.
|
88
88
|
|
89
|
+
Progress Bar
|
90
|
+
------------
|
91
|
+
|
92
|
+
Because Turbolinks skips the traditional full page reload, browsers won't display their native progress bar when changing pages. To fill this void, Turbolinks offers an optional JavaScript-and-CSS-based progress bar to display page loading progress.
|
93
|
+
|
94
|
+
To enable the progress bar, include the following in your JavaScript:
|
95
|
+
```javascript
|
96
|
+
Turbolinks.enableProgressBar();
|
97
|
+
```
|
98
|
+
|
99
|
+
The progress bar is implemented on the `<html>` element's pseudo `:before` element and can be **customized** by including CSS with higher specificity than the included styles. For example:
|
100
|
+
|
101
|
+
```css
|
102
|
+
html.turbolinks-progress-bar::before {
|
103
|
+
background-color: red !important;
|
104
|
+
height: 5px !important;
|
105
|
+
}
|
106
|
+
```
|
107
|
+
|
108
|
+
In Turbolinks 3.0, the progress bar will be turned on by default.
|
109
|
+
|
110
|
+
|
89
111
|
Initialization
|
90
112
|
--------------
|
91
113
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
pageCache = {}
|
2
2
|
cacheSize = 10
|
3
3
|
transitionCacheEnabled = false
|
4
|
+
progressBar = null
|
4
5
|
|
5
6
|
currentState = null
|
6
7
|
loadedAssets = null
|
@@ -26,10 +27,11 @@ fetch = (url) ->
|
|
26
27
|
|
27
28
|
rememberReferer()
|
28
29
|
cacheCurrentPage()
|
30
|
+
progressBar?.start()
|
29
31
|
|
30
32
|
if transitionCacheEnabled and cachedPage = transitionCacheFor(url.absolute)
|
31
33
|
fetchHistory cachedPage
|
32
|
-
fetchReplacement url
|
34
|
+
fetchReplacement url, null, false
|
33
35
|
else
|
34
36
|
fetchReplacement url, resetScrollPosition
|
35
37
|
|
@@ -40,7 +42,14 @@ transitionCacheFor = (url) ->
|
|
40
42
|
enableTransitionCache = (enable = true) ->
|
41
43
|
transitionCacheEnabled = enable
|
42
44
|
|
43
|
-
|
45
|
+
enableProgressBar = (enable = true) ->
|
46
|
+
if enable
|
47
|
+
progressBar ?= new ProgressBar 'html'
|
48
|
+
else
|
49
|
+
progressBar?.uninstall()
|
50
|
+
progressBar = null
|
51
|
+
|
52
|
+
fetchReplacement = (url, onLoadFunction, showProgressBar = true) ->
|
44
53
|
triggerEvent EVENTS.FETCH, url: url.absolute
|
45
54
|
|
46
55
|
xhr?.abort()
|
@@ -57,10 +66,18 @@ fetchReplacement = (url, onLoadFunction = =>) ->
|
|
57
66
|
changePage extractTitleAndBody(doc)...
|
58
67
|
manuallyTriggerHashChangeForFirefox()
|
59
68
|
reflectRedirectedUrl()
|
60
|
-
onLoadFunction()
|
69
|
+
onLoadFunction?()
|
61
70
|
triggerEvent EVENTS.LOAD
|
62
71
|
else
|
63
|
-
document.location.href = url.absolute
|
72
|
+
document.location.href = crossOriginRedirect() or url.absolute
|
73
|
+
|
74
|
+
if progressBar and showProgressBar
|
75
|
+
xhr.onprogress = (event) =>
|
76
|
+
percent = if event.lengthComputable
|
77
|
+
event.loaded / event.total * 100
|
78
|
+
else
|
79
|
+
progressBar.value + (100 - progressBar.value) / 10
|
80
|
+
progressBar.advanceTo(percent)
|
64
81
|
|
65
82
|
xhr.onloadend = -> xhr = null
|
66
83
|
xhr.onerror = -> document.location.href = url.absolute
|
@@ -110,6 +127,7 @@ changePage = (title, body, csrfToken, runScripts) ->
|
|
110
127
|
setAutofocusElement()
|
111
128
|
executeScriptTags() if runScripts
|
112
129
|
currentState = window.history.state
|
130
|
+
progressBar?.done()
|
113
131
|
triggerEvent EVENTS.CHANGE
|
114
132
|
triggerEvent EVENTS.UPDATE
|
115
133
|
|
@@ -134,7 +152,7 @@ setAutofocusElement = ->
|
|
134
152
|
autofocusElement = (list = document.querySelectorAll 'input[autofocus], textarea[autofocus]')[list.length - 1]
|
135
153
|
if autofocusElement and document.activeElement isnt autofocusElement
|
136
154
|
autofocusElement.focus()
|
137
|
-
|
155
|
+
|
138
156
|
reflectNewUrl = (url) ->
|
139
157
|
if (url = new ComponentUrl url).absolute isnt referer
|
140
158
|
window.history.pushState { turbolinks: true, url: url.absolute }, '', url.absolute
|
@@ -145,6 +163,9 @@ reflectRedirectedUrl = ->
|
|
145
163
|
preservedHash = if location.hasNoHash() then document.location.hash else ''
|
146
164
|
window.history.replaceState currentState, '', location.href + preservedHash
|
147
165
|
|
166
|
+
crossOriginRedirect = ->
|
167
|
+
redirect if (redirect = xhr.getResponseHeader('Location'))? and (new ComponentUrl(redirect)).crossOrigin()
|
168
|
+
|
148
169
|
rememberReferer = ->
|
149
170
|
referer = document.location.href
|
150
171
|
|
@@ -204,7 +225,7 @@ processResponse = ->
|
|
204
225
|
400 <= xhr.status < 600
|
205
226
|
|
206
227
|
validContent = ->
|
207
|
-
(contentType = xhr.getResponseHeader('Content-Type'))? and
|
228
|
+
(contentType = xhr.getResponseHeader('Content-Type'))? and
|
208
229
|
contentType.match /^(?:text\/html|application\/xhtml\+xml|application\/xml)(?:;|$)/
|
209
230
|
|
210
231
|
extractTrackAssets = (doc) ->
|
@@ -303,10 +324,10 @@ browserCompatibleDocumentParser = ->
|
|
303
324
|
|
304
325
|
|
305
326
|
# The ComponentUrl class converts a basic URL string into an object
|
306
|
-
# that behaves similarly to document.location.
|
327
|
+
# that behaves similarly to document.location.
|
307
328
|
#
|
308
|
-
# If an instance is created from a relative URL, the current document
|
309
|
-
# is used to fill in the missing attributes (protocol, host, port).
|
329
|
+
# If an instance is created from a relative URL, the current document
|
330
|
+
# is used to fill in the missing attributes (protocol, host, port).
|
310
331
|
class ComponentUrl
|
311
332
|
constructor: (@original = document.location.href) ->
|
312
333
|
return @original if @original.constructor is ComponentUrl
|
@@ -319,6 +340,9 @@ class ComponentUrl
|
|
319
340
|
|
320
341
|
hasNoHash: -> @hash.length is 0
|
321
342
|
|
343
|
+
crossOrigin: ->
|
344
|
+
@origin isnt (new ComponentUrl).origin
|
345
|
+
|
322
346
|
_parse: ->
|
323
347
|
(@link ?= document.createElement 'a').href = @original
|
324
348
|
{ @href, @protocol, @host, @hostname, @port, @pathname, @search, @hash } = @link
|
@@ -345,15 +369,12 @@ class Link extends ComponentUrl
|
|
345
369
|
super
|
346
370
|
|
347
371
|
shouldIgnore: ->
|
348
|
-
@
|
349
|
-
@_anchored() or
|
350
|
-
@_nonHtml() or
|
351
|
-
@_optOut() or
|
372
|
+
@crossOrigin() or
|
373
|
+
@_anchored() or
|
374
|
+
@_nonHtml() or
|
375
|
+
@_optOut() or
|
352
376
|
@_target()
|
353
377
|
|
354
|
-
_crossOrigin: ->
|
355
|
-
@origin isnt (new ComponentUrl).origin
|
356
|
-
|
357
378
|
_anchored: ->
|
358
379
|
(@hash.length > 0 or @href.charAt(@href.length - 1) is '#') and
|
359
380
|
(@withoutHash() is (new ComponentUrl).withoutHash())
|
@@ -373,9 +394,9 @@ class Link extends ComponentUrl
|
|
373
394
|
|
374
395
|
|
375
396
|
# The Click class handles clicked links, verifying if Turbolinks should
|
376
|
-
# take control by inspecting both the event and the link. If it should,
|
377
|
-
# the page change process is initiated. If not, control is passed back
|
378
|
-
# to the browser for default functionality.
|
397
|
+
# take control by inspecting both the event and the link. If it should,
|
398
|
+
# the page change process is initiated. If not, control is passed back
|
399
|
+
# to the browser for default functionality.
|
379
400
|
class Click
|
380
401
|
@installHandlerLast: (event) ->
|
381
402
|
unless event.defaultPrevented
|
@@ -390,7 +411,7 @@ class Click
|
|
390
411
|
@_extractLink()
|
391
412
|
if @_validForTurbolinks()
|
392
413
|
visit @link.href unless pageChangePrevented(@link.absolute)
|
393
|
-
@event.preventDefault()
|
414
|
+
@event.preventDefault()
|
394
415
|
|
395
416
|
_extractLink: ->
|
396
417
|
link = @event.target
|
@@ -401,13 +422,108 @@ class Click
|
|
401
422
|
@link? and not (@link.shouldIgnore() or @_nonStandardClick())
|
402
423
|
|
403
424
|
_nonStandardClick: ->
|
404
|
-
@event.which > 1 or
|
405
|
-
@event.metaKey or
|
406
|
-
@event.ctrlKey or
|
407
|
-
@event.shiftKey or
|
425
|
+
@event.which > 1 or
|
426
|
+
@event.metaKey or
|
427
|
+
@event.ctrlKey or
|
428
|
+
@event.shiftKey or
|
408
429
|
@event.altKey
|
409
430
|
|
410
431
|
|
432
|
+
class ProgressBar
|
433
|
+
className = 'turbolinks-progress-bar'
|
434
|
+
|
435
|
+
constructor: (@elementSelector) ->
|
436
|
+
@value = 0
|
437
|
+
@opacity = 1
|
438
|
+
@content = ''
|
439
|
+
@speed = 300
|
440
|
+
@install()
|
441
|
+
|
442
|
+
install: ->
|
443
|
+
@element = document.querySelector(@elementSelector)
|
444
|
+
@element.classList.add(className)
|
445
|
+
@styleElement = document.createElement('style')
|
446
|
+
document.head.appendChild(@styleElement)
|
447
|
+
@_updateStyle()
|
448
|
+
|
449
|
+
uninstall: ->
|
450
|
+
@element.classList.remove(className)
|
451
|
+
document.head.removeChild(@styleElement)
|
452
|
+
|
453
|
+
start: ->
|
454
|
+
@advanceTo(5)
|
455
|
+
|
456
|
+
advanceTo: (value) ->
|
457
|
+
if value > @value <= 100
|
458
|
+
@value = value
|
459
|
+
@_updateStyle()
|
460
|
+
|
461
|
+
if @value is 100
|
462
|
+
@_stopTrickle()
|
463
|
+
else if @value > 0
|
464
|
+
@_startTrickle()
|
465
|
+
|
466
|
+
done: ->
|
467
|
+
if @value > 0
|
468
|
+
@advanceTo(100)
|
469
|
+
@_reset()
|
470
|
+
|
471
|
+
_reset: ->
|
472
|
+
setTimeout =>
|
473
|
+
@opacity = 0
|
474
|
+
@_updateStyle()
|
475
|
+
, @speed / 2
|
476
|
+
|
477
|
+
setTimeout =>
|
478
|
+
@value = 0
|
479
|
+
@opacity = 1
|
480
|
+
@_withSpeed(0, => @_updateStyle(true))
|
481
|
+
, @speed
|
482
|
+
|
483
|
+
_startTrickle: ->
|
484
|
+
return if @trickling
|
485
|
+
@trickling = true
|
486
|
+
setTimeout(@_trickle, @speed)
|
487
|
+
|
488
|
+
_stopTrickle: ->
|
489
|
+
delete @trickling
|
490
|
+
|
491
|
+
_trickle: =>
|
492
|
+
return unless @trickling
|
493
|
+
@advanceTo(@value + Math.random() / 2)
|
494
|
+
setTimeout(@_trickle, @speed)
|
495
|
+
|
496
|
+
_withSpeed: (speed, fn) ->
|
497
|
+
originalSpeed = @speed
|
498
|
+
@speed = speed
|
499
|
+
result = fn()
|
500
|
+
@speed = originalSpeed
|
501
|
+
result
|
502
|
+
|
503
|
+
_updateStyle: (forceRepaint = false) ->
|
504
|
+
@_changeContentToForceRepaint() if forceRepaint
|
505
|
+
@styleElement.textContent = @_createCSSRule()
|
506
|
+
|
507
|
+
_changeContentToForceRepaint: ->
|
508
|
+
@content = if @content is '' then ' ' else ''
|
509
|
+
|
510
|
+
_createCSSRule: ->
|
511
|
+
"""
|
512
|
+
#{@elementSelector}.#{className}::before {
|
513
|
+
content: '#{@content}';
|
514
|
+
position: fixed;
|
515
|
+
top: 0;
|
516
|
+
left: 0;
|
517
|
+
background-color: #0076ff;
|
518
|
+
height: 3px;
|
519
|
+
opacity: #{@opacity};
|
520
|
+
width: #{@value}%;
|
521
|
+
transition: width #{@speed}ms ease-out, opacity #{@speed / 2}ms ease-in;
|
522
|
+
transform: translate3d(0,0,0);
|
523
|
+
}
|
524
|
+
"""
|
525
|
+
|
526
|
+
|
411
527
|
# Delay execution of function long enough to miss the popstate event
|
412
528
|
# some browsers fire on the initial page load.
|
413
529
|
bypassOnLoadPopstate = (fn) ->
|
@@ -487,7 +603,8 @@ else
|
|
487
603
|
visit,
|
488
604
|
pagesCached,
|
489
605
|
enableTransitionCache,
|
606
|
+
enableProgressBar,
|
490
607
|
allowLinkExtensions: Link.allowExtensions,
|
491
608
|
supported: browserSupportsTurbolinks,
|
492
609
|
EVENTS: clone(EVENTS)
|
493
|
-
}
|
610
|
+
}
|
data/lib/turbolinks/version.rb
CHANGED
data/test/config.ru
CHANGED
@@ -7,6 +7,29 @@ Assets = Sprockets::Environment.new do |env|
|
|
7
7
|
env.append_path File.join(Root, "lib", "assets", "javascripts")
|
8
8
|
end
|
9
9
|
|
10
|
+
class SlowResponse
|
11
|
+
CHUNKS = ['<html><body>', '.'*50, '.'*20, '<a href="/index.html">Home</a></body></html>']
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
[200, headers, self]
|
15
|
+
end
|
16
|
+
|
17
|
+
def each
|
18
|
+
CHUNKS.each do |part|
|
19
|
+
sleep rand(0.3..0.8)
|
20
|
+
yield part
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def length
|
25
|
+
CHUNKS.join.length
|
26
|
+
end
|
27
|
+
|
28
|
+
def headers
|
29
|
+
{ "Content-Length" => length.to_s, "Content-Type" => "text/html", "Cache-Control" => "no-cache, no-store, must-revalidate" }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
10
33
|
map "/js" do
|
11
34
|
run Assets
|
12
35
|
end
|
@@ -19,6 +42,10 @@ map "/withoutextension" do
|
|
19
42
|
run Rack::File.new(File.join(Root, "test", "withoutextension"), "Content-Type" => "text/html")
|
20
43
|
end
|
21
44
|
|
45
|
+
map "/slow-response" do
|
46
|
+
run SlowResponse.new
|
47
|
+
end
|
48
|
+
|
22
49
|
map "/" do
|
23
50
|
run Rack::Directory.new(File.join(Root, "test"))
|
24
51
|
end
|
data/test/index.html
CHANGED
@@ -5,6 +5,8 @@
|
|
5
5
|
<title>Home</title>
|
6
6
|
<script type="text/javascript" src="/js/turbolinks.js"></script>
|
7
7
|
<script type="text/javascript">
|
8
|
+
Turbolinks.enableProgressBar();
|
9
|
+
|
8
10
|
document.addEventListener("page:change", function() {
|
9
11
|
console.log("page changed");
|
10
12
|
});
|
@@ -19,8 +21,9 @@
|
|
19
21
|
</script>
|
20
22
|
</head>
|
21
23
|
<body class="page-index">
|
22
|
-
<ul style="margin-top:
|
24
|
+
<ul style="margin-top:20px;">
|
23
25
|
<li><a href="/other.html">Other page</a></li>
|
26
|
+
<li><a href="/slow-response">Slow loading page for progress bar</a></li>
|
24
27
|
<li><a href="/other.html"><span>Wrapped link</span></a></li>
|
25
28
|
<li><a href="/withoutextension">Without extension</a></li>
|
26
29
|
<li><a href="/withoutextension?sort=user.name">Without extension with query params</a></li>
|
metadata
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: turbolinks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Heinemeier Hansson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-10-
|
11
|
+
date: 2014-10-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: coffee-rails
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
description:
|
@@ -30,6 +30,8 @@ executables: []
|
|
30
30
|
extensions: []
|
31
31
|
extra_rdoc_files: []
|
32
32
|
files:
|
33
|
+
- MIT-LICENSE
|
34
|
+
- README.md
|
33
35
|
- lib/assets/javascripts/turbolinks.js.coffee
|
34
36
|
- lib/turbolinks.rb
|
35
37
|
- lib/turbolinks/cookies.rb
|
@@ -38,8 +40,6 @@ files:
|
|
38
40
|
- lib/turbolinks/x_domain_blocker.rb
|
39
41
|
- lib/turbolinks/xhr_headers.rb
|
40
42
|
- lib/turbolinks/xhr_url_for.rb
|
41
|
-
- README.md
|
42
|
-
- MIT-LICENSE
|
43
43
|
- test/config.ru
|
44
44
|
- test/dummy.gif
|
45
45
|
- test/index.html
|
@@ -58,20 +58,19 @@ require_paths:
|
|
58
58
|
- lib
|
59
59
|
required_ruby_version: !ruby/object:Gem::Requirement
|
60
60
|
requirements:
|
61
|
-
- -
|
61
|
+
- - ">="
|
62
62
|
- !ruby/object:Gem::Version
|
63
63
|
version: '0'
|
64
64
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
requirements: []
|
70
70
|
rubyforge_project:
|
71
|
-
rubygems_version: 2.
|
71
|
+
rubygems_version: 2.2.2
|
72
72
|
signing_key:
|
73
73
|
specification_version: 4
|
74
74
|
summary: Turbolinks makes following links in your web application faster (use with
|
75
75
|
Rails Asset Pipeline)
|
76
76
|
test_files: []
|
77
|
-
has_rdoc:
|