BigW Consortium Gitlab

build.js 8.52 KB
Newer Older
1 2
/* eslint-disable func-names, wrap-iife, no-use-before-define,
consistent-return, prefer-rest-params */
Filipa Lacerda committed
3
import _ from 'underscore';
4
import bp from './breakpoints';
5
import { bytesToKiB } from './lib/utils/number_utils';
6
import { setCiStatusFavicon } from './lib/utils/common_utils';
7

8
window.Build = (function () {
9 10 11 12
  Build.timeout = null;
  Build.state = null;

  function Build(options) {
13 14 15 16 17 18
    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;
19
    this.$document = $(document);
20
    this.logBytes = 0;
21
    this.hasBeenScrolled = false;
22

Filipa Lacerda committed
23 24
    this.updateDropdown = this.updateDropdown.bind(this);
    this.getBuildTrace = this.getBuildTrace.bind(this);
25

26 27
    this.$buildTrace = $('#build-trace');
    this.$buildRefreshAnimation = $('.js-build-refresh');
28
    this.$truncatedInfo = $('.js-truncated-info');
Filipa Lacerda committed
29
    this.$buildTraceOutput = $('.js-build-output');
30
    this.$topBar = $('.js-top-bar');
Filipa Lacerda committed
31 32 33 34

    // Scroll controllers
    this.$scrollTopBtn = $('.js-scroll-up');
    this.$scrollBottomBtn = $('.js-scroll-down');
35 36 37 38 39 40 41 42

    clearTimeout(Build.timeout);

    this.initSidebar();
    this.populateJobs(this.buildStage);
    this.updateStageDropdownText(this.buildStage);
    this.sidebarOnResize();

43 44 45 46 47 48 49 50
    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);

Filipa Lacerda committed
51 52 53 54 55 56 57 58
    // 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));
59

60
    this.scrollThrottled = _.throttle(this.toggleScroll.bind(this), 100);
61

62
    $(window)
63 64
      .off('scroll')
      .on('scroll', () => {
65
        const contentHeight = this.$buildTraceOutput.height();
66 67 68 69 70 71 72 73 74 75
        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);
        }

        this.scrollThrottled();
76 77
      });

78 79
    $(window)
      .off('resize.build')
80
      .on('resize.build', _.throttle(this.sidebarOnResize.bind(this), 100));
81

82
    this.updateArtifactRemoveDate();
83
    this.initAffixTopArea();
Filipa Lacerda committed
84

85
    this.getBuildTrace();
86 87
  }

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
  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,
      },
    });
  };

Filipa Lacerda committed
105
  Build.prototype.canScroll = function () {
106
    return $(document).height() > $(window).height();
Filipa Lacerda committed
107 108 109
  };

  Build.prototype.toggleScroll = function () {
110 111
    const currentPosition = $(document).scrollTop();
    const scrollHeight = $(document).height();
Filipa Lacerda committed
112

113
    const windowHeight = $(window).height();
Filipa Lacerda committed
114
    if (this.canScroll()) {
115
      if (currentPosition > 0 &&
116
        (scrollHeight - currentPosition !== windowHeight)) {
117 118 119 120 121 122 123
      // 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

Filipa Lacerda committed
124 125
        this.toggleDisableButton(this.$scrollTopBtn, true);
        this.toggleDisableButton(this.$scrollBottomBtn, false);
126
      } else if (scrollHeight - currentPosition === windowHeight) {
127 128
        // User is at the bottom of the build log.

Filipa Lacerda committed
129 130 131
        this.toggleDisableButton(this.$scrollTopBtn, false);
        this.toggleDisableButton(this.$scrollBottomBtn, true);
      }
132 133 134
    } else {
      this.toggleDisableButton(this.$scrollTopBtn, true);
      this.toggleDisableButton(this.$scrollBottomBtn, true);
Filipa Lacerda committed
135 136 137
    }
  };

138
  Build.prototype.scrollDown = function () {
139
    $(document).scrollTop($(document).height());
140 141 142 143
  };

  Build.prototype.scrollToBottom = function () {
    this.scrollDown();
144
    this.hasBeenScrolled = true;
Filipa Lacerda committed
145 146 147
    this.toggleScroll();
  };

148
  Build.prototype.scrollToTop = function () {
149
    $(document).scrollTop(0);
150
    this.hasBeenScrolled = true;
Filipa Lacerda committed
151 152 153 154 155 156 157 158 159 160 161 162
    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);
  };

163
  Build.prototype.initSidebar = function () {
164 165 166
    this.$sidebar = $('.js-build-sidebar');
  };

167
  Build.prototype.getBuildTrace = function () {
168
    return $.ajax({
169
      url: `${this.pageUrl}/trace.json`,
Filipa Lacerda committed
170 171 172
      data: this.state,
    })
      .done((log) => {
173
        setCiStatusFavicon(`${this.pageUrl}/status.json`);
174

175 176
        if (log.state) {
          this.state = log.state;
177
        }
178

179
        this.windowSize = this.$buildTraceOutput.height();
180

181
        if (log.append) {
Filipa Lacerda committed
182
          this.$buildTraceOutput.append(log.html);
183
          this.logBytes += log.size;
184
        } else {
Filipa Lacerda committed
185
          this.$buildTraceOutput.html(log.html);
186 187 188 189 190 191 192 193 194 195 196 197
          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');
198 199 200
        }

        if (!log.complete) {
201 202 203 204 205
          if (!this.hasBeenScrolled) {
            this.toggleScrollAnimation(true);
          } else {
            this.toggleScrollAnimation(false);
          }
Filipa Lacerda committed
206

207
          Build.timeout = setTimeout(() => {
208
            this.getBuildTrace();
209 210
          }, 4000);
        } else {
211
          this.$buildRefreshAnimation.remove();
Filipa Lacerda committed
212
          this.toggleScrollAnimation(false);
213 214
        }

215
        if (log.status !== this.buildStatus) {
Filipa Lacerda committed
216
          gl.utils.visitUrl(this.pageUrl);
217
        }
Filipa Lacerda committed
218 219
      })
      .fail(() => {
220
        this.$buildRefreshAnimation.remove();
221 222 223 224 225 226 227
      })
      .then(() => {
        if (!this.hasBeenScrolled) {
          this.scrollDown();
        }
      })
      .then(() => this.toggleScroll());
228 229
  };

230
  Build.prototype.shouldHideSidebarForViewport = function () {
231
    const bootstrapBreakpoint = bp.getBreakpointSize();
232 233 234
    return bootstrapBreakpoint === 'xs' || bootstrapBreakpoint === 'sm';
  };

235
  Build.prototype.toggleSidebar = function (shouldHide) {
236
    const shouldShow = typeof shouldHide === 'boolean' ? !shouldHide : undefined;
237
    const $toggleButton = $('.js-sidebar-build-toggle-header');
238

Filipa Lacerda committed
239 240
    this.$sidebar
      .toggleClass('right-sidebar-expanded', shouldShow)
241
      .toggleClass('right-sidebar-collapsed', shouldHide);
242

243
    this.$topBar
244 245 246 247 248 249 250 251
      .toggleClass('sidebar-expanded', shouldShow)
      .toggleClass('sidebar-collapsed', shouldHide);

    if (this.$sidebar.hasClass('right-sidebar-expanded')) {
      $toggleButton.addClass('hidden');
    } else {
      $toggleButton.removeClass('hidden');
    }
252 253
  };

254
  Build.prototype.sidebarOnResize = function () {
255 256 257
    this.toggleSidebar(this.shouldHideSidebarForViewport());
  };

258
  Build.prototype.sidebarOnClick = function () {
259 260 261
    if (this.shouldHideSidebarForViewport()) this.toggleSidebar();
  };

262 263
  Build.prototype.updateArtifactRemoveDate = function () {
    const $date = $('.js-artifacts-remove');
264
    if ($date.length) {
265 266 267 268
      const date = $date.text();
      return $date.text(
        gl.utils.timeFor(new Date(date.replace(/([0-9]+)-([0-9]+)-([0-9]+)/g, '$1/$2/$3')), ' '),
      );
269 270 271
    }
  };

272
  Build.prototype.populateJobs = function (stage) {
273
    $('.build-job').hide();
274
    $(`.build-job[data-stage="${stage}"]`).show();
275 276
  };

277
  Build.prototype.updateStageDropdownText = function (stage) {
278 279 280
    $('.stage-selection').text(stage);
  };

281
  Build.prototype.updateDropdown = function (e) {
282
    e.preventDefault();
283
    const stage = e.currentTarget.text;
284 285 286 287 288 289
    this.updateStageDropdownText(stage);
    this.populateJobs(stage);
  };

  return Build;
})();