Passed
Push — master ( 6ff3f7...f2d233 )
by Marcel
02:38
created

js/navigation.js   C

Complexity

Total Complexity 55
Complexity/F 2.39

Size

Lines of Code 387
Function Count 23

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 277
c 0
b 0
f 0
dl 0
loc 387
rs 6
wmc 55
mnd 32
bc 32
fnc 23
bpm 1.3913
cpm 2.3913
noi 1

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