summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/dirty_submit/dirty_submit_form.js
blob: 0fcaec9531c1638ae09b90266c1072e03565fba5 (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
import _ from 'underscore';
import $ from 'jquery';

class DirtySubmitForm {
  constructor(form) {
    this.form = form;
    this.dirtyInputs = [];
    this.isDisabled = true;

    this.init();
  }

  init() {
    this.inputs = this.form.querySelectorAll('input, textarea, select');
    this.submits = this.form.querySelectorAll('input[type=submit], button[type=submit]');

    this.inputs.forEach(DirtySubmitForm.initInput);
    this.toggleSubmission();

    this.registerListeners();
  }

  registerListeners() {
    const getThrottledHandlerForInput = _.memoize(() =>
      _.throttle(event => this.updateDirtyInput(event), DirtySubmitForm.THROTTLE_DURATION),
    );

    const throttledUpdateDirtyInput = event => {
      const throttledHandler = getThrottledHandlerForInput(event.target.name);
      throttledHandler(event);
    };

    this.form.addEventListener('input', throttledUpdateDirtyInput);
    this.form.addEventListener('change', throttledUpdateDirtyInput);
    $(this.form).on('change.select2', throttledUpdateDirtyInput);
    this.form.addEventListener('submit', event => this.formSubmit(event));
  }

  updateDirtyInput(event) {
    const { target } = event;

    if (!target.dataset.isDirtySubmitInput) return;

    this.updateDirtyInputs(target);
    this.toggleSubmission();
  }

  updateDirtyInputs(input) {
    const { name } = input;
    const isDirty =
      input.dataset.dirtySubmitOriginalValue !== DirtySubmitForm.inputCurrentValue(input);
    const indexOfInputName = this.dirtyInputs.indexOf(name);
    const isExisting = indexOfInputName !== -1;

    if (isDirty && !isExisting) this.dirtyInputs.push(name);
    if (!isDirty && isExisting) this.dirtyInputs.splice(indexOfInputName, 1);
  }

  toggleSubmission() {
    this.isDisabled = this.dirtyInputs.length === 0;
    this.submits.forEach(element => {
      element.disabled = this.isDisabled;
    });
  }

  formSubmit(event) {
    if (this.isDisabled) {
      event.preventDefault();
      event.stopImmediatePropagation();
    }

    return !this.isDisabled;
  }

  static initInput(element) {
    element.dataset.isDirtySubmitInput = true;
    element.dataset.dirtySubmitOriginalValue = DirtySubmitForm.inputCurrentValue(element);
  }

  static isInputCheckable(input) {
    return input.type === 'checkbox' || input.type === 'radio';
  }

  static inputCurrentValue(input) {
    return DirtySubmitForm.isInputCheckable(input) ? input.checked.toString() : input.value;
  }
}

DirtySubmitForm.THROTTLE_DURATION = 500;

export default DirtySubmitForm;