job results cleanup 2 of 2

Clean up of standard out directive code
This commit is contained in:
John Mitchell 2016-11-16 13:07:23 -05:00 committed by jaredevantabor
parent 96952e7841
commit 93f5b19685
2 changed files with 218 additions and 55 deletions

View File

@ -13,60 +13,139 @@ export default [ 'templateUrl', '$timeout', '$location', '$anchorScroll',
restrict: 'E',
link: function(scope, element) {
// utility function used to find the top visible line and
// parent header in the pane
//
// note that while this function is called when in mobile width
// the line anchor is not displayed in the css so calls
// to lineAnchor do nothing
var findTopLines = function() {
scope.visibleItems = "";
var $container = $('.JobResultsStdOut-stdoutContainer');
// get the first visible line's head element
var getHeadElement = function (line) {
var lineHasHeaderClass = !!(line
.hasClass("header_play") ||
line.hasClass("header_task"));
var lineClassList;
var lineUUIDClass;
if (lineHasHeaderClass) {
// find head element when the first visible
// line is a header
lineClassList = line.attr("class")
.split(" ");
// get the header class w task uuid...
lineUUIDClass = lineClassList
.filter(n => n
.indexOf("header_task_") > -1)[0];
// ...if that doesn't exist get the one
// w play uuid
if (!lineUUIDClass) {
lineUUIDClass = lineClassList
.filter(n => n.
indexOf("header_play_") > -1)[0];
}
// get the header line (not their might
// be more than one, so get the one with
// the actual header class)
//
// TODO it might be better in this case to just
// return `line` (less jumping with a cowsay
// case)
return $(".actual_header." +
lineUUIDClass);
} else {
// find head element when the first visible
// line is not a header
lineClassList = line.attr("class")
.split(" ");
// get the class w task uuid...
lineUUIDClass = lineClassList
.filter(n => n
.indexOf("task_") > -1)[0];
// ...if that doesn't exist get the one
// w play uuid
if (!lineUUIDClass) {
lineUUIDClass = lineClassList
.filter(n => n
.indexOf("play_") > -1)[0];
}
// get the header line (not their might
// be more than one, so get the one with
// the actual header class)
return $(".actual_header.header_" +
lineUUIDClass);
}
};
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];
}
var containerHeight = $container.height();
var containerTop = $container.position().top;
var containerNetHeight = containerHeight + containerTop;
$head = $(".actual_header.header_" + header);
}
parentItem = parseInt($($head.children()[0]).text());
return false;
// iterate through each line of standard out
$container.find('.JobResultsStdOut-aLineOfStdOut')
.each( function () {
var $this = $(this);
var lineHeight = $this.height();
var lineTop = $this.position().top;
var lineNetHeight = lineHeight + lineTop;
// check to see if the line is the first visible
// line in the viewport...
if (lineNetHeight > containerTop &&
lineTop < containerNetHeight) {
// ...if it is, return the line number
// for this line
visItem = parseInt($($this
.children()[0])
.text());
// as well as the line number for it's
// closest parent header line
var $head = getHeadElement($this);
parentItem = parseInt($($head
.children()[0])
.text());
// stop iterating over the standard out
// lines once the first one has been
// found
return false;
}
});
scope.visLine = visItem;
scope.parentVisLine = parentItem;
return {
visLine: visItem,
parentVisLine: parentItem
};
};
// find when window changes from mobile to desktop width
if (window.innerWidth < 1200) {
// find if window is initially mobile or desktop width
if (window.innerWidth <= 1200) {
scope.isMobile = true;
} else {
scope.isMobile = false;
}
$( window ).resize(function() {
if (window.innerWidth < 1200 && !scope.isMobile) {
// watch changes to the window size
$(window).resize(function() {
// and update the isMobile var accordingly
if (window.innerWidth <= 1200 && !scope.isMobile) {
scope.isMobile = true;
} else if (window.innerWidth >= 1200 & scope.isMobile) {
} else if (window.innerWidth > 1200 & scope.isMobile) {
scope.isMobile = false;
}
});
@ -75,55 +154,71 @@ export default [ 'templateUrl', '$timeout', '$location', '$anchorScroll',
var initScrollTop = function() {
lastScrollTop = 0;
}
};
var scrollWatcher = function() {
var st = $(this).scrollTop(),
fullHeight;
var st = $(this).scrollTop();
var netScroll = st + $(this).innerHeight();
var fullHeight;
if (st < lastScrollTop){
// user up scrolled, so disengage follow
scope.followEngaged = false;
}
if (scope.isMobile) {
// for mobile the height is the body of the entire
// page
fullHeight = $("body").height();
} else {
// for desktop the height is the body of the
// stdoutContainer, minus the "^ TOP" indicator
fullHeight = $(this)[0].scrollHeight - 25;
}
if($(this).scrollTop() + $(this).innerHeight() >=
fullHeight) {
if(netScroll >= fullHeight) {
// user scrolled all the way to bottom, so engage
// follow
scope.followEngaged = true;
}
// pane is now overflowed, show top indicator
if (st > 0 && !scope.isMobile) {
// pane is now overflowed, show top indicator.
if (st > 0) {
scope.stdoutOverflowed = true;
}
lastScrollTop = st;
}
};
// update scroll watchers when isMobile changes based on
// window resize
scope.$watch('isMobile', function(val) {
if (val === true) {
// make sure ^ TOP always shown
// make sure ^ TOP always shown for mobile
scope.stdoutOverflowed = true;
initScrollTop();
// unbind scroll watcher on standard out container
$(".JobResultsStdOut-stdoutContainer")
.unbind('scroll');
// init scroll watcher on window
initScrollTop();
$(window).on('scroll', scrollWatcher);
} else if (val === false) {
initScrollTop();
// unbind scroll watcher on window
$(window).unbind('scroll');
// init scroll watcher on standard out container
initScrollTop();
$(".JobResultsStdOut-stdoutContainer").on('scroll',
scrollWatcher);
}
});
// called to scroll to follow anchor
scope.followScroll = function() {
// a double-check to make sure the follow anchor is at
// the bottom of the standard out container
$(".JobResultsStdOut-followAnchor")
.appendTo(".JobResultsStdOut-stdoutContainer");
@ -131,11 +226,19 @@ export default [ 'templateUrl', '$timeout', '$location', '$anchorScroll',
$anchorScroll();
};
// called to scroll to top of standard out (when "^ TOP" is
// clicked)
scope.toTop = function() {
$location.hash('topAnchor');
$anchorScroll();
};
// called to scroll to the current line anchor
// when expand all/collapse all/filtering is engaged
//
// note that while this function can be called when in mobile
// width the line anchor is not displayed in the css so those
// calls do nothing
scope.lineAnchor = function() {
$location.hash('lineAnchor');
$anchorScroll();
@ -144,10 +247,12 @@ export default [ 'templateUrl', '$timeout', '$location', '$anchorScroll',
// if following becomes active, go ahead and get to the bottom
// of the standard out pane
scope.$watch('followEngaged', function(val) {
// scroll to follow point if followEngaged is true
if (val) {
scope.followScroll();
}
// set up tooltip changes for not finsihed job
if (!scope.jobFinished) {
if (val) {
scope.followTooltip = "Currently following standard out as it comes in. Click to unfollow.";
@ -157,73 +262,121 @@ export default [ 'templateUrl', '$timeout', '$location', '$anchorScroll',
}
});
// follow button ng-click function
scope.followToggleClicked = function() {
if (scope.jobFinished) {
// when the job is finished engage followScroll
scope.followScroll();
} else {
// when the job is not finished toggle followEngaged
// which is watched above
scope.followEngaged = !scope.followEngaged;
}
};
// expand all/collapse all ng-click function
scope.toggleAllStdout = function(type) {
findTopLines();
// find the top visible line in the container currently,
// as well as the header parent of that line
var topLines = findTopLines();
if (type === 'expand') {
$(".line_num_" + scope.visLine)
// for expand prepend the lineAnchor to the visible
// line
$(".line_num_" + topLines.visLine)
.prepend($("#lineAnchor"));
} else {
$(".line_num_" + scope.parentVisLine)
// for collapse prepent the lineAnchor to the
// visible line's parent header
$(".line_num_" + topLines.parentVisLine)
.prepend($("#lineAnchor"));
}
var expandClass;
if (type === 'expand') {
// for expand all, you'll need to find all the
// collapsed headers to expand them
expandClass = "fa-caret-right";
} else {
// for collapse all, you'll need to find all the
// expanded headers to collapse them
expandClass = "fa-caret-down";
}
// find all applicable task headers that need to be
// toggled
element.find(".expanderizer--task."+expandClass)
.each((i, val) => {
// and trigger their expansion/collapsing
$timeout(function(){
// TODO change to a direct call of the
// toggleLine function
angular.element(val).trigger('click');
// TODO only call lineAnchor for those
// that are above the first visible line
scope.lineAnchor();
});
});
// find all applicable play headers that need to be
// toggled
element.find(".expanderizer--play."+expandClass)
.each((i, val) => {
// for collapse all, only collapse play
// headers that do not have children task
// headers
if(angular.element("." +
angular.element(val).attr("data-uuid"))
.find(".expanderizer--task")
.length === 0 ||
type !== 'collapse') {
// trigger their expansion/
// collapsing
$timeout(function(){
// TODO change to a direct
// call of the
// toggleLine function
angular.element(val)
.trigger('click');
// TODO only call lineAnchor
// for those that are above
// the first visible line
scope.lineAnchor();
});
}
});
};
// expand/collapse triangle ng-click function
scope.toggleLine = function($event, id) {
// if the section is currently expanded
if ($($event.currentTarget).hasClass("fa-caret-down")) {
// hide all the children lines
$(id).hide();
// and change the triangle for the header to collapse
$($event.currentTarget)
.removeClass("fa-caret-down");
$($event.currentTarget)
.addClass("fa-caret-right");
} else {
// if the section is currently collapsed
// show all the children lines
$(id).show();
// and change the triangle for the header to expanded
$($event.currentTarget)
.removeClass("fa-caret-right");
$($event.currentTarget)
.addClass("fa-caret-down");
// if the section you expanded is a play
if ($($event.currentTarget)
.hasClass("expanderizer--play")) {
// find all children task headers and
// expand them too
$("." + $($event.currentTarget)
.attr("data-uuid"))
.find(".expanderizer--task")
@ -231,6 +384,9 @@ export default [ 'templateUrl', '$timeout', '$location', '$anchorScroll',
if ($(val)
.hasClass("fa-caret-right")) {
$timeout(function(){
// TODO change to a
// direct call of the
// toggleLine function
angular.element(val)
.trigger('click');
});

View File

@ -82,14 +82,21 @@ export default ['$log', function($log){
// play header classes
string += " header_play";
string += " header_play_" + event.event_data.play_uuid;
if (line) {
// give the actual header class to the line with the
// actual header info (think cowsay)
if (line.indexOf("PLAY") > -1) {
string += " actual_header";
}
} else if (event.event_name === "playbook_on_task_start") {
// task header classes
string += " header_task";
string += " header_task_" + event.event_data.task_uuid;
if (line) {
// give the actual header class to the line with the
// actual header info (think cowsay)
if (line.indexOf("TASK") > -1 ||
line.indexOf("RUNNING HANDLER") > -1) {
string += " actual_header";
}