Passed
Push — master ( 3c18a7...14c66b )
by Marcel
07:43 queued 04:35
created

js/navigation.js   F

Complexity

Total Complexity 60
Complexity/F 2.31

Size

Lines of Code 425
Function Count 26

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 60
eloc 303
mnd 34
bc 34
fnc 26
dl 0
loc 425
rs 3.6
bpm 1.3076
cpm 2.3076
noi 1
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like js/navigation.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
/**
2
 * Analytics
3
 *
4
 * This file is licensed under the Affero General Public License version 3 or
5
 * later. See the LICENSE.md file.
6
 *
7
 * @author Marcel Scherello <[email protected]>
8
 * @copyright 2021 Marcel Scherello
9
 */
10
/** global: OCA */
11
/** global: OCP */
12
/** global: OC */
13
'use strict';
14
/**
15
 * @namespace OCA.Analytics.Navigation
16
 */
17
OCA.Analytics.Navigation = {
18
    quickstartValue: '',
19
    quickstartId: 0,
20
    newReportId: 0,
21
22
    init: function (datasetId) {
23
        document.getElementById('navigationDatasets').innerHTML = '<div style="text-align:center; padding-top:100px" className="get-metadata icon-loading"></div>';
24
        OCA.Analytics.Navigation.getDatasets(datasetId);
25
    },
26
27
    buildNavigation: function (data) {
28
        OCA.Analytics.Sidebar.hideSidebar();
29
        document.getElementById('navigationDatasets').innerHTML = '';
30
        let li = document.createElement('li');
31
        let a = document.createElement('a');
32
        a.classList.add('icon-toggle-pictures');
33
        a.innerText = t('analytics', 'Overview');
34
        a.id = 'overviewButton'
35
        a.addEventListener('click', OCA.Analytics.Navigation.handleOverviewButton);
36
        li.appendChild(a);
37
        document.getElementById('navigationDatasets').appendChild(li);
38
39
        let li2 = document.createElement('li');
40
        let a2 = document.createElement('a');
41
        a2.classList.add('icon-add');
42
        a2.innerText = t('analytics', 'New report');
43
44
        a2.id = 'newDatasetButton';
45
        a2.addEventListener('click', OCA.Analytics.Navigation.handleNewDatasetButton);
46
        li2.appendChild(a2);
47
        document.getElementById('navigationDatasets').appendChild(li2);
48
49
        for (let navigation of data) {
50
            OCA.Analytics.Navigation.buildNavigationRow(navigation);
51
        }
52
    },
53
54
    buildNavigationRow: function (data) {
55
        let li = document.createElement('li');
56
        let typeIcon;
57
58
        let a = document.createElement('a');
59
        a.setAttribute('href', '#/r/' + data['id']);
60
        a.style.position = 'relative';
61
        let typeINT = parseInt(data['type']);
62
        if (typeINT === OCA.Analytics.TYPE_INTERNAL_FILE || typeINT === OCA.Analytics.TYPE_EXCEL) {
63
            typeIcon = 'icon-file';
64
        } else if (typeINT === OCA.Analytics.TYPE_INTERNAL_DB) {
65
            typeIcon = 'icon-projects';
66
        } else if (typeINT === OCA.Analytics.TYPE_SHARED) {
67
            if (document.getElementById('advanced').value === 'true') {
68
                // don´t show shared reports in advanced config mode at all as no config is possible
69
                return;
70
            }
71
            typeIcon = 'icon-shared';
72
        } else if (typeINT === OCA.Analytics.TYPE_EMPTY_GROUP) {
73
            typeIcon = 'icon-folder';
74
            li.classList.add('collapsible');
75
        } else {
76
            typeIcon = 'icon-external';
77
        }
78
        a.classList.add(typeIcon);
79
80
        a.innerText = data['name'];
81
        a.dataset.id = data['id'];
82
        a.dataset.type = data['type'];
83
        a.dataset.name = data['name'];
84
        li.appendChild(a);
85
86
        let ulSublist = document.createElement('ul');
87
        ulSublist.id = 'dataset-' + data['id'];
88
89
        if (parseInt(data['favorite']) === 1) {
90
            let divFav = OCA.Analytics.Navigation.buildFavoriteIcon(data['id'], data['name'])
91
            a.appendChild(divFav);
92
        }
93
94
        let divUtils = OCA.Analytics.Navigation.buildNavigationUtils(data);
95
        let divMenu = OCA.Analytics.Navigation.buildNavigationMenu(data);
96
        li.appendChild(divUtils);
97
        li.appendChild(divMenu);
98
99
        if (typeINT === OCA.Analytics.TYPE_EMPTY_GROUP) {
100
            li.appendChild(ulSublist);
101
            a.addEventListener('click', OCA.Analytics.Navigation.handleGroupClicked);
102
        } else {
103
            a.addEventListener('click', OCA.Analytics.Navigation.handleNavigationClicked);
104
        }
105
106
        // add navigation row to navigation list or to an existing parent node
107
        let categoryList;
108
        if (parseInt(data['parent']) !== 0 && document.getElementById('dataset-' + data['parent'])) {
109
            categoryList = document.getElementById('dataset-' + data['parent']);
110
            categoryList.appendChild(li);
111
        } else {
112
            categoryList = document.getElementById('navigationDatasets');
113
            if (parseInt(data['favorite']) === 1) {
114
                categoryList.insertBefore(li, categoryList.children[2]);
115
            } else {
116
                categoryList.appendChild(li);
117
            }
118
        }
119
120
    },
121
122
    buildFavoriteIcon: function (id, name) {
123
        let divFav = document.createElement('div');
124
        divFav.classList.add('favorite-mark');
125
        divFav.id = 'fav-' + id;
126
        let spanFav = document.createElement('span');
127
        spanFav.classList.add('icon', 'icon-starred', 'favorite-star');
128
        spanFav.dataset.testing = 'favI' + name;
129
        divFav.appendChild(spanFav)
130
        return divFav;
131
    },
132
133
    buildNavigationUtils: function (data) {
134
        let divUtils = document.createElement('div');
135
        divUtils.classList.add('app-navigation-entry-utils');
136
        let ulUtils = document.createElement('ul');
137
138
        // add indicators when a dataload or schedule is existing
139
        if (document.getElementById('advanced').value === 'true') {
140
            if (data.schedules && parseInt(data.schedules) !== 0) {
141
                let liScheduleButton = document.createElement('li');
142
                liScheduleButton.classList.add('app-navigation-entry-utils-menu-button');
143
                let ScheduleButton = document.createElement('button');
144
                ScheduleButton.classList.add('icon-history', 'toolTip');
145
                ScheduleButton.setAttribute('title', t('analytics', 'scheduled dataload'));
146
                liScheduleButton.appendChild(ScheduleButton);
147
                ulUtils.appendChild(liScheduleButton);
148
            }
149
            if (data.dataloads && parseInt(data.dataloads) !== 0) {
150
                let liScheduleButton = document.createElement('li');
151
                liScheduleButton.classList.add('app-navigation-entry-utils-menu-button');
152
                let ScheduleButton = document.createElement('button');
153
                ScheduleButton.classList.add('icon-category-workflow', 'toolTip');
154
                ScheduleButton.setAttribute('title', t('analytics', 'Dataload'));
155
                liScheduleButton.appendChild(ScheduleButton);
156
                ulUtils.appendChild(liScheduleButton);
157
            }
158
        }
159
160
        let liMenuButton = document.createElement('li');
161
        liMenuButton.classList.add('app-navigation-entry-utils-menu-button');
162
        let button = document.createElement('button');
163
        button.addEventListener('click', OCA.Analytics.Navigation.handleOptionsClicked);
164
        button.dataset.id = data.id;
165
        button.dataset.name = data.name;
166
        button.dataset.type = data.type;
167
        button.classList.add('menuButton');
168
        liMenuButton.appendChild(button);
169
        ulUtils.appendChild(liMenuButton);
170
        divUtils.appendChild(ulUtils);
171
172
        return divUtils;
173
    },
174
175
    buildNavigationMenu: function (data) {
176
        // clone the DOM template
177
        let navigationMenu = document.importNode(document.getElementById('templateNavigationMenu').content, true);
178
179
        let menu = navigationMenu.getElementById('navigationMenu');
180
        menu.dataset.id = data.id;
181
        menu.dataset.type = data.type;
182
        menu.dataset.name = data.name;
183
184
        let edit = navigationMenu.getElementById('navigationMenuEdit');
185
        edit.addEventListener('click', OCA.Analytics.Navigation.handleBasicClicked);
186
        edit.children[1].innerText = t('analytics', 'Basic settings');
187
        edit.dataset.testing = 'basic' + data.name;
188
189
        let favorite = navigationMenu.getElementById('navigationMenueFavorite');
190
        favorite.addEventListener('click', OCA.Analytics.Navigation.handleFavoriteClicked);
191
        favorite.dataset.testing = 'fav' + data.name;
192
193
        let advanced = navigationMenu.getElementById('navigationMenuAdvanced');
194
        if (document.getElementById('advanced').value === 'true') {
195
            edit.remove();
196
            advanced.addEventListener('click', OCA.Analytics.Navigation.handleReportClicked);
197
            advanced.children[0].classList.add('icon-category-monitoring');
198
            advanced.children[1].innerText = t('analytics', 'Back to report');
199
            advanced.dataset.testing = 'back' + data.name;
200
        } else {
201
            advanced.addEventListener('click', OCA.Analytics.Navigation.handleAdvancedClicked);
202
            advanced.children[0].classList.add('icon-category-customization');
203
            advanced.children[1].innerText = t('analytics', 'Advanced');
204
            advanced.dataset.testing = 'adv' + data.name;
205
        }
206
207
        if (parseInt(data.favorite) === 1) {
208
            favorite.firstElementChild.classList.replace('icon-star', 'icon-starred');
209
            favorite.children[1].innerHTML = t('analytics', 'Remove from favorites');
210
        } else {
211
            favorite.children[1].innerHTML = t('analytics', 'Add to favorites');
212
        }
213
214
        let deleteReport = navigationMenu.getElementById('navigationMenuDelete');
215
        deleteReport.dataset.id = data.id;
216
        deleteReport.addEventListener('click', OCA.Analytics.Sidebar.Dataset.handleDeleteButton);
217
218
        let unshareReport = navigationMenu.getElementById('navigationMenuUnshare');
219
220
        if (parseInt(data['type']) === OCA.Analytics.TYPE_EMPTY_GROUP) {
221
            unshareReport.remove();
222
            favorite.remove();
223
            deleteReport.children[1].innerHTML = t('analytics', 'Delete folder');
224
            advanced.remove();
225
        } else if (parseInt(data['type']) === OCA.Analytics.TYPE_SHARED) {
226
            advanced.remove();
227
            deleteReport.remove();
228
            edit.remove();
229
            unshareReport.dataset.shareId = data.shareId;
230
            unshareReport.addEventListener('click', OCA.Analytics.Navigation.handleUnshareButton);
231
        } else {
232
            unshareReport.remove();
233
        }
234
235
        return navigationMenu;
236
    },
237
238
    handleNewDatasetButton: function () {
239
        OCA.Analytics.Navigation.createDataset();
240
    },
241
242
    handleOverviewButton: function () {
243
        if (document.querySelector('#navigationDatasets .active')) {
244
            document.querySelector('#navigationDatasets .active').classList.remove('active');
245
        }
246
        OCA.Analytics.UI.hideElement('analytics-content');
247
        OCA.Analytics.UI.showElement('analytics-intro');
248
        document.getElementById('ulAnalytics').innerHTML = '';
249
        window.location.href = '#'
250
        OCA.Analytics.Dashboard.init()
251
    },
252
253
    handleNavigationClicked: function (evt) {
254
        if (document.querySelector('.app-navigation-entry-menu.open') !== null) {
255
            document.querySelector('.app-navigation-entry-menu.open').classList.remove('open');
256
        }
257
        let activeCategory = document.querySelector('#navigationDatasets .active');
258
        if (evt) {
259
            if (activeCategory) {
260
                activeCategory.classList.remove('active');
261
            }
262
            evt.target.parentElement.classList.add('active');
263
        }
264
        if (document.getElementById('advanced').value === 'true') {
265
            OCA.Analytics.Sidebar.showSidebar(evt);
266
            evt.stopPropagation();
267
        } else {
268
            document.getElementById('filterVisualisation').innerHTML = '';
269
            if (typeof (OCA.Analytics.currentReportData.options) !== 'undefined') {
270
                // reset any user-filters and display the filters stored for the report
271
                delete OCA.Analytics.currentReportData.options;
272
            }
273
            OCA.Analytics.unsavedFilters = false;
274
            OCA.Analytics.Sidebar.hideSidebar();
275
            OCA.Analytics.Backend.getData();
276
        }
277
    },
278
279
    handleOptionsClicked: function (evt) {
280
        OCA.Analytics.UI.hideReportMenu();
281
        let openMenu;
282
        if (document.querySelector('.app-navigation-entry-menu.open') !== null) {
283
            openMenu = document.querySelector('.app-navigation-entry-menu.open').previousElementSibling.firstElementChild.firstElementChild.firstElementChild.dataset.id;
284
            document.querySelector('.app-navigation-entry-menu.open').classList.remove('open');
285
        }
286
        if (openMenu !== evt.target.dataset.id) {
0 ignored issues
show
Bug introduced by
The variable openMenu does not seem to be initialized in case document.querySelector("...ry-menu.open") !== null on line 282 is false. Are you sure this can never be the case?
Loading history...
287
            evt.target.parentElement.parentElement.parentElement.nextElementSibling.classList.add('open');
288
        }
289
    },
290
291
    handleBasicClicked: function (evt) {
292
        if (document.querySelector('.app-navigation-entry-menu.open') !== null) {
293
            document.querySelector('.app-navigation-entry-menu.open').classList.remove('open');
294
        }
295
        evt.stopPropagation();
296
        OCA.Analytics.Sidebar.showSidebar(evt);
297
    },
298
299
    handleAdvancedClicked: function (evt) {
300
        document.querySelector('.app-navigation-entry-menu.open').classList.remove('open');
301
        const datasetId = evt.target.closest('div').dataset.id;
302
        window.location = OC.generateUrl('apps/analytics/a/') + '#/r/' + datasetId;
303
        evt.stopPropagation();
304
    },
305
306
    handleFavoriteClicked: function (evt) {
307
        let datasetId = evt.target.closest('div').dataset.id;
308
        let icon = evt.target.parentNode.firstElementChild;
309
        let isFavorite = 'false';
310
311
        if (icon.classList.contains('icon-star')) {
312
            icon.classList.replace('icon-star', 'icon-starred');
313
            evt.target.parentNode.children[1].innerHTML = t('analytics', 'Remove from favorites');
314
            isFavorite = 'true';
315
316
            let divFav = OCA.Analytics.Navigation.buildFavoriteIcon(datasetId, '');
317
            evt.target.closest('div').parentElement.firstElementChild.appendChild(divFav);
318
319
        } else {
320
            icon.classList.replace('icon-starred', 'icon-star');
321
            evt.target.parentNode.children[1].innerHTML = t('analytics', 'Add to favorites');
322
            document.getElementById('fav-' + datasetId).remove();
323
        }
324
        OCA.Analytics.Navigation.favoriteUpdate(datasetId, isFavorite);
325
        document.querySelector('.app-navigation-entry-menu.open').classList.remove('open');
326
    },
327
328
    handleReportClicked: function (evt) {
329
        const datasetId = evt.target.closest('div').dataset.id;
330
        window.location = OC.generateUrl('apps/analytics/') + '#/r/' + datasetId;
331
        evt.stopPropagation();
332
    },
333
334
    handleGroupClicked: function (evt) {
335
        if (evt.target.parentNode.classList.contains('open')) {
336
            evt.target.parentNode.classList.remove('open');
337
        } else {
338
            evt.target.parentNode.classList.add('open');
339
        }
340
        evt.stopPropagation();
341
    },
342
343
    handleImportButton: function () {
344
        const mimeparts = ['text/csv', 'text/plain'];
345
        OC.dialogs.filepicker(t('analytics', 'Select file'), OCA.Analytics.Navigation.importDataset.bind(this), false, mimeparts, true, 1);
346
    },
347
348
    handleUnshareButton: function (evt) {
349
        let shareId = evt.target.parentNode.dataset.shareId;
350
351
        let xhr = new XMLHttpRequest();
352
        xhr.open('DELETE', OC.generateUrl('apps/analytics/share/' + shareId, true), true);
353
        xhr.setRequestHeader('requesttoken', OC.requestToken);
354
        xhr.setRequestHeader('OCS-APIREQUEST', 'true');
355
        xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
356
357
        xhr.onreadystatechange = function () {
358
            if (xhr.readyState === 4) {
359
                OCA.Analytics.Navigation.init();
360
            }
361
        };
362
363
        xhr.send();
364
    },
365
366
    importDataset: function (path, raw) {
367
        $.ajax({
368
            type: 'POST',
369
            url: OC.generateUrl('apps/analytics/dataset/import/'),
370
            data: {
371
                'path': path,
372
                'raw': raw
373
            },
374
            success: function () {
375
                OCA.Analytics.Navigation.init();
376
            }
377
        });
378
379
    },
380
381
    createDataset: function (file = '') {
382
        $.ajax({
383
            type: 'POST',
384
            url: OC.generateUrl('apps/analytics/dataset'),
385
            data: {
386
                'file': file,
387
            },
388
            success: function (data) {
389
                OCA.Analytics.Navigation.newReportId = data;
390
                OCA.Analytics.Navigation.init(data);
391
            }
392
        });
393
    },
394
395
    getDatasets: function (datasetId) {
396
        $.ajax({
397
            type: 'GET',
398
            url: OC.generateUrl('apps/analytics/dataset'),
399
            success: function (data) {
400
                OCA.Analytics.Navigation.buildNavigation(data);
401
                OCA.Analytics.datasets = data;
402
                if (datasetId) {
403
                    OCA.Analytics.Sidebar.hideSidebar();
404
                    let navigationItem = document.querySelector('#navigationDatasets [data-id="' + datasetId + '"]');
405
                    if (navigationItem.parentElement.parentElement.parentElement.classList.contains('collapsible')) {
406
                        navigationItem.parentElement.parentElement.parentElement.classList.add('open');
407
                    }
408
                    navigationItem.click();
409
                    if (datasetId === OCA.Analytics.Navigation.newReportId && document.getElementById('advanced').value !== 'true') {
410
                        document.querySelector('#navigationMenu[data-id="' + datasetId + '"] #navigationMenuEdit').click();
411
                    }
412
                }
413
            }
414
        });
415
    },
416
417
    favoriteUpdate: function (datasetId, isFavorite) {
418
        let params = 'favorite=' + isFavorite;
419
        let xhr = new XMLHttpRequest();
420
        xhr.open('POST', OC.generateUrl('apps/analytics/favorite/' + datasetId, true), true);
421
        xhr.setRequestHeader('requesttoken', OC.requestToken);
422
        xhr.setRequestHeader('OCS-APIREQUEST', 'true');
423
        xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
424
        xhr.send(params);
425
    },
426
};
427
428
document.addEventListener('DOMContentLoaded', function () {
429
    if (document.getElementById('advanced').value !== 'true') {
430
        OCA.Analytics.WhatsNew.whatsnew();
431
        document.getElementById('wizzartStart').addEventListener('click', OCA.Analytics.Wizzard.show);
432
        if (parseInt(document.getElementById('analyticsWizzard').value) === 0) {
433
            OCA.Analytics.Wizzard.show();
434
        }
435
    }
436
    document.getElementById('importDatasetButton').addEventListener('click', OCA.Analytics.Navigation.handleImportButton);
437
});