Make "to top" link visible when scrolling through the log

These are the changes by @dmathieu (reverted at d7bef2b) slightly
changed to allow us to use them also on Travis Pro.

The change is to still use onScroll calculations in order to position
elements instead of using "position: fixed". The latter method is harder
to use when element needs to be positioned relatively to other element -
on Pro we would have to still calculate the position because of the
right sidebar.
This commit is contained in:
Piotr Sarnacki 2014-03-04 14:02:22 +01:00
parent b4f3eac0bf
commit 535a873dc0
6 changed files with 228 additions and 72 deletions

View File

@ -35,7 +35,8 @@ unless window.TravisApplication
@slider = new Travis.Slider()
@pusher = new Travis.Pusher(Travis.config.pusher_key) if Travis.config.pusher_key
@tailing = new Travis.Tailing()
@tailing = new Travis.Tailing($(window), '#tail', '#log')
@toTop = new Travis.ToTop($(window), '.to-top', '#log-container')
@set('auth', Travis.Auth.create(app: this, endpoint: Travis.config.api_endpoint))

View File

@ -1,12 +1,51 @@
@Travis.Tailing = ->
@position = $(window).scrollTop()
$(window).scroll( $.throttle( 200, @onScroll.bind(this) ) )
this
class Travis.ToTop
# NOTE: I could have probably extract fixed positioning from
# Tailing, but then I would need to parametrize positionElement
# function to make it flexible to handle both cases. In that
# situation I prefer a bit less DRY code over simplicity of
# the calculations.
constructor: (@window, @element_selector, @container_selector) ->
@position = @window.scrollTop()
@window.scroll( $.throttle( 200, @onScroll.bind(this) ) )
this
$.extend Travis.Tailing.prototype,
element: ->
$(@element_selector)
container: ->
$(@container_selector)
onScroll: ->
@positionElement()
positionElement: ->
element = @element()
container = @container()
return if element.length is 0
containerHeight = container.height()
windowHeight = @window.height()
offset = container.offset().top + containerHeight - (@window.scrollTop() + windowHeight)
max = containerHeight - windowHeight
offset = max if offset > max
console.log(offset, max)
if offset > 0
element.css(bottom: offset)
else
element.css(bottom: 2)
class @Travis.Tailing
options:
timeout: 200
tail: ->
$(@tail_selector)
log: ->
$(@log_selector)
constructor: (@window, @tail_selector, @log_selector) ->
@position = @window.scrollTop()
@window.scroll( $.throttle( 200, @onScroll.bind(this) ) )
this
run: ->
@autoScroll()
@positionButton()
@ -16,37 +55,43 @@ $.extend Travis.Tailing.prototype,
if @active() then @stop() else @start()
active: ->
$('#tail').hasClass('active')
@tail().hasClass('active')
start: ->
$('#tail').addClass('active')
@tail().addClass('active')
@run()
stop: ->
$('#tail').removeClass('active')
@tail().removeClass('active')
autoScroll: ->
return unless @active()
win = $(window)
log = $('#log')
logBottom = log.offset().top + log.outerHeight() + 40
winBottom = win.scrollTop() + win.height()
win.scrollTop(logBottom - win.height()) if logBottom - winBottom > 0
return false unless @active()
logBottom = @log().offset().top + @log().outerHeight() + 40
winBottom = @window.scrollTop() + @window.height()
if logBottom - winBottom > 0
@window.scrollTop(logBottom - @window.height())
true
else
false
onScroll: ->
@positionButton()
position = $(window).scrollTop()
position = @window.scrollTop()
@stop() if position < @position
@position = position
positionButton: ->
tail = $('#tail')
return if tail.length is 0
offset = $(window).scrollTop() - $('#log').offset().top
max = $('#log').height() - $('#tail').height() + 5
offset = max if offset > max
if offset > 0
tail.css(top: offset - 2)
else
tail.css(top: 0)
return if @tail().length is 0
offset = @window.scrollTop() - @log().offset().top
max = @log().height() - @tail().height() + 5
if offset > 0 && offset <= max
@tail().removeClass('bottom')
@tail().addClass('scrolling')
else
if offset > max
@tail().addClass('bottom')
else
@tail().removeClass('bottom')
@tail().removeClass('scrolling')

View File

@ -1,7 +1,14 @@
<div id="log-container">
<a href="#" id="tail" {{action "toggleTailing" target="view"}}>
<span class="status"></span>
<label>Follow logs</label>
<label>
{{#if view.job.isFinished}}
Scroll to end of logs
{{else}}
Follow logs
{{/if}}
</label>
</a>
<pre id="log" class="ansi"></pre>

View File

@ -0,0 +1,89 @@
fakeWindow =
scroll: sinon.spy()
scrollTop: sinon.stub().returns(0)
height: sinon.stub().returns(40)
element = jQuery('<div id="specTail"></div>')
log = jQuery('<div id="specLog"></div>')
tail = new Travis.Tailing(fakeWindow, '#specTail', '#specLog')
tail.tail = -> element
tail.log = -> log
module "Travis.Tailing",
setup: ->
jQuery('body').append(element)
jQuery('body').append(log)
teardown: ->
element.remove()
log.remove()
tail.stop()
test "toggle", ->
equal(element.hasClass('active'), false)
tail.toggle()
equal(element.hasClass('active'), true)
tail.toggle()
stop()
Ember.run.later ->
start()
equal(element.hasClass('active'), false)
, 300
test "active", ->
equal(tail.active(), false)
element.addClass('active')
equal(tail.active(), true)
test "autoscroll when inactive", ->
tail.scrollTo = sinon.spy()
equal(tail.active(), false)
equal(tail.autoScroll(), false)
equal(tail.scrollTo.called, false)
test "autoscroll", ->
element.addClass('active')
log.offset = -> {top: 1}
log.outerHeight = -> 1
equal(tail.active(), true)
equal(tail.autoScroll(), true)
equal(fakeWindow.scrollTop.calledWith(2), true)
test "autoscroll when we're at the bottom", ->
element.addClass('active')
log.offset = -> {top: 0}
log.outerHeight = -> 0
equal(tail.active(), true)
equal(tail.autoScroll(), false)
equal(fakeWindow.scrollTop.calledWith(0), false)
test 'should stop scrolling if the position changed', ->
element.addClass('active')
tail.position = 100
tail.onScroll()
equal(element.hasClass('active'), false)
test 'positionButton adds the scrolling class', ->
log.offset = -> {top: -1}
tail.positionButton()
equal(element.hasClass('scrolling'), true)
equal(element.hasClass('bottom'), false)
test 'positionButton removes the scrolling class', ->
log.offset = -> {top: 1}
tail.positionButton()
equal(element.hasClass('scrolling'), false)
equal(element.hasClass('bottom'), false)
test 'positionButton sets the button as bottom', ->
log.offset = -> {top: -100}
log.height = -> 50
tail.height = -> 1
tail.positionButton()
equal(element.hasClass('scrolling'), false)
equal(element.hasClass('bottom'), true)

View File

@ -88,44 +88,67 @@ pre#log
#log-container
position: relative
#log-container #tail
z-index: 99
position: absolute
display: block
top: 0
right: 2px
margin: 13px 10px 0 0
padding: 0 2px 0 3px
color: #666
text-shadow: 0px 1px 0px #fff
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif
font-size: $font-size-tiny
line-height: 14px
text-decoration: none
white-space: nowrap
border: 1px solid #bbb
border-top-color: #ddd
border-bottom-color: #bbb
@include border-radius(8px)
@include background(linear-gradient(#fff, #e0e0e0))
#log-container
#tail
z-index: 99
position: absolute
display: block
top: 0
right: 2px
margin: 13px 10px 0 0
padding: 0 2px 0 3px
color: #666
text-shadow: 0px 1px 0px #fff
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif
font-size: $font-size-tiny
line-height: 14px
text-decoration: none
white-space: nowrap
border: 1px solid #bbb
border-top-color: #ddd
border-bottom-color: #bbb
@include border-radius(8px)
@include background(linear-gradient(#fff, #e0e0e0))
label
display: none
cursor: pointer
&:hover
padding: 1px 4px 1px 6px
label
display: inline
display: none
cursor: pointer
.status
display: inline-block
margin-right: 1px
width: 8px
height: 8px
background-color: #aaa
@include border-radius(4px)
@include box-shadow(white 1px 1px 2px)
&:hover
padding: 1px 4px 1px 6px
label
display: inline
&.active .status
background-color: #6b0
&.scrolling
position: fixed
right: 32px
&.bottom
bottom: 45px
top: inherit
.status
display: inline-block
margin-right: 1px
width: 8px
height: 8px
background-color: #aaa
@include border-radius(4px)
@include box-shadow(white 1px 1px 2px)
&.active .status
background-color: #6b0
.to-top
z-index: 99
position: absolute
display: block
bottom: 2px
right: 2px
width: 50px
margin-right: 2px
padding-right: 16px
text-align: right
color: #999
background: inline-image('ui/to-top.png') no-repeat right 6px

View File

@ -3,12 +3,3 @@
// float: left
margin-top: 0
color: #999
.to-top
display: inline-block
width: 50px
float: right
margin-right: 2px
padding-right: 16px
text-align: right
color: #999
background: inline-image('ui/to-top.png') no-repeat right 6px