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
122
123
124
125
126
127
128
129
130
131
132
133
134
|
import autosize from 'autosize';
import $ from 'jquery';
import { isEmpty } from 'lodash';
import GfmAutoComplete, { defaultAutocompleteConfig } from 'ee_else_ce/gfm_auto_complete';
import { disableButtonIfEmptyField } from '~/lib/utils/common_utils';
import dropzoneInput from './dropzone_input';
import { addMarkdownListeners, removeMarkdownListeners } from './lib/utils/text_markdown';
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.
* @param {Object} gfmDataSources The paths of the autocomplete data sources to use for GfmAutoComplete
* By default, the backend embeds these in the global object gl.GfmAutocomplete.dataSources.
* Use this param to override them.
*/
constructor(form, enableGFM = {}, forceNew = false, gfmDataSources = {}) {
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
let dataSources = (gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources) || {};
if (!isEmpty(gfmDataSources)) {
dataSources = gfmDataSources;
}
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(dataSources, 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(dataSources, 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(dataSources);
this.autoComplete.setup(this.form.find('.js-gfm-input'), this.enableGFM);
this.formDropzone = dropzoneInput(this.form, { parallelUploads: 1 });
if (this.form.is(':not(.js-no-autosize)')) {
autosize(this.textarea);
}
}
// form and textarea event listeners
this.addEventListeners();
addMarkdownListeners(this.form);
this.form.show();
if (this.isAutosizeable) this.setupAutosize();
if (this.textarea.data('autofocus') === true) this.textarea.focus();
}
setupAutosize() {
// eslint-disable-next-line @gitlab/no-global-event-off
this.textarea.off('autosize:resized').on('autosize:resized', this.setHeightData.bind(this));
// eslint-disable-next-line @gitlab/no-global-event-off
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() {
// eslint-disable-next-line @gitlab/no-global-event-off
this.textarea.off('focus');
// eslint-disable-next-line @gitlab/no-global-event-off
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'));
}
}
|