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:
parent
b4f3eac0bf
commit
535a873dc0
|
@ -35,7 +35,8 @@ unless window.TravisApplication
|
||||||
|
|
||||||
@slider = new Travis.Slider()
|
@slider = new Travis.Slider()
|
||||||
@pusher = new Travis.Pusher(Travis.config.pusher_key) if Travis.config.pusher_key
|
@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))
|
@set('auth', Travis.Auth.create(app: this, endpoint: Travis.config.api_endpoint))
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,51 @@
|
||||||
@Travis.Tailing = ->
|
class Travis.ToTop
|
||||||
@position = $(window).scrollTop()
|
# NOTE: I could have probably extract fixed positioning from
|
||||||
$(window).scroll( $.throttle( 200, @onScroll.bind(this) ) )
|
# Tailing, but then I would need to parametrize positionElement
|
||||||
this
|
# 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:
|
options:
|
||||||
timeout: 200
|
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: ->
|
run: ->
|
||||||
@autoScroll()
|
@autoScroll()
|
||||||
@positionButton()
|
@positionButton()
|
||||||
|
@ -16,37 +55,43 @@ $.extend Travis.Tailing.prototype,
|
||||||
if @active() then @stop() else @start()
|
if @active() then @stop() else @start()
|
||||||
|
|
||||||
active: ->
|
active: ->
|
||||||
$('#tail').hasClass('active')
|
@tail().hasClass('active')
|
||||||
|
|
||||||
start: ->
|
start: ->
|
||||||
$('#tail').addClass('active')
|
@tail().addClass('active')
|
||||||
@run()
|
@run()
|
||||||
|
|
||||||
stop: ->
|
stop: ->
|
||||||
$('#tail').removeClass('active')
|
@tail().removeClass('active')
|
||||||
|
|
||||||
autoScroll: ->
|
autoScroll: ->
|
||||||
return unless @active()
|
return false unless @active()
|
||||||
win = $(window)
|
logBottom = @log().offset().top + @log().outerHeight() + 40
|
||||||
log = $('#log')
|
winBottom = @window.scrollTop() + @window.height()
|
||||||
logBottom = log.offset().top + log.outerHeight() + 40
|
|
||||||
winBottom = win.scrollTop() + win.height()
|
if logBottom - winBottom > 0
|
||||||
win.scrollTop(logBottom - win.height()) if logBottom - winBottom > 0
|
@window.scrollTop(logBottom - @window.height())
|
||||||
|
true
|
||||||
|
else
|
||||||
|
false
|
||||||
|
|
||||||
onScroll: ->
|
onScroll: ->
|
||||||
@positionButton()
|
@positionButton()
|
||||||
position = $(window).scrollTop()
|
position = @window.scrollTop()
|
||||||
@stop() if position < @position
|
@stop() if position < @position
|
||||||
@position = position
|
@position = position
|
||||||
|
|
||||||
positionButton: ->
|
positionButton: ->
|
||||||
tail = $('#tail')
|
return if @tail().length is 0
|
||||||
return if tail.length is 0
|
offset = @window.scrollTop() - @log().offset().top
|
||||||
offset = $(window).scrollTop() - $('#log').offset().top
|
max = @log().height() - @tail().height() + 5
|
||||||
max = $('#log').height() - $('#tail').height() + 5
|
|
||||||
offset = max if offset > max
|
|
||||||
if offset > 0
|
|
||||||
tail.css(top: offset - 2)
|
|
||||||
else
|
|
||||||
tail.css(top: 0)
|
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
|
@ -1,7 +1,14 @@
|
||||||
<div id="log-container">
|
<div id="log-container">
|
||||||
<a href="#" id="tail" {{action "toggleTailing" target="view"}}>
|
<a href="#" id="tail" {{action "toggleTailing" target="view"}}>
|
||||||
<span class="status"></span>
|
<span class="status"></span>
|
||||||
<label>Follow logs</label>
|
|
||||||
|
<label>
|
||||||
|
{{#if view.job.isFinished}}
|
||||||
|
Scroll to end of logs
|
||||||
|
{{else}}
|
||||||
|
Follow logs
|
||||||
|
{{/if}}
|
||||||
|
</label>
|
||||||
</a>
|
</a>
|
||||||
<pre id="log" class="ansi"></pre>
|
<pre id="log" class="ansi"></pre>
|
||||||
|
|
||||||
|
|
89
assets/scripts/spec/unit/tailing_spec.coffee
Normal file
89
assets/scripts/spec/unit/tailing_spec.coffee
Normal 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)
|
|
@ -88,44 +88,67 @@ pre#log
|
||||||
#log-container
|
#log-container
|
||||||
position: relative
|
position: relative
|
||||||
|
|
||||||
#log-container #tail
|
#log-container
|
||||||
z-index: 99
|
#tail
|
||||||
position: absolute
|
z-index: 99
|
||||||
display: block
|
position: absolute
|
||||||
top: 0
|
display: block
|
||||||
right: 2px
|
top: 0
|
||||||
margin: 13px 10px 0 0
|
right: 2px
|
||||||
padding: 0 2px 0 3px
|
margin: 13px 10px 0 0
|
||||||
color: #666
|
padding: 0 2px 0 3px
|
||||||
text-shadow: 0px 1px 0px #fff
|
color: #666
|
||||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif
|
text-shadow: 0px 1px 0px #fff
|
||||||
font-size: $font-size-tiny
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif
|
||||||
line-height: 14px
|
font-size: $font-size-tiny
|
||||||
text-decoration: none
|
line-height: 14px
|
||||||
white-space: nowrap
|
text-decoration: none
|
||||||
border: 1px solid #bbb
|
white-space: nowrap
|
||||||
border-top-color: #ddd
|
border: 1px solid #bbb
|
||||||
border-bottom-color: #bbb
|
border-top-color: #ddd
|
||||||
@include border-radius(8px)
|
border-bottom-color: #bbb
|
||||||
@include background(linear-gradient(#fff, #e0e0e0))
|
@include border-radius(8px)
|
||||||
|
@include background(linear-gradient(#fff, #e0e0e0))
|
||||||
|
|
||||||
label
|
|
||||||
display: none
|
|
||||||
cursor: pointer
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
padding: 1px 4px 1px 6px
|
|
||||||
label
|
label
|
||||||
display: inline
|
display: none
|
||||||
|
cursor: pointer
|
||||||
|
|
||||||
.status
|
&:hover
|
||||||
display: inline-block
|
padding: 1px 4px 1px 6px
|
||||||
margin-right: 1px
|
label
|
||||||
width: 8px
|
display: inline
|
||||||
height: 8px
|
|
||||||
background-color: #aaa
|
|
||||||
@include border-radius(4px)
|
|
||||||
@include box-shadow(white 1px 1px 2px)
|
|
||||||
|
|
||||||
&.active .status
|
&.scrolling
|
||||||
background-color: #6b0
|
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
|
||||||
|
|
|
@ -3,12 +3,3 @@
|
||||||
// float: left
|
// float: left
|
||||||
margin-top: 0
|
margin-top: 0
|
||||||
color: #999
|
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
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user