turbolinks 2.4.0 → 2.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|