languageDisplay.ts ➔ doUpdateLanguageDisplay   F
last analyzed

Complexity

Conditions 14

Size

Total Lines 181
Code Lines 145

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 145
dl 0
loc 181
rs 2.52
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like languageDisplay.ts ➔ doUpdateLanguageDisplay 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
import { getGlobalConfiguration, SETTINGS_animeLanguageDisplay } from '../configuration/configuration';
2
import * as core from '../utils/aniwatchCore';
3
import * as helper from '../utils/helpers';
4
5
const MANIPULATED_ATTR_NAME = 'awpManipulated';
6
7
export function init(): void {
8
    getGlobalConfiguration().getProperty(SETTINGS_animeLanguageDisplay, value => {
9
        if (value) {
10
            core.registerScript((node: Node) => {
11
                // run the scripts
12
                if (node instanceof Element) {
13
                    updateLanguageDisplay(node)
14
                }
15
            }, "^/anime/[0-9]*$");
16
        }
17
    });
18
}
19
20
function updateLanguageDisplay(node: Element): void {
21
    const LIST_NODE_NAME = 'MD-LIST-ITEM';
22
    const BOX_NODE_NAME = 'DIV';
23
    const BOX_CLASS_NAME = 'card-margin';
24
25
    if (node.nodeName === LIST_NODE_NAME) {
26
        updateLanguageDisplayListMode(node);
27
    }
28
    else if (node.nodeName === BOX_NODE_NAME && node.classList.contains(BOX_CLASS_NAME)) {
29
        updateLanguageDisplayBoxMode(node);
30
    }
31
}
32
33
function updateLanguageDisplayListMode(node: Element): void {
34
    // last column with flags
35
    let col = node.querySelector('h3.layout-align-end-center');
36
37
    if (!helper.assigned(col) || col.hasAttribute(MANIPULATED_ATTR_NAME)) {
38
        return;
39
    }
40
41
    doUpdateLanguageDisplay(col, false);
42
}
43
44
function updateLanguageDisplayBoxMode(node: Element): void {
45
    // last column with flags
46
    let col = node.querySelector('div.layout-align-end-start');
47
48
    if (!helper.assigned(col) || col.hasAttribute(MANIPULATED_ATTR_NAME)) {
49
        return;
50
    }
51
52
    doUpdateLanguageDisplay(col, true);
53
}
54
55
56
function doUpdateLanguageDisplay(parent: Element, isBoxedModed: boolean): void {
57
    const LIST_LANG_PREFIX = 'ep.lang.';
58
    const BOX_LANG_PREFIX = 'episodeObject.lang.';
59
    // aniwatch uses different prefixes in list und box mode :/
60
    let realLangPrefix = isBoxedModed ? BOX_LANG_PREFIX : LIST_LANG_PREFIX;
61
62
    const DUB_SUFFIX = 'dub';
63
    const SUB_SUFFIX = 'sub';
64
65
    const DUB_ICON = 'volume_up';
66
    const SUB_ICON = 'closed_caption';
67
    const ZERO_WIDTH_SPACE_CHARACTER = ''; // ​
68
69
    let subs: Array<string> = [];
70
    let dubs: Array<string> = [];
71
72
    // find subs
73
    let subCols = parent.querySelectorAll('[ng-hide*="sub"]');
74
    subCols.forEach((element: Element) => {
75
        let langAttr = element.attributes['ng-hide'].value;
76
        let lang = langAttr.substring(langAttr.indexOf(realLangPrefix) + realLangPrefix.length, langAttr.indexOf(SUB_SUFFIX));
77
        if (element.attributes['aria-hidden'].value == 'false') {
78
            subs.push(lang);
79
        }
80
    });
81
82
    // find dubs
83
    let dubCols = parent.querySelectorAll('[ng-hide*="dub"]');
84
    dubCols.forEach((element: Element) => {
85
        let langAttr = element.attributes['ng-hide'].value;
86
        let lang = langAttr.substring(langAttr.indexOf(realLangPrefix) + realLangPrefix.length, langAttr.indexOf(DUB_SUFFIX));
87
        if (element.attributes['aria-hidden'].value == 'false') {
88
            dubs.push(lang);
89
        }
90
    });
91
92
    // build output html
93
    let iconsRequired = true;
94
    let cols = [];
95
96
    // subs first;
97
    if (subs.length > 0) {
98
        let colDiv = document.createElement('div');
99
        colDiv.setAttribute('layout', 'column');
100
        colDiv.classList.add('layout-column');
101
102
        // do we have dubs?
103
        if (dubs.length > 0) {
104
            let dubDiv = document.createElement('div');
105
            dubDiv.setAttribute('layout', 'row');
106
            dubDiv.setAttribute('layout-align', 'start center');
107
            dubDiv.classList.add('layout-align-start-center', 'layout-row');
108
109
            let dubIconDiv = document.createElement('i');
110
            if (iconsRequired) {
111
                dubIconDiv.classList.add('material-icons', 'mr-3');
112
                dubIconDiv.innerText = DUB_ICON;
113
            }
114
            // add dummy with 24px for correct presentation
115
            else {
116
                dubIconDiv.style.height = '24px';
117
                dubIconDiv.innerText = ZERO_WIDTH_SPACE_CHARACTER;
118
            }
119
120
            dubDiv.appendChild(dubIconDiv);
121
122
            let japIcon = document.createElement('i');
123
            japIcon.classList.add('flag', 'flag-jp', 'mg-all-1');
124
            dubDiv.appendChild(japIcon);
125
126
            colDiv.appendChild(dubDiv);
127
        }
128
129
        // do the subs
130
        let subDiv = document.createElement('div');
131
        subDiv.setAttribute('layout', 'row');
132
        subDiv.setAttribute('layout-align', 'start center');
133
        subDiv.classList.add('layout-align-start-center', 'layout-row');
134
135
        let subIconDiv = document.createElement('i');
136
        if (iconsRequired) {
137
            subIconDiv.classList.add('material-icons', 'mr-3');
138
            subIconDiv.innerText = SUB_ICON;
139
        }
140
        // add dummy with 24px for correct presentation
141
        else {
142
            subIconDiv.style.height = '24px';
143
            subIconDiv.innerText = ZERO_WIDTH_SPACE_CHARACTER;
144
        }
145
146
        subDiv.appendChild(subIconDiv);
147
        subs.forEach((lang: string) => {
148
            let langIcon = document.createElement('i');
149
            langIcon.classList.add('flag', `flag-${lang}`, 'mg-all-1');
150
            subDiv.appendChild(langIcon);
151
        });
152
153
        colDiv.appendChild(subDiv);
154
155
        cols.push(colDiv);
156
        iconsRequired = false;
157
    }
158
159
    if (dubs.length > 0) {
160
        dubs.forEach((lang: string) => {
161
            let colDiv = document.createElement('div');
162
            colDiv.setAttribute('layout', 'column');
163
            colDiv.classList.add('layout-column');
164
165
            let dubDiv = document.createElement('div');
166
            dubDiv.setAttribute('layout', 'row');
167
            dubDiv.setAttribute('layout-align', 'start center');
168
            dubDiv.classList.add('layout-align-start-center', 'layout-row');
169
170
            let dubIconDiv = document.createElement('i');
171
            if (iconsRequired) {
172
                dubIconDiv.classList.add('material-icons', 'mr-3');
173
                dubIconDiv.innerText = DUB_ICON;
174
            }
175
            // add dummy with 24px for correct presentation
176
            else {
177
                dubIconDiv.style.height = '24px';
178
                dubIconDiv.innerText = ZERO_WIDTH_SPACE_CHARACTER;
179
            }
180
181
            dubDiv.appendChild(dubIconDiv);
182
183
            let langIcon = document.createElement('i');
184
            langIcon.classList.add('flag', `flag-${lang}`, 'mg-all-1');
185
            dubDiv.appendChild(langIcon);
186
187
            colDiv.appendChild(dubDiv);
188
189
            // do we have subs?
190
            if (subs.length > 0) {
191
                let subDiv = document.createElement('div');
192
                subDiv.setAttribute('layout', 'row');
193
                subDiv.setAttribute('layout-align', 'start center');
194
                subDiv.classList.add('layout-align-start-center', 'layout-row');
195
196
                let subIconDiv = document.createElement('i');
197
                if (iconsRequired) {
198
                    subIconDiv.classList.add('material-icons', 'mr-3');
199
                    subIconDiv.innerText = SUB_ICON;
200
                }
201
                // add dummy with 24px for correct presentation
202
                else {
203
                    subIconDiv.style.height = '24px';
204
                    subIconDiv.innerText = ZERO_WIDTH_SPACE_CHARACTER;
205
                }
206
207
                subDiv.appendChild(subIconDiv);
208
                colDiv.appendChild(subDiv);
209
            }
210
211
            cols.push(colDiv);
212
            iconsRequired = false;
213
        });
214
    }
215
216
    parent.innerHTML = '';
217
    cols.forEach(div => {
218
        parent.appendChild(div);
219
    });
220
221
    parent.querySelectorAll('.layout-column:not(:last-child)').forEach(div => {
222
        if (div instanceof HTMLElement) {
223
            div.style.borderRight = '1px solid rgba(155,155,155, 0.2)';
224
        }
225
    })
226
227
    parent.querySelectorAll('.layout-column').forEach(div => {
228
        if (div instanceof HTMLElement) {
229
            div.style.paddingLeft = '2px';
230
            div.style.paddingRight = '2px';
231
        }
232
    })
233
234
    parent.setAttribute('awpManipulated', String(true));
235
}
236