BigW Consortium Gitlab

build.js 9.41 KB
Newer Older
1 2
/* eslint-disable func-names, wrap-iife, no-use-before-define,
consistent-return, prefer-rest-params */
3 4
/* global Breakpoints */

Filipa Lacerda committed
5
import _ from 'underscore';
6 7
import { bytesToKiB } from './lib/utils/number_utils';

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

  function Build(options) {
13 14 15 16 17 18 19
    this.options = options || $('.js-build-options').data();

    this.pageUrl = this.options.pageUrl;
    this.buildUrl = this.options.buildUrl;
    this.buildStatus = this.options.buildStatus;
    this.state = this.options.logState;
    this.buildStage = this.options.buildStage;
20
    this.$document = $(document);
21
    this.logBytes = 0;
Filipa Lacerda committed
22
    this.scrollOffsetPadding = 30;
23
    this.hasBeenScrolled = false;
24

Filipa Lacerda committed
25 26 27
    this.updateDropdown = this.updateDropdown.bind(this);
    this.getBuildTrace = this.getBuildTrace.bind(this);
    this.scrollToBottom = this.scrollToBottom.bind(this);
28

29 30 31
    this.$body = $('body');
    this.$buildTrace = $('#build-trace');
    this.$buildRefreshAnimation = $('.js-build-refresh');
32
    this.$truncatedInfo = $('.js-truncated-info');
Filipa Lacerda committed
33 34 35 36 37 38
    this.$buildTraceOutput = $('.js-build-output');
    this.$scrollContainer = $('.js-scroll-container');

    // Scroll controllers
    this.$scrollTopBtn = $('.js-scroll-up');
    this.$scrollBottomBtn = $('.js-scroll-down');
39 40 41 42 43 44 45 46 47 48

    clearTimeout(Build.timeout);
    // Init breakpoint checker
    this.bp = Breakpoints.get();

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

49 50 51 52 53 54 55 56
    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
57 58 59 60 61 62 63 64
    // 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));
65

66 67 68 69 70 71 72 73 74
    const scrollThrottled = _.throttle(this.toggleScroll.bind(this), 100);

    this.$scrollContainer
      .off('scroll')
      .on('scroll', () => {
        this.hasBeenScrolled = true;
        scrollThrottled();
      });

75 76
    $(window)
      .off('resize.build')
77
      .on('resize.build', _.throttle(this.sidebarOnResize.bind(this), 100));
78

79
    this.updateArtifactRemoveDate();
Filipa Lacerda committed
80 81 82

    // eslint-disable-next-line
    this.getBuildTrace()
83 84 85 86 87 88
      .then(() => this.toggleScroll())
      .then(() => {
        if (!this.hasBeenScrolled) {
          this.scrollToBottom();
        }
      });
Filipa Lacerda committed
89 90

    this.verifyTopPosition();
91 92
  }

Filipa Lacerda committed
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
  Build.prototype.canScroll = function () {
    return (this.$scrollContainer.prop('scrollHeight') - this.scrollOffsetPadding) > this.$scrollContainer.height();
  };

  /**
   * |                          | Up       | Down     |
   * |--------------------------|----------|----------|
   * | on scroll bottom         | active   | disabled |
   * | on scroll top            | disabled | active   |
   * | no scroll                | disabled | disabled |
   * | on.('scroll') is on top  | disabled | active   |
   * | on('scroll) is on bottom | active   | disabled |
   *
   */
  Build.prototype.toggleScroll = function () {
108 109
    const currentPosition = this.$scrollContainer.scrollTop();
    const bottomScroll = currentPosition + this.$scrollContainer.innerHeight();
Filipa Lacerda committed
110 111

    if (this.canScroll()) {
112
      if (currentPosition === 0) {
Filipa Lacerda committed
113 114 115 116 117 118 119 120 121 122 123 124 125
        this.toggleDisableButton(this.$scrollTopBtn, true);
        this.toggleDisableButton(this.$scrollBottomBtn, false);
      } else if (bottomScroll === this.$scrollContainer.prop('scrollHeight')) {
        this.toggleDisableButton(this.$scrollTopBtn, false);
        this.toggleDisableButton(this.$scrollBottomBtn, true);
      } else {
        this.toggleDisableButton(this.$scrollTopBtn, false);
        this.toggleDisableButton(this.$scrollBottomBtn, false);
      }
    }
  };

  Build.prototype.scrollToTop = function () {
126 127
    this.hasBeenScrolled = true;
    this.$scrollContainer.scrollTop(0);
Filipa Lacerda committed
128 129 130 131
    this.toggleScroll();
  };

  Build.prototype.scrollToBottom = function () {
132 133
    this.hasBeenScrolled = true;
    this.$scrollContainer.scrollTop(this.$scrollContainer.prop('scrollHeight'));
Filipa Lacerda committed
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
    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 trace top position depends on the space ocupied by the elments rendered before
   */
  Build.prototype.verifyTopPosition = function () {
    const $buildPage = $('.build-page');

152
    const $flashError = $('.alert-wrapper');
Filipa Lacerda committed
153 154 155 156
    const $header = $('.build-header', $buildPage);
    const $runnersStuck = $('.js-build-stuck', $buildPage);
    const $startsEnvironment = $('.js-environment-container', $buildPage);
    const $erased = $('.js-build-erased', $buildPage);
157
    const prependTopDefault = 20;
Filipa Lacerda committed
158

159
    // header + navigation + margin
Filipa Lacerda committed
160 161
    let topPostion = 168;

162
    if ($header.length) {
Filipa Lacerda committed
163 164 165
      topPostion += $header.outerHeight();
    }

166
    if ($runnersStuck.length) {
Filipa Lacerda committed
167 168 169
      topPostion += $runnersStuck.outerHeight();
    }

170 171
    if ($startsEnvironment.length) {
      topPostion += $startsEnvironment.outerHeight() + prependTopDefault;
Filipa Lacerda committed
172 173
    }

174 175 176 177 178 179
    if ($erased.length) {
      topPostion += $erased.outerHeight() + prependTopDefault;
    }

    if ($flashError.length) {
      topPostion += $flashError.outerHeight();
Filipa Lacerda committed
180 181 182 183 184 185 186
    }

    this.$buildTrace.css({
      top: topPostion,
    });
  };

187
  Build.prototype.initSidebar = function () {
188 189 190 191
    this.$sidebar = $('.js-build-sidebar');
    this.$sidebar.niceScroll();
  };

192
  Build.prototype.getBuildTrace = function () {
193
    return $.ajax({
194
      url: `${this.pageUrl}/trace.json`,
Filipa Lacerda committed
195 196 197
      data: this.state,
    })
      .done((log) => {
198
        gl.utils.setCiStatusFavicon(`${this.pageUrl}/status.json`);
199 200
        if (log.state) {
          this.state = log.state;
201
        }
202 203

        if (log.append) {
Filipa Lacerda committed
204
          this.$buildTraceOutput.append(log.html);
205
          this.logBytes += log.size;
206
        } else {
Filipa Lacerda committed
207
          this.$buildTraceOutput.html(log.html);
208 209 210 211 212 213 214 215 216 217 218 219
          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');
220 221 222
        }

        if (!log.complete) {
Filipa Lacerda committed
223 224
          this.toggleScrollAnimation(true);

225
          Build.timeout = setTimeout(() => {
Filipa Lacerda committed
226 227
            //eslint-disable-next-line
            this.getBuildTrace()
228 229 230 231 232
              .then(() => {
                if (!this.hasBeenScrolled) {
                  this.scrollToBottom();
                }
              });
233 234
          }, 4000);
        } else {
235
          this.$buildRefreshAnimation.remove();
Filipa Lacerda committed
236
          this.toggleScrollAnimation(false);
237 238
        }

239
        if (log.status !== this.buildStatus) {
Filipa Lacerda committed
240
          gl.utils.visitUrl(this.pageUrl);
241
        }
Filipa Lacerda committed
242 243
      })
      .fail(() => {
244
        this.$buildRefreshAnimation.remove();
Filipa Lacerda committed
245
      });
246 247
  };

248 249
  Build.prototype.shouldHideSidebarForViewport = function () {
    const bootstrapBreakpoint = this.bp.getBreakpointSize();
250 251 252
    return bootstrapBreakpoint === 'xs' || bootstrapBreakpoint === 'sm';
  };

253
  Build.prototype.toggleSidebar = function (shouldHide) {
254
    const shouldShow = typeof shouldHide === 'boolean' ? !shouldHide : undefined;
255
    const $toggleButton = $('.js-sidebar-build-toggle-header');
256

Filipa Lacerda committed
257 258
    this.$buildTrace
      .toggleClass('sidebar-expanded', shouldShow)
259
      .toggleClass('sidebar-collapsed', shouldHide);
Filipa Lacerda committed
260 261
    this.$sidebar
      .toggleClass('right-sidebar-expanded', shouldShow)
262
      .toggleClass('right-sidebar-collapsed', shouldHide);
263 264 265 266 267 268 269 270 271 272

    $('.js-build-page')
      .toggleClass('sidebar-expanded', shouldShow)
      .toggleClass('sidebar-collapsed', shouldHide);

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

275
  Build.prototype.sidebarOnResize = function () {
276
    this.toggleSidebar(this.shouldHideSidebarForViewport());
277

Filipa Lacerda committed
278 279
    this.verifyTopPosition();

280
    if (this.canScroll()) {
Filipa Lacerda committed
281 282
      this.toggleScroll();
    }
283 284
  };

285
  Build.prototype.sidebarOnClick = function () {
286
    if (this.shouldHideSidebarForViewport()) this.toggleSidebar();
287
    this.verifyTopPosition();
288 289
  };

290 291
  Build.prototype.updateArtifactRemoveDate = function () {
    const $date = $('.js-artifacts-remove');
292
    if ($date.length) {
293 294 295 296
      const date = $date.text();
      return $date.text(
        gl.utils.timeFor(new Date(date.replace(/([0-9]+)-([0-9]+)-([0-9]+)/g, '$1/$2/$3')), ' '),
      );
297 298 299
    }
  };

300
  Build.prototype.populateJobs = function (stage) {
301
    $('.build-job').hide();
302
    $(`.build-job[data-stage="${stage}"]`).show();
303 304
  };

305
  Build.prototype.updateStageDropdownText = function (stage) {
306 307 308
    $('.stage-selection').text(stage);
  };

309
  Build.prototype.updateDropdown = function (e) {
310
    e.preventDefault();
311
    const stage = e.currentTarget.text;
312 313 314 315 316 317
    this.updateStageDropdownText(stage);
    this.populateJobs(stage);
  };

  return Build;
})();