src/helpers.js   F
last analyzed

Complexity

Total Complexity 75
Complexity/F 1.56

Size

Lines of Code 385
Function Count 48

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 210
dl 0
loc 385
rs 2.4
c 0
b 0
f 0
wmc 75
mnd 27
bc 27
fnc 48
bpm 0.5625
cpm 1.5625
noi 10

11 Functions

Rating   Name   Duplication   Size   Complexity  
A helpers.js ➔ readableEmail 0 9 3
A helpers.js ➔ rot13ToText 0 7 4
F helpers.js ➔ uncloakLinks 0 77 14
A helpers.js ➔ convertFormFromRot13 0 12 2
A helpers.js ➔ imgLazyLoad 0 23 5
A helpers.js ➔ convertShortchutForLink 0 12 4
A helpers.js ➔ responsiveImage 0 17 5
A helpers.js ➔ testWebPSupport 0 9 2
A helpers.js ➔ seasonedBackground 0 14 5
B helpers.js ➔ replaceOn 0 34 6
A helpers.js ➔ convertImageLinkToWebPLink 0 11 4

How to fix   Complexity   

Complexity

Complex classes like src/helpers.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
 * List of all functions
3
 *
4
 * - liveBlock(attr)
5
 * - liveForm(attr)
6
 *
7
 * -  seasonedBackground
8
 * - responsiveImage(string)    Relative to Liip filters
9
 * - uncloakLinks(attr)
10
 * - convertFormFromRot13(attr)
11
 * - readableEmail(attr)
12
 * - convertImageLinkToWebPLink()
13
 */
14
15
/**
16
 * Live Block Watcher (and button)
17
 *
18
 * Fetch (ajax) function permitting to get block via a POST request
19
 *
20
 * @param {string} attribute
21
 */
22
export function liveBlock(
23
  liveBlockAttribute = 'data-live',
24
  liveFormSelector = '.live-form'
25
) {
26
  var btnToBlock = function (event, btn) {
27
    btn.setAttribute(
28
      liveBlockAttribute,
29
      btn.getAttribute('src-' + liveBlockAttribute)
30
    );
31
    getLiveBlock(btn);
32
  };
33
34
  var getLiveBlock = function (item) {
35
    fetch(item.getAttribute(liveBlockAttribute), {
36
      //headers: { "Content-Type": "application/json", Accept: "text/plain" },
37
      method: 'POST',
38
      credentials: 'include',
39
    })
40
      .then(function (response) {
41
        return response.text();
42
      })
43
      .then(function (body) {
44
        item.removeAttribute(liveBlockAttribute);
45
        item.outerHTML = body;
46
      })
47
      .then(function () {
48
        document.dispatchEvent(new Event('DOMChanged'));
0 ignored issues
show
Bug introduced by
The variable Event seems to be never declared. If this is a global, consider adding a /** global: Event */ 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...
49
      });
50
  };
51
52
  var htmlLoader =
53
    '<div style="width:1em;height:1em;border: 2px solid #222;border-top-color: #fff;border-radius: 50%;  animation: 1s spin linear infinite;"></div><style>@keyframes spin {from{transform:rotate(0deg)}to{transform:rotate(360deg)}}</style>';
54
55
  var setLoader = function (form) {
56
    var $submitButton = getSubmitButton(form);
57
    if ($submitButton !== undefined) {
58
      var initialButton = $submitButton.outerHTML;
0 ignored issues
show
Unused Code introduced by
The variable initialButton seems to be never used. Consider removing it.
Loading history...
59
      $submitButton.innerHTML = '';
60
      $submitButton.outerHTML = htmlLoader;
61
    }
62
  };
63
64
  var sendForm = function (form, liveFormBlock) {
65
    setLoader(form);
66
67
    var formData = new FormData(form.srcElement);
68
    fetch(form.srcElement.action, {
69
      method: 'POST',
70
      body: formData,
71
      credentials: 'include',
72
    })
73
      .then(function (response) {
74
        return response.text();
75
      })
76
      .then(function (body) {
77
        liveFormBlock.outerHTML = body;
78
      })
79
      .then(function () {
80
        document.dispatchEvent(new Event('DOMChanged'));
0 ignored issues
show
Bug introduced by
The variable Event seems to be never declared. If this is a global, consider adding a /** global: Event */ 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...
81
      });
82
  };
83
84
  var getSubmitButton = function (form) {
85
    if (form.srcElement.querySelector('[type=submit]') !== null) {
86
      return form.srcElement.querySelector('[type=submit]');
87
    }
88
    if (form.srcElement.getElementsByTagName('button') !== null) {
89
      return form.srcElement.getElementsByTagName('button')[0];
90
    }
91
    return null;
92
  };
93
94
  // Listen data-live
95
  document.querySelectorAll('[' + liveBlockAttribute + ']').forEach((item) => {
96
    getLiveBlock(item);
97
  });
98
99
  // Listen button src-data-live
100
  document
101
    .querySelectorAll('[src-' + liveBlockAttribute + ']')
102
    .forEach((item) => {
103
      item.addEventListener('click', (event) => {
104
        btnToBlock(event, item);
105
      });
106
    });
107
108
  // Listen live-form
109
  document.querySelectorAll(liveFormSelector).forEach((item) => {
110
    if (item.querySelector('form') !== null) {
111
      item.querySelector('form').addEventListener('submit', (e) => {
112
        e.preventDefault();
113
        sendForm(e, item);
114
      });
115
    }
116
  });
117
}
118
119
/**
120
 * Block to replace Watcher
121
 * On $event on element find via $attribute, set attribute's content in element.innerHTML
122
 */
123
export function replaceOn(attribute = 'replaceBy', eventName = 'click') {
124
  var loadVideo = function (element) {
125
    var content = element.getAttribute(attribute);
126
    if (
127
      element.classList.contains('hero-banner-overlay-lg') &&
128
      element.querySelector('picture') &&
129
      window.innerWidth < 992
130
    ) {
131
      element.querySelector('picture').outerHTML = content;
132
      element.querySelector('.btn-play').outerHTML = ' ';
133
    } else {
134
      element.innerHTML = content;
135
    }
136
    if (element.classList.contains('hero-banner-overlay-lg')) {
137
      element.style.zIndex = '2000';
138
    }
139
    element.removeAttribute(attribute);
140
    document.dispatchEvent(new Event('DOMChanged'));
0 ignored issues
show
Bug introduced by
The variable Event seems to be never declared. If this is a global, consider adding a /** global: Event */ 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...
141
  };
142
143
  document
144
    .querySelectorAll('[' + attribute + ']:not([listen])')
145
    .forEach(function (element) {
146
      element.setAttribute('listen', '');
147
      element.addEventListener(
148
        eventName,
149
        function (event) {
150
          loadVideo(event.currentTarget); //event.currentTarget;
151
          element.removeAttribute('listen');
152
        },
153
        { once: true }
154
      );
155
    });
156
}
157
158
/**
159
 *
160
 *
161
 */
162
export function seasonedBackground() {
163
  document.querySelectorAll('[x-hash]').forEach(function (element) {
164
    if (window.location.hash) {
165
      if (element.getAttribute('x-hash') == window.location.hash.substring(1)) {
166
        element.parentNode.parentNode
167
          .querySelectorAll('img')
168
          .forEach(function (img) {
169
            img.style = 'display:none';
170
          });
171
        element.style = 'display:block';
172
      }
173
    }
174
  });
175
}
176
177
/**
178
 * Transform image's path (src) produce with Liip to responsive path
179
 *
180
 * @param {string} src
181
 */
182
export function responsiveImage(src) {
183
  var screenWidth = window.innerWidth;
184
  if (screenWidth <= 576) {
185
    src = src.replace('/default/', '/xs/');
186
  } else if (screenWidth <= 768) {
187
    src = src.replace('/default/', '/sm/');
188
  } else if (screenWidth <= 992) {
189
    src = src.replace('/default/', '/md/');
190
  } else if (screenWidth <= 1200) {
191
    src = src.replace('/default/', '/lg/');
192
  } else {
193
    // 1200+
194
    src = src.replace('/default/', '/xl/');
195
  }
196
197
  return src;
198
}
199
200
/**
201
 * Convert elements wich contain attribute (data-href) in normal link (a href)
202
 * You can use a callback function to decrypt the link (eg: rot13ToText ;-))
203
 *
204
 * @param {string}  attribute
205
 */
206
export async function uncloakLinks(attribute = 'data-rot') {
207
  var convertLink = function (element) {
208
    // fix "bug" with img
209
    if (element.getAttribute(attribute) === null) {
210
      var element = element.closest('[' + attribute + ']');
211
    }
212
    if (element.getAttribute(attribute) === null) return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
213
    var link = document.createElement('a');
214
    var href = element.getAttribute(attribute);
215
    element.removeAttribute(attribute);
216
    for (var i = 0, n = element.attributes.length; i < n; i++) {
217
      link.setAttribute(
218
        element.attributes[i].nodeName,
219
        element.attributes[i].nodeValue
220
      );
221
    }
222
    link.innerHTML = element.innerHTML;
223
    link.setAttribute(
224
      'href',
225
      responsiveImage(convertShortchutForLink(rot13ToText(href)))
226
    );
227
    element.parentNode.replaceChild(link, element);
228
    return link;
229
  };
230
231
  var convertThemAll = function (attribute) {
232
    [].forEach.call(document.querySelectorAll('[' + attribute + ']'), function (
233
      element
234
    ) {
235
      convertLink(element);
236
    });
237
  };
238
239
  var fireEventLinksBuilt = async function (element, event) {
240
    await document.dispatchEvent(new Event('DOMChanged'));
0 ignored issues
show
Bug introduced by
The variable Event seems to be never declared. If this is a global, consider adding a /** global: Event */ 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...
241
242
    var clickEvent = new Event(event.type);
243
    element.dispatchEvent(clickEvent);
244
  };
245
246
  var convertLinkOnEvent = async function (event) {
247
    // convert them all if it's an image (thanks this bug), permit to use gallery (baguetteBox)
248
    if (event.target.tagName == 'IMG') {
249
      await convertThemAll(attribute);
250
      var element = event.target;
251
    } else {
252
      var element = convertLink(event.target);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable element already seems to be declared on line 250. 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...
253
    }
254
    fireEventLinksBuilt(element, event);
255
  };
256
257
  [].forEach.call(document.querySelectorAll('[' + attribute + ']'), function (
258
    element
259
  ) {
260
    element.addEventListener(
261
      'touchstart',
262
      function (e) {
263
        convertLinkOnEvent(e);
264
      },
265
      { once: true, passive: true }
266
    );
267
    element.addEventListener(
268
      'click',
269
      function (e) {
270
        convertLinkOnEvent(e);
271
      },
272
      { once: true }
273
    );
274
    element.addEventListener(
275
      'mouseover',
276
      function (e) {
277
        convertLinkOnEvent(e);
278
      },
279
      { once: true }
280
    );
281
  });
282
}
283
284
/**
285
 * Convert action attr encoded in rot 13 to normal action with default attr `data-frot`
286
 *
287
 * @param {string}  attribute
288
 */
289
export function convertFormFromRot13(attribute = 'data-frot') {
290
  [].forEach.call(document.querySelectorAll('[' + attribute + ']'), function (
291
    element
292
  ) {
293
    var action = element.getAttribute(attribute);
294
    element.removeAttribute(attribute);
295
    element.setAttribute(
296
      'action',
297
      convertShortchutForLink(rot13ToText(action))
298
    );
299
  });
300
}
301
302
export function convertShortchutForLink(str) {
303
  if (str.charAt(0) == '-') {
304
    return str.replace('-', 'http://');
305
  }
306
  if (str.charAt(0) == '_') {
307
    return str.replace('_', 'https://');
308
  }
309
  if (str.charAt(0) == '@') {
310
    return str.replace('@', 'mailto:');
311
  }
312
  return str;
313
}
314
315
/**
316
 * readableEmail(selector) Transform an email encoded with rot13 in a readable mail (and add mailto:)
317
 *
318
 * @param {string}  text
319
 */
320
export function readableEmail(selector) {
321
  document.querySelectorAll(selector).forEach(function (item) {
322
    var mail = rot13ToText(item.textContent);
323
    item.innerHTML = '<a href="mailto:' + mail + '">' + mail + '</a>';
324
    if (selector.charAt(0) == '.') {
325
      item.classList.remove(selector.substring(1));
326
    }
327
  });
328
}
329
330
/**
331
 * Decode rot13
332
 *
333
 * @param {string}  str
334
 */
335
export function rot13ToText(str) {
336
  return str.replace(/[a-zA-Z]/g, function (c) {
337
    return String.fromCharCode(
338
      (c <= 'Z' ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26
339
    );
340
  });
341
}
342
343
export function testWebPSupport() {
344
  var elem = document.createElement('canvas');
345
346
  if (elem.getContext && elem.getContext('2d')) {
347
    return elem.toDataURL('image/webp').indexOf('data:image/webp') == 0;
0 ignored issues
show
Best Practice introduced by
Comparing elem.toDataURL("image/we...exOf("data:image/webp") to 0 using the == operator is not safe. Consider using === instead.
Loading history...
348
  }
349
350
  return false;
351
}
352
353
/**
354
 * Used in ThemeComponent
355
 */
356
export function convertImageLinkToWebPLink() {
357
  var switchToWebP = function () {
358
    [].forEach.call(document.querySelectorAll('a[dwl]'), function (element) {
359
      var href = responsiveImage(element.getAttribute('dwl'));
360
      element.setAttribute('href', href);
361
      element.removeAttribute('dwl');
362
    });
363
  };
364
365
  if (testWebPSupport()) switchToWebP();
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
366
}
367
368
/**
369
 * Simple Image Lazy Loader
370
 * original from : https://davidwalsh.name/lazyload-image-fade
371
 *
372
 * @param {string}  attribute
373
 *
374
 * @example
375
 * imgLazyLoad()
376
 * <span data-img=/img/me.png>Tagada</span> or <img data-img=/img/me.png alt=Tagada>
377
 *
378
 * will be converted to
379
 *
380
 * <img src=/img/me.png alt=Tagada />
381
 *
382
 * still used in piedvert. To remove ?!
383
 */
384
export function imgLazyLoad(attribute = 'data-img') {
385
  [].forEach.call(document.querySelectorAll('[' + attribute + ']'), function (
386
    img
387
  ) {
388
    var newDomImg = document.createElement('img');
389
    var src = img.getAttribute(attribute);
390
    img.removeAttribute(attribute);
391
    for (var i = 0, n = img.attributes.length; i < n; i++) {
392
      newDomImg.setAttribute(
393
        img.attributes[i].nodeName,
394
        img.attributes[i].nodeValue
395
      );
396
    }
397
    if (newDomImg.getAttribute('alt') === null && img.textContent != '') {
398
      newDomImg.setAttribute('alt', img.textContent);
399
    }
400
    newDomImg.setAttribute(
401
      'src',
402
      typeof responsiveImage === 'function' ? responsiveImage(src) : src
403
    );
404
    img.outerHTML = newDomImg.outerHTML;
405
  });
406
}
407