templates/assets/js/exception.js   F
last analyzed

Complexity

Total Complexity 70
Complexity/F 2.5

Size

Lines of Code 293
Function Count 28

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 190
dl 0
loc 293
rs 2.8
c 0
b 0
f 0
wmc 70
mnd 42
bc 42
fnc 28
bpm 1.5
cpm 2.5
noi 9

How to fix   Complexity   

Complexity

Complex classes like templates/assets/js/exception.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/* This file is based on WebProfilerBundle/Resources/views/Profiler/base_js.html.twig.
2
   If you make any change in this file, verify the same change is needed in the other file. */
3
/*<![CDATA[*/
4
if (typeof Sfjs === 'undefined') {
0 ignored issues
show
Bug introduced by
The variable Sfjs seems to be never declared. If this is a global, consider adding a /** global: Sfjs */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
5
    Sfjs = (function() {
0 ignored issues
show
Bug introduced by
The variable Sfjs seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.Sfjs.
Loading history...
6
        "use strict";
7
8
        if ('classList' in document.documentElement) {
9
            var hasClass = function (el, cssClass) { return el.classList.contains(cssClass); };
10
            var removeClass = function(el, cssClass) { el.classList.remove(cssClass); };
11
            var addClass = function(el, cssClass) { el.classList.add(cssClass); };
12
            var toggleClass = function(el, cssClass) { el.classList.toggle(cssClass); };
13
        } else {
14
            var hasClass = function (el, cssClass) { return el.className.match(new RegExp('\\b' + cssClass + '\\b')); };
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable hasClass already seems to be declared on line 9. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
15
            var removeClass = function(el, cssClass) { el.className = el.className.replace(new RegExp('\\b' + cssClass + '\\b'), ' '); };
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable removeClass already seems to be declared on line 10. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
16
            var addClass = function(el, cssClass) { if (!hasClass(el, cssClass)) { el.className += " " + cssClass; } };
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable addClass already seems to be declared on line 11. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
17
            var toggleClass = function(el, cssClass) { hasClass(el, cssClass) ? removeClass(el, cssClass) : addClass(el, cssClass); };
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable toggleClass already seems to be declared on line 12. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
18
        }
19
20
        var addEventListener;
21
22
        var el = document.createElement('div');
23
        if (!('addEventListener' in el)) {
24
            addEventListener = function (element, eventName, callback) {
25
                element.attachEvent('on' + eventName, callback);
26
            };
27
        } else {
28
            addEventListener = function (element, eventName, callback) {
29
                element.addEventListener(eventName, callback, false);
30
            };
31
        }
32
33
        if (navigator.clipboard) {
0 ignored issues
show
Bug introduced by
The variable navigator seems to be never declared. If this is a global, consider adding a /** global: navigator */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
34
            document.querySelectorAll('[data-clipboard-text]').forEach(function(element) {
35
                removeClass(element, 'hidden');
36
                element.addEventListener('click', function() {
37
                    navigator.clipboard.writeText(element.getAttribute('data-clipboard-text'));
0 ignored issues
show
Bug introduced by
The variable navigator seems to be never declared. If this is a global, consider adding a /** global: navigator */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
38
                })
39
            });
40
        }
41
42
        return {
43
            addEventListener: addEventListener,
44
45
            createTabs: function() {
46
                var tabGroups = document.querySelectorAll('.sf-tabs:not([data-processed=true])');
47
48
                /* create the tab navigation for each group of tabs */
49
                for (var i = 0; i < tabGroups.length; i++) {
50
                    var tabs = tabGroups[i].querySelectorAll(':scope > .tab');
51
                    var tabNavigation = document.createElement('ul');
52
                    tabNavigation.className = 'tab-navigation';
53
54
                    var selectedTabId = 'tab-' + i + '-0'; /* select the first tab by default */
55
                    for (var j = 0; j < tabs.length; j++) {
56
                        var tabId = 'tab-' + i + '-' + j;
57
                        var tabTitle = tabs[j].querySelector('.tab-title').innerHTML;
58
59
                        var tabNavigationItem = document.createElement('li');
60
                        tabNavigationItem.setAttribute('data-tab-id', tabId);
61
                        if (hasClass(tabs[j], 'active')) { selectedTabId = tabId; }
62
                        if (hasClass(tabs[j], 'disabled')) { addClass(tabNavigationItem, 'disabled'); }
63
                        tabNavigationItem.innerHTML = tabTitle;
64
                        tabNavigation.appendChild(tabNavigationItem);
65
66
                        var tabContent = tabs[j].querySelector('.tab-content');
67
                        tabContent.parentElement.setAttribute('id', tabId);
68
                    }
69
70
                    tabGroups[i].insertBefore(tabNavigation, tabGroups[i].firstChild);
71
                    addClass(document.querySelector('[data-tab-id="' + selectedTabId + '"]'), 'active');
72
                }
73
74
                /* display the active tab and add the 'click' event listeners */
75
                for (i = 0; i < tabGroups.length; i++) {
76
                    tabNavigation = tabGroups[i].querySelectorAll(':scope >.tab-navigation li');
77
78
                    for (j = 0; j < tabNavigation.length; j++) {
79
                        tabId = tabNavigation[j].getAttribute('data-tab-id');
80
                        document.getElementById(tabId).querySelector('.tab-title').className = 'hidden';
81
82
                        if (hasClass(tabNavigation[j], 'active')) {
83
                            document.getElementById(tabId).className = 'block';
84
                        } else {
85
                            document.getElementById(tabId).className = 'hidden';
86
                        }
87
88
                        tabNavigation[j].addEventListener('click', function(e) {
89
                            var activeTab = e.target || e.srcElement;
90
91
                            /* needed because when the tab contains HTML contents, user can click */
92
                            /* on any of those elements instead of their parent '<li>' element */
93
                            while (activeTab.tagName.toLowerCase() !== 'li') {
94
                                activeTab = activeTab.parentNode;
95
                            }
96
97
                            /* get the full list of tabs through the parent of the active tab element */
98
                            var tabNavigation = activeTab.parentNode.children;
99
                            for (var k = 0; k < tabNavigation.length; k++) {
100
                                var tabId = tabNavigation[k].getAttribute('data-tab-id');
101
                                document.getElementById(tabId).className = 'hidden';
102
                                removeClass(tabNavigation[k], 'active');
103
                            }
104
105
                            addClass(activeTab, 'active');
106
                            var activeTabId = activeTab.getAttribute('data-tab-id');
107
                            document.getElementById(activeTabId).className = 'block';
108
                        });
109
                    }
110
111
                    tabGroups[i].setAttribute('data-processed', 'true');
112
                }
113
            },
114
115
            createToggles: function() {
116
                var toggles = document.querySelectorAll('.sf-toggle:not([data-processed=true])');
117
118
                for (var i = 0; i < toggles.length; i++) {
119
                    var elementSelector = toggles[i].getAttribute('data-toggle-selector');
120
                    var element = document.querySelector(elementSelector);
121
122
                    addClass(element, 'sf-toggle-content');
123
124
                    if (toggles[i].hasAttribute('data-toggle-initial') && toggles[i].getAttribute('data-toggle-initial') == 'display') {
125
                        addClass(toggles[i], 'sf-toggle-on');
126
                        addClass(element, 'sf-toggle-visible');
127
                    } else {
128
                        addClass(toggles[i], 'sf-toggle-off');
129
                        addClass(element, 'sf-toggle-hidden');
130
                    }
131
132
                    addEventListener(toggles[i], 'click', function(e) {
133
                        e.preventDefault();
134
135
                        if ('' !== window.getSelection().toString()) {
136
                            /* Don't do anything on text selection */
137
                            return;
138
                        }
139
140
                        var toggle = e.target || e.srcElement;
141
142
                        /* needed because when the toggle contains HTML contents, user can click */
143
                        /* on any of those elements instead of their parent '.sf-toggle' element */
144
                        while (!hasClass(toggle, 'sf-toggle')) {
145
                            toggle = toggle.parentNode;
146
                        }
147
148
                        var element = document.querySelector(toggle.getAttribute('data-toggle-selector'));
149
150
                        toggleClass(toggle, 'sf-toggle-on');
151
                        toggleClass(toggle, 'sf-toggle-off');
152
                        toggleClass(element, 'sf-toggle-hidden');
153
                        toggleClass(element, 'sf-toggle-visible');
154
155
                        /* the toggle doesn't change its contents when clicking on it */
156
                        if (!toggle.hasAttribute('data-toggle-alt-content')) {
157
                            return;
158
                        }
159
160
                        if (!toggle.hasAttribute('data-toggle-original-content')) {
161
                            toggle.setAttribute('data-toggle-original-content', toggle.innerHTML);
162
                        }
163
164
                        var currentContent = toggle.innerHTML;
165
                        var originalContent = toggle.getAttribute('data-toggle-original-content');
166
                        var altContent = toggle.getAttribute('data-toggle-alt-content');
167
                        toggle.innerHTML = currentContent !== altContent ? altContent : originalContent;
168
                    });
169
170
                    /* Prevents from disallowing clicks on links inside toggles */
171
                    var toggleLinks = toggles[i].querySelectorAll('a');
172
                    for (var j = 0; j < toggleLinks.length; j++) {
173
                        addEventListener(toggleLinks[j], 'click', function(e) {
174
                            e.stopPropagation();
175
                        });
176
                    }
177
178
                    /* Prevents from disallowing clicks on "copy to clipboard" elements inside toggles */
179
                    var copyToClipboardElements = toggles[i].querySelectorAll('span[data-clipboard-text]');
180
                    for (var k = 0; k < copyToClipboardElements.length; k++) {
181
                        addEventListener(copyToClipboardElements[k], 'click', function(e) {
182
                            e.stopPropagation();
183
                        });
184
                    }
185
186
                    toggles[i].setAttribute('data-processed', 'true');
187
                }
188
            },
189
190
            createFilters: function() {
191
                document.querySelectorAll('[data-filters] [data-filter]').forEach(function (filter) {
192
                    var filters = filter.closest('[data-filters]'),
193
                        type = 'choice',
194
                        name = filter.dataset.filter,
195
                        ucName = name.charAt(0).toUpperCase()+name.slice(1),
196
                        list = document.createElement('ul'),
197
                        values = filters.dataset['filter'+ucName] || filters.querySelectorAll('[data-filter-'+name+']'),
198
                        labels = {},
199
                        defaults = null,
200
                        indexed = {},
201
                        processed = {};
202
                    if (typeof values === 'string') {
203
                        type = 'level';
204
                        labels = values.split(',');
205
                        values = values.toLowerCase().split(',');
206
                        defaults = values.length - 1;
207
                    }
208
                    addClass(list, 'filter-list');
209
                    addClass(list, 'filter-list-'+type);
210
                    values.forEach(function (value, i) {
211
                        if (value instanceof HTMLElement) {
0 ignored issues
show
Bug introduced by
The variable HTMLElement seems to be never declared. If this is a global, consider adding a /** global: HTMLElement */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
212
                            value = value.dataset['filter'+ucName];
213
                        }
214
                        if (value in processed) {
215
                            return;
216
                        }
217
                        var option = document.createElement('li'),
218
                            label = i in labels ? labels[i] : value,
219
                            active = false,
220
                            matches;
221
                        if ('' === label) {
222
                            option.innerHTML = '<em>(none)</em>';
223
                        } else {
224
                            option.innerText = label;
225
                        }
226
                        option.dataset.filter = value;
227
                        option.setAttribute('title', 1 === (matches = filters.querySelectorAll('[data-filter-'+name+'="'+value+'"]').length) ? 'Matches 1 row' : 'Matches '+matches+' rows');
228
                        indexed[value] = i;
229
                        list.appendChild(option);
230
                        addEventListener(option, 'click', function () {
231
                            if ('choice' === type) {
232
                                filters.querySelectorAll('[data-filter-'+name+']').forEach(function (row) {
233
                                    if (option.dataset.filter === row.dataset['filter'+ucName]) {
234
                                        toggleClass(row, 'filter-hidden-'+name);
235
                                    }
236
                                });
237
                                toggleClass(option, 'active');
238
                            } else if ('level' === type) {
239
                                if (i === this.parentNode.querySelectorAll('.active').length - 1) {
240
                                    return;
241
                                }
242
                                this.parentNode.querySelectorAll('li').forEach(function (currentOption, j) {
243
                                    if (j <= i) {
244
                                        addClass(currentOption, 'active');
245
                                        if (i === j) {
246
                                            addClass(currentOption, 'last-active');
247
                                        } else {
248
                                            removeClass(currentOption, 'last-active');
249
                                        }
250
                                    } else {
251
                                        removeClass(currentOption, 'active');
252
                                        removeClass(currentOption, 'last-active');
253
                                    }
254
                                });
255
                                filters.querySelectorAll('[data-filter-'+name+']').forEach(function (row) {
256
                                    if (i < indexed[row.dataset['filter'+ucName]]) {
257
                                        addClass(row, 'filter-hidden-'+name);
258
                                    } else {
259
                                        removeClass(row, 'filter-hidden-'+name);
260
                                    }
261
                                });
262
                            }
263
                        });
264
                        if ('choice' === type) {
265
                            active = null === defaults || 0 <= defaults.indexOf(value);
266
                        } else if ('level' === type) {
267
                            active = i <= defaults;
268
                            if (active && i === defaults) {
269
                                addClass(option, 'last-active');
270
                            }
271
                        }
272
                        if (active) {
273
                            addClass(option, 'active');
274
                        } else {
275
                            filters.querySelectorAll('[data-filter-'+name+'="'+value+'"]').forEach(function (row) {
276
                                toggleClass(row, 'filter-hidden-'+name);
277
                            });
278
                        }
279
                        processed[value] = true;
280
                    });
281
282
                    if (1 < list.childNodes.length) {
283
                        filter.appendChild(list);
284
                        filter.dataset.filtered = '';
285
                    }
286
                });
287
            }
288
        };
289
    })();
290
291
    Sfjs.addEventListener(document, 'DOMContentLoaded', function() {
292
        Sfjs.createTabs();
293
        Sfjs.createToggles();
294
        Sfjs.createFilters();
295
    });
296
}
297
/*]]>*/
298