all things scroll following and anchoring

This commit is contained in:
John Mitchell 2016-11-11 14:27:35 -05:00 committed by jaredevantabor
parent 1c907a1f07
commit c94d86206d
6 changed files with 196 additions and 9 deletions

View File

@ -85,6 +85,24 @@
color: @default-interface-txt;
}
.JobResultsStdOut-followButton.is-engaged {
background-color: @default-link;
color: @default-bg;
}
.JobResultsStdOut-followButton.is-engaged .JobResultsStdOut-followIcon {
color: @default-bg;
}
.JobResultsStdOut-followButton.is-engaged:hover {
background-color: @default-icon;
}
.JobResultsStdOut-followButton.is-engaged:hover .JobResultsStdOut-followIcon,
.JobResultsStdOut-followButton.is-engaged .JobResultsStdOut-followIcon:hover {
color: @default-border;
}
.JobResultsStdOut-stdoutContainer {
height: ~"calc(100% - 48px)";
background-color: @default-no-items-bord;
@ -167,3 +185,10 @@
width: 70px;
height: 100%;
}
.foo {
margin-right: auto;
margin-top: 8px;
margin-left: 20px;
font-weight: bold;
}

View File

@ -5,14 +5,132 @@
*************************************************/
// import hostStatusBarController from './host-status-bar.controller';
export default [ 'templateUrl', '$timeout',
function(templateUrl, $timeout) {
export default [ 'templateUrl', '$timeout', '$location', '$anchorScroll', '$log',
function(templateUrl, $timeout, $location, $anchorScroll, $log) {
return {
scope: false,
templateUrl: templateUrl('job-results/job-results-stdout/job-results-stdout'),
restrict: 'E',
link: function(scope, element, attrs) {
link: function(scope, element) {
var findTopLines = function() {
scope.visibleItems = "";
var $container = $('.JobResultsStdOut-stdoutContainer');
var visItem,
parentItem;
$container.find('.JobResultsStdOut-aLineOfStdOut').each( function () {
var $this = $(this);
if ( $this.position().top + $this.height() > $container.position().top &&
$this.position().top < ($container.height() + $container.position().top) ) {
visItem = parseInt($($this.children()[0]).text());
var $head,
classList,
header;
if ($this.hasClass("header_play") || $this.hasClass("header_task")) {
classList = $this.attr("class")
.split(" ");
header = classList
.filter(n => n.indexOf("header_task_") > -1)[0];
if (!header) {
header = classList
.filter(n => n.indexOf("header_play_") > -1)[0];
}
$head = $(".actual_header." + header);
} else {
classList = $this.attr("class")
.split(" ");
header = classList
.filter(n => n.indexOf("task_") > -1)[0];
if (!header) {
header = classList
.filter(n => n.indexOf("play_") > -1)[0];
}
$head = $(".actual_header.header_" + header);
}
parentItem = parseInt($($head.children()[0]).text());
return false;
}
});
scope.visLine = visItem;
scope.parentVisLine = parentItem;
scope.visibleItems = "First visible line: " + visItem + ", Parent line: " + parentItem;
};
var lastScrollTop = 0;
$(".JobResultsStdOut-stdoutContainer").on('scroll', function() {
var st = $(this).scrollTop();
if (st < lastScrollTop){
// user up scrolled, so disengage follow
scope.followEngaged = false;
}
if($(this).scrollTop() + $(this).innerHeight() >=
$(this)[0].scrollHeight) {
// user scrolled all the way to bottom, so engage
// follow
scope.followEngaged = true;
}
lastScrollTop = st;
});
scope.followScroll = function() {
$(".JobResultsStdOut-followAnchor")
.appendTo(".JobResultsStdOut-stdoutContainer");
$location.hash('followAnchor');
$anchorScroll();
};
scope.topLineAnchor = function() {
$location.hash('topLineAnchor');
$anchorScroll();
}
// follow button for completed job should specify that the
// button will jump to the bottom of the standard out pane,
// not follow lines as they come in
if (scope.jobFinished) {
scope.followTooltip = "Jump to last line";
}
// if following becomes active, go ahead and get to the bottom
// of the standard out pane
scope.$watch('followEngaged', function(val) {
if (val) {
scope.followScroll();
}
if (!scope.jobFinished) {
if (val) {
scope.followTooltip = "Follow standard out";
} else {
scope.followTooltip = "Unfollow standard out";
}
}
});
scope.followToggleClicked = function() {
if (scope.jobFinished) {
scope.followScroll();
} else {
scope.followEngaged = !scope.followEngaged;
}
};
scope.toggleAllStdout = function(type) {
findTopLines();
if (type === 'expand') {
$(".line_num_" + scope.visLine)
.prepend($("#topLineAnchor"));
} else {
$(".line_num_" + scope.parentVisLine)
.prepend($("#topLineAnchor"));
}
var expandClass;
if (type === 'expand') {
expandClass = "fa-caret-right";
@ -24,6 +142,7 @@ export default [ 'templateUrl', '$timeout',
.each((i, val) => {
$timeout(function(){
angular.element(val).trigger('click');
scope.topLineAnchor();
});
});
@ -38,6 +157,7 @@ export default [ 'templateUrl', '$timeout',
$timeout(function(){
angular.element(val)
.trigger('click');
scope.topLineAnchor();
});
}
});

View File

@ -17,16 +17,32 @@
</div>
</div>
<div class="JobResultsStdOut-toolbarStdoutColumn">
<!-- <span class="foo">{{ visibleItems }}</span> -->
<div class="JobResultsStdOut-followButton"
aw-tool-tip="Follow standard out."
data-placement="left">
ng-class="{'is-engaged': followEngaged && !jobFinished}"
aw-tool-tip="Follow standard out"
data-placement="left"
ng-click="followToggleClicked()">
<i class="JobResultsStdOut-followIcon fa fa-arrow-down">
</i>
</div>
<!-- TODO: get followTooltip watch working
<div class="JobResultsStdOut-followButton"
aw-tool-tip="{{followTooltip}}"
data-tip-watch="followTooltip"
data-placement="left"
ng-click="followToggleClicked()">
<i class="JobResultsStdOut-followIcon fa fa-arrow-down">
</i>
</div> -->
</div>
</div>
<div class="JobResultsStdOut-stdoutContainer">
<div class="JobResultsStdOut-numberColumnPreload"></div>
<div id='topLineAnchor'></div>
<div id="followAnchor"
class="JobResultsStdOut-followAnchor">
</div>
</div>
<div class="JobResultsStdOut-footer">
<div class="JobResultsStdOut-footerNumberColumn"></div>

View File

@ -1,4 +1,4 @@
export default ['jobData', 'jobDataOptions', 'jobLabels', 'count', '$scope', 'ParseTypeChange', 'ParseVariableString', 'jobResultsService', '$rootScope', 'eventQueue', '$compile', function(jobData, jobDataOptions, jobLabels, count, $scope, ParseTypeChange, ParseVariableString, jobResultsService, $rootScope, eventQueue, $compile) {
export default ['jobData', 'jobDataOptions', 'jobLabels', 'jobFinished', 'count', '$scope', 'ParseTypeChange', 'ParseVariableString', 'jobResultsService', '$rootScope', 'eventQueue', '$compile', function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTypeChange, ParseVariableString, jobResultsService, $rootScope, eventQueue, $compile) {
var getTowerLinks = function() {
var getTowerLink = function(key) {
if ($scope.job.related[key]) {
@ -81,6 +81,11 @@ export default ['jobData', 'jobDataOptions', 'jobLabels', 'count', '$scope', 'Pa
$scope.countFinished = count.countFinished;
$scope.stdoutArr = [];
// if the job is still running engage following of the last line in the
// standard out pane
$scope.jobFinished = jobFinished;
$scope.followEngaged = !$scope.jobFinished;
// EVENT STUFF BELOW
// just putting the event queue on scope so it can be inspected in the
@ -121,6 +126,7 @@ export default ['jobData', 'jobDataOptions', 'jobLabels', 'count', '$scope', 'Pa
if (change === 'finishedTime' && !$scope.job.finished) {
$scope.job.finished = mungedEvent.finishedTime;
$scope.jobFinished = true;
}
if (change === 'countFinished') {
@ -136,6 +142,10 @@ export default ['jobData', 'jobDataOptions', 'jobLabels', 'count', '$scope', 'Pa
.element(".JobResultsStdOut-stdoutContainer")
.append($compile(mungedEvent
.stdout)($scope));
if ($scope.followEngaged) {
$scope.followScroll();
}
}
});
}

View File

@ -44,6 +44,14 @@ export default {
});
return val.promise;
}],
// used to signify if job is completed or still running
jobFinished: ['jobData', function(jobData) {
if (jobData.finished) {
return true;
} else {
return false;
}
}],
// after the GET for the job, this helps us keep the status bar from
// flashing as rest data comes in. If the job is finished and
// there's a playbook_on_stats event, go ahead and resolve the count

View File

@ -31,15 +31,23 @@ export default [function(){
line = line.replace(/\[0m/g, '</span>');
return line;
},
getCollapseClasses: function(event) {
getCollapseClasses: function(event, line, lineNum) {
var string = "";
if (event.event_name === "playbook_on_play_start") {
string += " header_play";
string += " header_play_" + event.event_data.play_uuid;
if (line) {
string += " actual_header";
}
} else if (event.event_name === "playbook_on_task_start") {
string += " header_task";
string += " header_task_" + event.event_data.task_uuid;
if (event.event_data.play_uuid) {
string += " play_" + event.event_data.play_uuid;
}
if (line) {
string += " actual_header";
}
} else {
if (event.event_data.play_uuid) {
string += " play_" + event.event_data.play_uuid;
@ -48,7 +56,7 @@ export default [function(){
string += " task_" + event.event_data.task_uuid;
}
}
string += " line_num_" + lineNum;
return string;
},
getCollapseIcon: function(event, line) {
@ -94,7 +102,7 @@ export default [function(){
event.stdout.split("\r\n").slice(0, -1))
.map(lineArr => {
return `
<div class="JobResultsStdOut-aLineOfStdOut${this.getCollapseClasses(event)}">
<div class="JobResultsStdOut-aLineOfStdOut${this.getCollapseClasses(event, lineArr[1], lineArr[0])}">
<div class="JobResultsStdOut-lineNumberColumn">${this.getCollapseIcon(event, lineArr[1])}${lineArr[0]}</div>
<div class="JobResultsStdOut-stdoutColumn">${this.prettify(lineArr[1])}</div>
</div>`;