StrictDoc Documentation
strictdoc/features/diff_and_changelog/assets/diff.js
Source file coverage
Path:
strictdoc/features/diff_and_changelog/assets/diff.js
Lines:
161
Non-empty lines:
131
Non-empty lines covered with requirements:
131 / 131 (100.0%)
Functions:
0
Functions covered by requirements:
0 / 0 (0.0%)
1
//
2
// @relation(SDOC-SRS-111, scope=file)
3
//
4
 
5
(function() {
6
  window.addEventListener("load", function() {
7
 
8
    const mutatingFrame = document.querySelector('#diff_result');
9
    if (!mutatingFrame) return;
10
 
11
    var observer = new MutationObserver(function(mutationsList, observer) {
12
 
13
      const diffTabRoot = mutatingFrame.querySelector('.diff');
14
      if (diffTabRoot) {
15
        // triggers on the diff tab
16
        prepareBulkButtons(diffTabRoot);
17
        syncDiff(diffTabRoot);
18
      }
19
 
20
      // Disable the observer after the first trigger
21
      // because the functions modify the elements inside the #diff_result.
22
      observer.disconnect();
23
    });
24
 
25
    observer.observe(
26
      mutatingFrame, {
27
        childList: true,
28
        subtree: true
29
      }
30
    );
31
 
32
  }, false);
33
 
34
  function prepareBulkButtons(root) {
35
 
36
    const leftColumn = root.querySelector('.diff_column[left]');
37
    const rightColumn = root.querySelector('.diff_column[right]');
38
 
39
    const leftOpenBtn = root.querySelector('#diff_left_open');
40
    const leftCloseBtn = root.querySelector('#diff_left_close');
41
    const rightOpenBtn = root.querySelector('#diff_right_open');
42
    const rightCloseBtn = root.querySelector('#diff_right_close');
43
 
44
    const detailsLeftAll = [...leftColumn.querySelectorAll('details')];
45
    const detailsRightAll = [...rightColumn.querySelectorAll('details')];
46
    const detailsLeft = [...leftColumn.querySelectorAll('details[modified]')];
47
    const detailsRight = [...rightColumn.querySelectorAll('details[modified]')];
48
 
49
    leftOpenBtn.addEventListener("click", (event) => {
50
      event.preventDefault();
51
      _openAll(detailsLeft);
52
    });
53
    leftCloseBtn.addEventListener("click", (event) => {
54
      event.preventDefault();
55
      _closeAll(detailsLeftAll);
56
      leftColumn.scrollTo(0, 0);
57
    });
58
    rightOpenBtn.addEventListener("click", (event) => {
59
      event.preventDefault();
60
      _openAll(detailsRight);
61
    });
62
    rightCloseBtn.addEventListener("click", (event) => {
63
      event.preventDefault();
64
      _closeAll(detailsRightAll);
65
      rightColumn.scrollTo(0, 0);
66
    });
67
  }
68
 
69
  function syncDiff(root) {
70
 
71
    const sync = [...root.querySelectorAll('button[uid]')]
72
      .reduce((acc, curr) => {
73
        const uid = curr.getAttribute('uid');
74
        const side = curr.getAttribute('side');
75
        acc[uid] = {
76
          [side]: curr,
77
          ...acc[uid]
78
        }
79
        return acc
80
      }, {});
81
 
82
    Object.entries(sync).forEach(([uid, obj]) => {
83
      if (!obj.left) {
84
        obj.right.remove()
85
        return
86
      };
87
 
88
      if (!obj.right) {
89
        obj.left.remove()
90
        return
91
      };
92
 
93
      obj.left.addEventListener("click", (event) => {
94
        event.preventDefault();
95
 
96
        // _openAll(detailsLeftAll);
97
        _openAncestors(obj.left, root);
98
        _scrollTo(obj.left);
99
 
100
        setTimeout(() => {
101
          // _openAll(detailsRightAll);
102
          _openAncestors(obj.right, root);
103
          _scrollTo(obj.right);
104
        }, 900);
105
      });
106
 
107
      obj.right.addEventListener("click", (event) => {
108
        event.preventDefault();
109
 
110
        // _openAll(detailsRightAll);
111
        _openAncestors(obj.right, root);
112
        _scrollTo(obj.right);
113
 
114
        setTimeout(() => {
115
          // _openAll(detailsLeftAll);
116
          _openAncestors(obj.left, root);
117
          _scrollTo(obj.left);
118
        }, 900);
119
      });
120
    })
121
  };
122
 
123
  function _openAncestors(element, root) {
124
 
125
    let currentElement = element;
126
 
127
    while (currentElement && currentElement !== root) {
128
      currentElement = currentElement.parentElement;
129
 
130
      if (currentElement && currentElement.tagName === 'DETAILS') {
131
        currentElement.setAttribute('open', '');
132
      }
133
    }
134
  };
135
 
136
  function _scrollTo(target) {
137
    // We insert a special element into the button ('button[uid]')
138
    // that compensates by 40 pixels
139
    // for the height of the sticky header
140
    // at the top of the scroll container:
141
    // ***
142
    // <a-a style="position:absolute;top:-40px;"></a-a>
143
 
144
    const a = target.querySelector('a-a') || target;
145
    a.scrollIntoView({
146
      behavior: "smooth"
147
    });
148
  };
149
 
150
  function _closeAll(details) {
151
    details.forEach(detail => {
152
      detail.removeAttribute("open")
153
    });
154
  };
155
 
156
  function _openAll(details) {
157
    details.forEach(detail => {
158
      detail.setAttribute("open", "")
159
    });
160
  };
161
})();