summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/gl_form.js
blob: 6958cf4c1734c1a20861c2c31ec3c6cdebb2bea4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import $ from 'jquery';
import autosize from 'autosize';
import GfmAutoComplete, { defaultAutocompleteConfig } from 'ee_else_ce/gfm_auto_complete';
import dropzoneInput from './dropzone_input';
import { addMarkdownListeners, removeMarkdownListeners } from './lib/utils/text_markdown';
import { disableButtonIfEmptyField } from '~/lib/utils/common_utils';

export default class GLForm {
  /**
   * Create a GLForm
   *
   * @param {jQuery} form Root element of the GLForm
   * @param {Object} enableGFM Which autocomplete features should be enabled?
   * @param {Boolean} forceNew If true, treat the element as a **new** form even if `gfm-form` class already exists.
   */
  constructor(form, enableGFM = {}, forceNew = false) {
    this.form = form;
    this.textarea = this.form.find('textarea.js-gfm-input');
    this.enableGFM = { ...defaultAutocompleteConfig, ...enableGFM };

    // Disable autocomplete for keywords which do not have dataSources available
    const dataSources = (gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources) || {};
    Object.keys(this.enableGFM).forEach(item => {
      if (item !== 'emojis' && !dataSources[item]) {
        this.enableGFM[item] = false;
      }
    });

    // Before we start, we should clean up any previous data for this form
    this.destroy();
    // Set up the form
    this.setupForm(forceNew);
    this.form.data('glForm', this);
  }

  destroy() {
    // Clean form listeners
    this.clearEventListeners();
    if (this.autoComplete) {
      this.autoComplete.destroy();
    }
    if (this.formDropzone) {
      this.formDropzone.destroy();
    }

    this.form.data('glForm', null);
  }

  setupForm(forceNew = false) {
    const isNewForm = this.form.is(':not(.gfm-form)') || forceNew;
    this.form.removeClass('js-new-note-form');
    if (isNewForm) {
      this.form.find('.div-dropzone').remove();
      this.form.addClass('gfm-form');
      // remove notify commit author checkbox for non-commit notes
      disableButtonIfEmptyField(
        this.form.find('.js-note-text'),
        this.form.find('.js-comment-button, .js-note-new-discussion'),
      );
      this.autoComplete = new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources);
      this.autoComplete.setup(this.form.find('.js-gfm-input'), this.enableGFM);
      this.formDropzone = dropzoneInput(this.form, { parallelUploads: 1 });
      autosize(this.textarea);
    }
    // form and textarea event listeners
    this.addEventListeners();
    addMarkdownListeners(this.form);
    this.form.show();
    if (this.isAutosizeable) this.setupAutosize();
  }

  setupAutosize() {
    this.textarea.off('autosize:resized').on('autosize:resized', this.setHeightData.bind(this));

    this.textarea.off('mouseup.autosize').on('mouseup.autosize', this.destroyAutosize.bind(this));

    setTimeout(() => {
      autosize(this.textarea);
      this.textarea.css('resize', 'vertical');
    }, 0);
  }

  setHeightData() {
    this.textarea.data('height', this.textarea.outerHeight());
  }

  destroyAutosize() {
    const outerHeight = this.textarea.outerHeight();

    if (this.textarea.data('height') === outerHeight) return;

    autosize.destroy(this.textarea);

    this.textarea.data('height', outerHeight);
    this.textarea.outerHeight(outerHeight);
    this.textarea.css('max-height', window.outerHeight);
  }

  clearEventListeners() {
    this.textarea.off('focus');
    this.textarea.off('blur');
    removeMarkdownListeners(this.form);
  }

  addEventListeners() {
    this.textarea.on('focus', function focusTextArea() {
      $(this)
        .closest('.md-area')
        .addClass('is-focused');
    });
    this.textarea.on('blur', function blurTextArea() {
      $(this)
        .closest('.md-area')
        .removeClass('is-focused');
    });
  }

  get supportsQuickActions() {
    return Boolean(this.textarea.data('supports-quick-actions'));
  }
}