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', restrict: 'E',
link: function(scope, element) { 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() { var findTopLines = function() {
scope.visibleItems = "";
var $container = $('.JobResultsStdOut-stdoutContainer'); 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, var visItem,
parentItem; 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, var containerHeight = $container.height();
classList, var containerTop = $container.position().top;
header; var containerNetHeight = containerHeight + containerTop;
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); // iterate through each line of standard out
} $container.find('.JobResultsStdOut-aLineOfStdOut')
parentItem = parseInt($($head.children()[0]).text()); .each( function () {
return false; 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 // find if window is initially mobile or desktop width
if (window.innerWidth < 1200) { if (window.innerWidth <= 1200) {
scope.isMobile = true; scope.isMobile = true;
} else { } else {
scope.isMobile = false; scope.isMobile = false;
} }
$( window ).resize(function() { // watch changes to the window size
if (window.innerWidth < 1200 && !scope.isMobile) { $(window).resize(function() {
// and update the isMobile var accordingly
if (window.innerWidth <= 1200 && !scope.isMobile) {
scope.isMobile = true; scope.isMobile = true;
} else if (window.innerWidth >= 1200 & scope.isMobile) { } else if (window.innerWidth > 1200 & scope.isMobile) {
scope.isMobile = false; scope.isMobile = false;
} }
}); });
@@ -75,55 +154,71 @@ export default [ 'templateUrl', '$timeout', '$location', '$anchorScroll',
var initScrollTop = function() { var initScrollTop = function() {
lastScrollTop = 0; lastScrollTop = 0;
} };
var scrollWatcher = function() { var scrollWatcher = function() {
var st = $(this).scrollTop(), var st = $(this).scrollTop();
fullHeight; var netScroll = st + $(this).innerHeight();
var fullHeight;
if (st < lastScrollTop){ if (st < lastScrollTop){
// user up scrolled, so disengage follow // user up scrolled, so disengage follow
scope.followEngaged = false; scope.followEngaged = false;
} }
if (scope.isMobile) { if (scope.isMobile) {
// for mobile the height is the body of the entire
// page
fullHeight = $("body").height(); fullHeight = $("body").height();
} else { } else {
// for desktop the height is the body of the
// stdoutContainer, minus the "^ TOP" indicator
fullHeight = $(this)[0].scrollHeight - 25; fullHeight = $(this)[0].scrollHeight - 25;
} }
if($(this).scrollTop() + $(this).innerHeight() >= if(netScroll >= fullHeight) {
fullHeight) {
// user scrolled all the way to bottom, so engage // user scrolled all the way to bottom, so engage
// follow // follow
scope.followEngaged = true; scope.followEngaged = true;
} }
// pane is now overflowed, show top indicator // pane is now overflowed, show top indicator.
if (st > 0 && !scope.isMobile) { if (st > 0) {
scope.stdoutOverflowed = true; scope.stdoutOverflowed = true;
} }
lastScrollTop = st; lastScrollTop = st;
} };
// update scroll watchers when isMobile changes based on
// window resize
scope.$watch('isMobile', function(val) { scope.$watch('isMobile', function(val) {
if (val === true) { if (val === true) {
// make sure ^ TOP always shown // make sure ^ TOP always shown for mobile
scope.stdoutOverflowed = true; scope.stdoutOverflowed = true;
initScrollTop(); // unbind scroll watcher on standard out container
$(".JobResultsStdOut-stdoutContainer") $(".JobResultsStdOut-stdoutContainer")
.unbind('scroll'); .unbind('scroll');
// init scroll watcher on window
initScrollTop();
$(window).on('scroll', scrollWatcher); $(window).on('scroll', scrollWatcher);
} else if (val === false) { } else if (val === false) {
initScrollTop(); // unbind scroll watcher on window
$(window).unbind('scroll'); $(window).unbind('scroll');
// init scroll watcher on standard out container
initScrollTop();
$(".JobResultsStdOut-stdoutContainer").on('scroll', $(".JobResultsStdOut-stdoutContainer").on('scroll',
scrollWatcher); scrollWatcher);
} }
}); });
// called to scroll to follow anchor
scope.followScroll = function() { scope.followScroll = function() {
// a double-check to make sure the follow anchor is at
// the bottom of the standard out container
$(".JobResultsStdOut-followAnchor") $(".JobResultsStdOut-followAnchor")
.appendTo(".JobResultsStdOut-stdoutContainer"); .appendTo(".JobResultsStdOut-stdoutContainer");
@@ -131,11 +226,19 @@ export default [ 'templateUrl', '$timeout', '$location', '$anchorScroll',
$anchorScroll(); $anchorScroll();
}; };
// called to scroll to top of standard out (when "^ TOP" is
// clicked)
scope.toTop = function() { scope.toTop = function() {
$location.hash('topAnchor'); $location.hash('topAnchor');
$anchorScroll(); $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() { scope.lineAnchor = function() {
$location.hash('lineAnchor'); $location.hash('lineAnchor');
$anchorScroll(); $anchorScroll();
@@ -144,10 +247,12 @@ export default [ 'templateUrl', '$timeout', '$location', '$anchorScroll',
// if following becomes active, go ahead and get to the bottom // if following becomes active, go ahead and get to the bottom
// of the standard out pane // of the standard out pane
scope.$watch('followEngaged', function(val) { scope.$watch('followEngaged', function(val) {
// scroll to follow point if followEngaged is true
if (val) { if (val) {
scope.followScroll(); scope.followScroll();
} }
// set up tooltip changes for not finsihed job
if (!scope.jobFinished) { if (!scope.jobFinished) {
if (val) { if (val) {
scope.followTooltip = "Currently following standard out as it comes in. Click to unfollow."; 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() { scope.followToggleClicked = function() {
if (scope.jobFinished) { if (scope.jobFinished) {
// when the job is finished engage followScroll
scope.followScroll(); scope.followScroll();
} else { } else {
// when the job is not finished toggle followEngaged
// which is watched above
scope.followEngaged = !scope.followEngaged; scope.followEngaged = !scope.followEngaged;
} }
}; };
// expand all/collapse all ng-click function
scope.toggleAllStdout = function(type) { 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') { if (type === 'expand') {
$(".line_num_" + scope.visLine) // for expand prepend the lineAnchor to the visible
// line
$(".line_num_" + topLines.visLine)
.prepend($("#lineAnchor")); .prepend($("#lineAnchor"));
} else { } else {
$(".line_num_" + scope.parentVisLine) // for collapse prepent the lineAnchor to the
// visible line's parent header
$(".line_num_" + topLines.parentVisLine)
.prepend($("#lineAnchor")); .prepend($("#lineAnchor"));
} }
var expandClass; var expandClass;
if (type === 'expand') { if (type === 'expand') {
// for expand all, you'll need to find all the
// collapsed headers to expand them
expandClass = "fa-caret-right"; expandClass = "fa-caret-right";
} else { } else {
// for collapse all, you'll need to find all the
// expanded headers to collapse them
expandClass = "fa-caret-down"; expandClass = "fa-caret-down";
} }
// find all applicable task headers that need to be
// toggled
element.find(".expanderizer--task."+expandClass) element.find(".expanderizer--task."+expandClass)
.each((i, val) => { .each((i, val) => {
// and trigger their expansion/collapsing
$timeout(function(){ $timeout(function(){
// TODO change to a direct call of the
// toggleLine function
angular.element(val).trigger('click'); angular.element(val).trigger('click');
// TODO only call lineAnchor for those
// that are above the first visible line
scope.lineAnchor(); scope.lineAnchor();
}); });
}); });
// find all applicable play headers that need to be
// toggled
element.find(".expanderizer--play."+expandClass) element.find(".expanderizer--play."+expandClass)
.each((i, val) => { .each((i, val) => {
// for collapse all, only collapse play
// headers that do not have children task
// headers
if(angular.element("." + if(angular.element("." +
angular.element(val).attr("data-uuid")) angular.element(val).attr("data-uuid"))
.find(".expanderizer--task") .find(".expanderizer--task")
.length === 0 || .length === 0 ||
type !== 'collapse') { type !== 'collapse') {
// trigger their expansion/
// collapsing
$timeout(function(){ $timeout(function(){
// TODO change to a direct
// call of the
// toggleLine function
angular.element(val) angular.element(val)
.trigger('click'); .trigger('click');
// TODO only call lineAnchor
// for those that are above
// the first visible line
scope.lineAnchor(); scope.lineAnchor();
}); });
} }
}); });
}; };
// expand/collapse triangle ng-click function
scope.toggleLine = function($event, id) { scope.toggleLine = function($event, id) {
// if the section is currently expanded
if ($($event.currentTarget).hasClass("fa-caret-down")) { if ($($event.currentTarget).hasClass("fa-caret-down")) {
// hide all the children lines
$(id).hide(); $(id).hide();
// and change the triangle for the header to collapse
$($event.currentTarget) $($event.currentTarget)
.removeClass("fa-caret-down"); .removeClass("fa-caret-down");
$($event.currentTarget) $($event.currentTarget)
.addClass("fa-caret-right"); .addClass("fa-caret-right");
} else { } else {
// if the section is currently collapsed
// show all the children lines
$(id).show(); $(id).show();
// and change the triangle for the header to expanded
$($event.currentTarget) $($event.currentTarget)
.removeClass("fa-caret-right"); .removeClass("fa-caret-right");
$($event.currentTarget) $($event.currentTarget)
.addClass("fa-caret-down"); .addClass("fa-caret-down");
// if the section you expanded is a play
if ($($event.currentTarget) if ($($event.currentTarget)
.hasClass("expanderizer--play")) { .hasClass("expanderizer--play")) {
// find all children task headers and
// expand them too
$("." + $($event.currentTarget) $("." + $($event.currentTarget)
.attr("data-uuid")) .attr("data-uuid"))
.find(".expanderizer--task") .find(".expanderizer--task")
@@ -231,6 +384,9 @@ export default [ 'templateUrl', '$timeout', '$location', '$anchorScroll',
if ($(val) if ($(val)
.hasClass("fa-caret-right")) { .hasClass("fa-caret-right")) {
$timeout(function(){ $timeout(function(){
// TODO change to a
// direct call of the
// toggleLine function
angular.element(val) angular.element(val)
.trigger('click'); .trigger('click');
}); });

View File

@@ -82,14 +82,21 @@ export default ['$log', function($log){
// play header classes // play header classes
string += " header_play"; string += " header_play";
string += " header_play_" + event.event_data.play_uuid; 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"; string += " actual_header";
} }
} else if (event.event_name === "playbook_on_task_start") { } else if (event.event_name === "playbook_on_task_start") {
// task header classes // task header classes
string += " header_task"; string += " header_task";
string += " header_task_" + event.event_data.task_uuid; 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"; string += " actual_header";
} }