Passed
Push — master ( 09e0b7...d4947d )
by Marcel
02:30
created

js/navigation.js   B

Complexity

Total Complexity 52
Complexity/F 2.26

Size

Lines of Code 371
Function Count 23

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

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