aboutsummaryrefslogtreecommitdiff
path: root/static/input_list.js
blob: a7a446f3f18d20cda4d5ad1198807f866fe7b9b6 (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
/*
  ∀ children('.input-list') => 'unit' ∈ classList(child)

  <div class="input-list">
    <div class="unit"><input/></div>
    <div class="unit final"><input/></div>
  </div>

*/


/* private */
function transferListeners(old_unit, new_unit) {
    for (let [o, n] of zip([old_unit, ...old_unit.querySelectorAll("*")],
                           [new_unit, ...new_unit.querySelectorAll("*")])) {
        for (const key in o.listeners) {
            if (! o.listeners.hasOwnProperty(key)) continue;
            for (let proc of o.listeners[key]) {
                n.addEventListener(key, proc);
            }
        }
    }
}


/* private */
function advance_final(input_list) {
    let old_unit = input_list.unit;
    let new_unit = old_unit.cloneNode(true);
    new_unit.classList.add('final');
    transferListeners(old_unit, new_unit);
    input_list.appendChild(new_unit);
}


/* private */
function update_inline_list () {

    /* can target self */
    let unit = this.closest('.unit');

    let lst = this.closest('.input-list');

    if (unit.classList.contains("final")) {
        if (this.value !== '') {
            unit.classList.remove('final');
            advance_final(lst);
        }
    } else {
        /* TODO all significant fields empty, instead of just current */
        if (this.value === '') {
            let sibling = unit.previousElementSibling || unit.nextElementSibling;
            unit.remove();
            if (sibling.tagName !== 'input')
                sibling = sibling.querySelector('input');
            sibling.focus();
        }
    }
}

/* run this from window.onload (or similar) */
function init_input_list() {

    for (let lst of document.getElementsByClassName('input-list')) {

        for (let el of lst.getElementsByTagName('input')) {
            el.addEventListener('input', update_inline_list);
        }

        let oldUnit = lst.querySelector('.final.unit')
        let unit = oldUnit.cloneNode(true);

        transferListeners(oldUnit, unit);

        lst.unit = unit;

        if (lst.dataset.bindby) {
            lst.get_value = lst.dataset.bindby;
        } else if (lst.dataset.joinby) {
            lst.get_value = get_get_value(lst.dataset.joinby);
        } else {
            lst.get_value = get_get_value();
        }

        /* Propagate add event listener downwards */
        lst._addEventListener = lst.addEventListener;
        lst.addEventListener = function(type, proc) {
            switch (type) {
            case 'input':
                for (let el of lst.getElementsByTagName('input')) {
                    el.addEventListener('input', proc);
                }
            default:
                lst._addEventListener(type, proc);
            }
        };
    }
}

/* -------------------------------------------------- */

/* different function forms since we want to capture one self */
const get_get_value = (join=',') => function () {
    return [...this.querySelectorAll('input')]
        .map(x => x.value)
        .filter(x => x != '');
        // .join(join);
}

/* -------------------------------------------------- */