2017-08-17 22:00:37 +05:30
|
|
|
/* eslint-disable func-names, wrap-iife, no-use-before-define,
|
|
|
|
consistent-return, prefer-rest-params */
|
2017-09-10 17:25:29 +05:30
|
|
|
import _ from 'underscore';
|
|
|
|
import bp from './breakpoints';
|
2017-08-17 22:00:37 +05:30
|
|
|
import { bytesToKiB } from './lib/utils/number_utils';
|
|
|
|
|
|
|
|
window.Build = (function () {
|
|
|
|
Build.timeout = null;
|
|
|
|
Build.state = null;
|
|
|
|
|
|
|
|
function Build(options) {
|
|
|
|
this.options = options || $('.js-build-options').data();
|
|
|
|
|
|
|
|
this.pageUrl = this.options.pageUrl;
|
|
|
|
this.buildStatus = this.options.buildStatus;
|
|
|
|
this.state = this.options.logState;
|
|
|
|
this.buildStage = this.options.buildStage;
|
|
|
|
this.$document = $(document);
|
|
|
|
this.logBytes = 0;
|
2017-09-10 17:25:29 +05:30
|
|
|
this.hasBeenScrolled = false;
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2017-09-10 17:25:29 +05:30
|
|
|
this.updateDropdown = this.updateDropdown.bind(this);
|
|
|
|
this.getBuildTrace = this.getBuildTrace.bind(this);
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
|
|
this.$buildTrace = $('#build-trace');
|
|
|
|
this.$buildRefreshAnimation = $('.js-build-refresh');
|
|
|
|
this.$truncatedInfo = $('.js-truncated-info');
|
2017-09-10 17:25:29 +05:30
|
|
|
this.$buildTraceOutput = $('.js-build-output');
|
|
|
|
this.$topBar = $('.js-top-bar');
|
|
|
|
|
|
|
|
// Scroll controllers
|
|
|
|
this.$scrollTopBtn = $('.js-scroll-up');
|
|
|
|
this.$scrollBottomBtn = $('.js-scroll-down');
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
|
|
clearTimeout(Build.timeout);
|
|
|
|
|
|
|
|
this.initSidebar();
|
|
|
|
this.populateJobs(this.buildStage);
|
|
|
|
this.updateStageDropdownText(this.buildStage);
|
|
|
|
this.sidebarOnResize();
|
|
|
|
|
|
|
|
this.$document
|
|
|
|
.off('click', '.js-sidebar-build-toggle')
|
|
|
|
.on('click', '.js-sidebar-build-toggle', this.sidebarOnClick.bind(this));
|
|
|
|
|
|
|
|
this.$document
|
|
|
|
.off('click', '.stage-item')
|
|
|
|
.on('click', '.stage-item', this.updateDropdown);
|
|
|
|
|
2017-09-10 17:25:29 +05:30
|
|
|
// add event listeners to the scroll buttons
|
|
|
|
this.$scrollTopBtn
|
|
|
|
.off('click')
|
|
|
|
.on('click', this.scrollToTop.bind(this));
|
|
|
|
|
|
|
|
this.$scrollBottomBtn
|
|
|
|
.off('click')
|
|
|
|
.on('click', this.scrollToBottom.bind(this));
|
|
|
|
|
|
|
|
this.scrollThrottled = _.throttle(this.toggleScroll.bind(this), 100);
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
|
|
$(window)
|
2017-09-10 17:25:29 +05:30
|
|
|
.off('scroll')
|
|
|
|
.on('scroll', () => {
|
|
|
|
const contentHeight = this.$buildTraceOutput.height();
|
|
|
|
if (contentHeight > this.windowSize) {
|
|
|
|
// means the user did not scroll, the content was updated.
|
|
|
|
this.windowSize = contentHeight;
|
|
|
|
} else {
|
|
|
|
// User scrolled
|
|
|
|
this.hasBeenScrolled = true;
|
|
|
|
this.toggleScrollAnimation(false);
|
|
|
|
}
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2017-09-10 17:25:29 +05:30
|
|
|
this.scrollThrottled();
|
|
|
|
});
|
|
|
|
|
|
|
|
$(window)
|
|
|
|
.off('resize.build')
|
|
|
|
.on('resize.build', _.throttle(this.sidebarOnResize.bind(this), 100));
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
|
|
this.updateArtifactRemoveDate();
|
2017-09-10 17:25:29 +05:30
|
|
|
this.initAffixTopArea();
|
|
|
|
|
|
|
|
this.getBuildTrace();
|
2017-08-17 22:00:37 +05:30
|
|
|
}
|
|
|
|
|
2017-09-10 17:25:29 +05:30
|
|
|
Build.prototype.initAffixTopArea = function () {
|
|
|
|
/**
|
|
|
|
If the browser does not support position sticky, it returns the position as static.
|
|
|
|
If the browser does support sticky, then we allow the browser to handle it, if not
|
|
|
|
then we default back to Bootstraps affix
|
|
|
|
**/
|
|
|
|
if (this.$topBar.css('position') !== 'static') return;
|
|
|
|
|
|
|
|
const offsetTop = this.$buildTrace.offset().top;
|
|
|
|
|
|
|
|
this.$topBar.affix({
|
|
|
|
offset: {
|
|
|
|
top: offsetTop,
|
|
|
|
},
|
|
|
|
});
|
2017-08-17 22:00:37 +05:30
|
|
|
};
|
|
|
|
|
2017-09-10 17:25:29 +05:30
|
|
|
Build.prototype.canScroll = function () {
|
|
|
|
return $(document).height() > $(window).height();
|
|
|
|
};
|
|
|
|
|
|
|
|
Build.prototype.toggleScroll = function () {
|
|
|
|
const currentPosition = $(document).scrollTop();
|
|
|
|
const scrollHeight = $(document).height();
|
|
|
|
|
|
|
|
const windowHeight = $(window).height();
|
|
|
|
if (this.canScroll()) {
|
|
|
|
if (currentPosition > 0 &&
|
|
|
|
(scrollHeight - currentPosition !== windowHeight)) {
|
|
|
|
// User is in the middle of the log
|
|
|
|
|
|
|
|
this.toggleDisableButton(this.$scrollTopBtn, false);
|
|
|
|
this.toggleDisableButton(this.$scrollBottomBtn, false);
|
|
|
|
} else if (currentPosition === 0) {
|
|
|
|
// User is at Top of Build Log
|
|
|
|
|
|
|
|
this.toggleDisableButton(this.$scrollTopBtn, true);
|
|
|
|
this.toggleDisableButton(this.$scrollBottomBtn, false);
|
|
|
|
} else if (scrollHeight - currentPosition === windowHeight) {
|
|
|
|
// User is at the bottom of the build log.
|
|
|
|
|
|
|
|
this.toggleDisableButton(this.$scrollTopBtn, false);
|
|
|
|
this.toggleDisableButton(this.$scrollBottomBtn, true);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.toggleDisableButton(this.$scrollTopBtn, true);
|
|
|
|
this.toggleDisableButton(this.$scrollBottomBtn, true);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Build.prototype.scrollDown = function () {
|
|
|
|
$(document).scrollTop($(document).height());
|
|
|
|
};
|
|
|
|
|
|
|
|
Build.prototype.scrollToBottom = function () {
|
|
|
|
this.scrollDown();
|
|
|
|
this.hasBeenScrolled = true;
|
|
|
|
this.toggleScroll();
|
|
|
|
};
|
|
|
|
|
|
|
|
Build.prototype.scrollToTop = function () {
|
|
|
|
$(document).scrollTop(0);
|
|
|
|
this.hasBeenScrolled = true;
|
|
|
|
this.toggleScroll();
|
|
|
|
};
|
|
|
|
|
|
|
|
Build.prototype.toggleDisableButton = function ($button, disable) {
|
|
|
|
if (disable && $button.prop('disabled')) return;
|
|
|
|
$button.prop('disabled', disable);
|
|
|
|
};
|
|
|
|
|
|
|
|
Build.prototype.toggleScrollAnimation = function (toggle) {
|
|
|
|
this.$scrollBottomBtn.toggleClass('animate', toggle);
|
|
|
|
};
|
|
|
|
|
|
|
|
Build.prototype.initSidebar = function () {
|
|
|
|
this.$sidebar = $('.js-build-sidebar');
|
2017-08-17 22:00:37 +05:30
|
|
|
};
|
|
|
|
|
|
|
|
Build.prototype.getBuildTrace = function () {
|
|
|
|
return $.ajax({
|
|
|
|
url: `${this.pageUrl}/trace.json`,
|
2017-09-10 17:25:29 +05:30
|
|
|
data: this.state,
|
|
|
|
})
|
|
|
|
.done((log) => {
|
2017-08-17 22:00:37 +05:30
|
|
|
gl.utils.setCiStatusFavicon(`${this.pageUrl}/status.json`);
|
|
|
|
|
|
|
|
if (log.state) {
|
|
|
|
this.state = log.state;
|
2016-09-13 17:45:13 +05:30
|
|
|
}
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2017-09-10 17:25:29 +05:30
|
|
|
this.windowSize = this.$buildTraceOutput.height();
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
if (log.append) {
|
2017-09-10 17:25:29 +05:30
|
|
|
this.$buildTraceOutput.append(log.html);
|
2017-08-17 22:00:37 +05:30
|
|
|
this.logBytes += log.size;
|
|
|
|
} else {
|
2017-09-10 17:25:29 +05:30
|
|
|
this.$buildTraceOutput.html(log.html);
|
2017-08-17 22:00:37 +05:30
|
|
|
this.logBytes = log.size;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if the incremental sum of logBytes we received is less than the total
|
|
|
|
// we need to show a message warning the user about that.
|
|
|
|
if (this.logBytes < log.total) {
|
|
|
|
// size is in bytes, we need to calculate KiB
|
|
|
|
const size = bytesToKiB(this.logBytes);
|
|
|
|
$('.js-truncated-info-size').html(`${size}`);
|
|
|
|
this.$truncatedInfo.removeClass('hidden');
|
|
|
|
} else {
|
|
|
|
this.$truncatedInfo.addClass('hidden');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!log.complete) {
|
2017-09-10 17:25:29 +05:30
|
|
|
if (!this.hasBeenScrolled) {
|
|
|
|
this.toggleScrollAnimation(true);
|
|
|
|
} else {
|
|
|
|
this.toggleScrollAnimation(false);
|
|
|
|
}
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
Build.timeout = setTimeout(() => {
|
2017-09-10 17:25:29 +05:30
|
|
|
this.getBuildTrace();
|
2017-08-17 22:00:37 +05:30
|
|
|
}, 4000);
|
|
|
|
} else {
|
|
|
|
this.$buildRefreshAnimation.remove();
|
2017-09-10 17:25:29 +05:30
|
|
|
this.toggleScrollAnimation(false);
|
2017-08-17 22:00:37 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
if (log.status !== this.buildStatus) {
|
2017-09-10 17:25:29 +05:30
|
|
|
gl.utils.visitUrl(this.pageUrl);
|
2016-09-13 17:45:13 +05:30
|
|
|
}
|
2017-09-10 17:25:29 +05:30
|
|
|
})
|
|
|
|
.fail(() => {
|
2017-08-17 22:00:37 +05:30
|
|
|
this.$buildRefreshAnimation.remove();
|
2017-09-10 17:25:29 +05:30
|
|
|
})
|
|
|
|
.then(() => {
|
|
|
|
if (!this.hasBeenScrolled) {
|
|
|
|
this.scrollDown();
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.then(() => this.toggleScroll());
|
2017-08-17 22:00:37 +05:30
|
|
|
};
|
|
|
|
|
|
|
|
Build.prototype.shouldHideSidebarForViewport = function () {
|
2017-09-10 17:25:29 +05:30
|
|
|
const bootstrapBreakpoint = bp.getBreakpointSize();
|
2017-08-17 22:00:37 +05:30
|
|
|
return bootstrapBreakpoint === 'xs' || bootstrapBreakpoint === 'sm';
|
|
|
|
};
|
|
|
|
|
|
|
|
Build.prototype.toggleSidebar = function (shouldHide) {
|
|
|
|
const shouldShow = typeof shouldHide === 'boolean' ? !shouldHide : undefined;
|
2017-09-10 17:25:29 +05:30
|
|
|
const $toggleButton = $('.js-sidebar-build-toggle-header');
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2017-09-10 17:25:29 +05:30
|
|
|
this.$sidebar
|
|
|
|
.toggleClass('right-sidebar-expanded', shouldShow)
|
2017-08-17 22:00:37 +05:30
|
|
|
.toggleClass('right-sidebar-collapsed', shouldHide);
|
2017-09-10 17:25:29 +05:30
|
|
|
|
|
|
|
this.$topBar
|
|
|
|
.toggleClass('sidebar-expanded', shouldShow)
|
|
|
|
.toggleClass('sidebar-collapsed', shouldHide);
|
|
|
|
|
|
|
|
if (this.$sidebar.hasClass('right-sidebar-expanded')) {
|
|
|
|
$toggleButton.addClass('hidden');
|
|
|
|
} else {
|
|
|
|
$toggleButton.removeClass('hidden');
|
|
|
|
}
|
2017-08-17 22:00:37 +05:30
|
|
|
};
|
|
|
|
|
|
|
|
Build.prototype.sidebarOnResize = function () {
|
|
|
|
this.toggleSidebar(this.shouldHideSidebarForViewport());
|
|
|
|
};
|
|
|
|
|
|
|
|
Build.prototype.sidebarOnClick = function () {
|
|
|
|
if (this.shouldHideSidebarForViewport()) this.toggleSidebar();
|
|
|
|
};
|
|
|
|
|
|
|
|
Build.prototype.updateArtifactRemoveDate = function () {
|
|
|
|
const $date = $('.js-artifacts-remove');
|
|
|
|
if ($date.length) {
|
|
|
|
const date = $date.text();
|
|
|
|
return $date.text(
|
|
|
|
gl.utils.timeFor(new Date(date.replace(/([0-9]+)-([0-9]+)-([0-9]+)/g, '$1/$2/$3')), ' '),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Build.prototype.populateJobs = function (stage) {
|
|
|
|
$('.build-job').hide();
|
|
|
|
$(`.build-job[data-stage="${stage}"]`).show();
|
|
|
|
};
|
2016-09-13 17:45:13 +05:30
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
Build.prototype.updateStageDropdownText = function (stage) {
|
|
|
|
$('.stage-selection').text(stage);
|
|
|
|
};
|
2016-09-13 17:45:13 +05:30
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
Build.prototype.updateDropdown = function (e) {
|
|
|
|
e.preventDefault();
|
|
|
|
const stage = e.currentTarget.text;
|
|
|
|
this.updateStageDropdownText(stage);
|
|
|
|
this.populateJobs(stage);
|
|
|
|
};
|
2016-09-13 17:45:13 +05:30
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
return Build;
|
|
|
|
})();
|