js/script.js   F
last analyzed

Complexity

Total Complexity 97
Complexity/F 2.94

Size

Lines of Code 590
Function Count 33

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 0
dl 0
loc 590
rs 3.12
c 0
b 0
f 0
wmc 97
mnd 3
bc 81
fnc 33
bpm 2.4545
cpm 2.9392
noi 0

2 Functions

Rating   Name   Duplication   Size   Complexity  
A script.js ➔ define 0 3 1
B script.js ➔ ?!? 0 569 1

How to fix   Complexity   

Complexity

Complex classes like js/script.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
 * Waves v0.7.5
3
 * http://fian.my.id/Waves
4
 *
5
 * Copyright 2014-2016 Alfiana E. Sibuea and other contributors
6
 * Released under the MIT license
7
 * https://github.com/fians/Waves/blob/master/LICENSE
8
 */
9
10
;(function(window, factory) {
11
    'use strict';
12
13
    // AMD. Register as an anonymous module.  Wrap in function so we have access
14
    // to root via `this`.
15
    if (typeof define === 'function' && define.amd) {
16
        define([], function() {
17
            return factory.apply(window);
18
        });
19
    }
20
21
    // Node. Does not work with strict CommonJS, but only CommonJS-like
22
    // environments that support module.exports, like Node.
23
    else if (typeof exports === 'object') {
24
        module.exports = factory.call(window);
25
    }
26
27
    // Browser globals.
28
    else {
29
        window.Waves = factory.call(window);
30
    }
31
})(typeof global === 'object' ? global : this, function() {
32
    'use strict';
33
34
    var Waves            = Waves || {};
35
    var $$               = document.querySelectorAll.bind(document);
36
    var toString         = Object.prototype.toString;
37
    var isTouchAvailable = 'ontouchstart' in window;
38
39
40
    // Find exact position of element
41
    function isWindow(obj) {
42
        return obj !== null && obj === obj.window;
43
    }
44
45
    function getWindow(elem) {
46
        return isWindow(elem) ? elem : elem.nodeType === 9 && elem.defaultView;
47
    }
48
49
    function isObject(value) {
50
        var type = typeof value;
51
        return type === 'function' || type === 'object' && !!value;
52
    }
53
54
    function isDOMNode(obj) {
55
        return isObject(obj) && obj.nodeType > 0;
56
    }
57
58
    function getWavesElements(nodes) {
59
        var stringRepr = toString.call(nodes);
60
61
        if (stringRepr === '[object String]') {
62
            return $$(nodes);
63
        } else if (isObject(nodes) && /^\[object (Array|HTMLCollection|NodeList|Object)\]$/.test(stringRepr) && nodes.hasOwnProperty('length')) {
64
            return nodes;
65
        } else if (isDOMNode(nodes)) {
66
            return [nodes];
67
        }
68
69
        return [];
70
    }
71
72
    function offset(elem) {
73
        var docElem, win,
74
            box = { top: 0, left: 0 },
75
            doc = elem && elem.ownerDocument;
76
77
        docElem = doc.documentElement;
78
79
        if (typeof elem.getBoundingClientRect !== typeof undefined) {
80
            box = elem.getBoundingClientRect();
81
        }
82
        win = getWindow(doc);
83
        return {
84
            top: box.top + win.pageYOffset - docElem.clientTop,
85
            left: box.left + win.pageXOffset - docElem.clientLeft
86
        };
87
    }
88
89
    function convertStyle(styleObj) {
90
        var style = '';
91
92
        for (var prop in styleObj) {
93
            if (styleObj.hasOwnProperty(prop)) {
94
                style += (prop + ':' + styleObj[prop] + ';');
95
            }
96
        }
97
98
        return style;
99
    }
100
101
    var Effect = {
102
103
        // Effect duration
104
        duration: 750,
105
106
        // Effect delay (check for scroll before showing effect)
107
        delay: 200,
108
109
        show: function(e, element, velocity) {
110
111
            // Disable right click
112
            if (e.button === 2) {
113
                return false;
114
            }
115
116
            element = element || this;
117
118
            // Create ripple
119
            var ripple = document.createElement('div');
120
            ripple.className = 'waves-ripple waves-rippling';
121
            element.appendChild(ripple);
122
123
            // Get click coordinate and element width
124
            var pos       = offset(element);
125
            var relativeY = 0;
126
            var relativeX = 0;
127
            // Support for touch devices
128
            if('touches' in e && e.touches.length) {
129
                relativeY   = (e.touches[0].pageY - pos.top);
130
                relativeX   = (e.touches[0].pageX - pos.left);
131
            }
132
            //Normal case
133
            else {
134
                relativeY   = (e.pageY - pos.top);
135
                relativeX   = (e.pageX - pos.left);
136
            }
137
            // Support for synthetic events
138
            relativeX = relativeX >= 0 ? relativeX : 0;
139
            relativeY = relativeY >= 0 ? relativeY : 0;
140
141
            var scale     = 'scale(' + ((element.clientWidth / 100) * 3) + ')';
142
            var translate = 'translate(0,0)';
143
144
            if (velocity) {
145
                translate = 'translate(' + (velocity.x) + 'px, ' + (velocity.y) + 'px)';
146
            }
147
148
            // Attach data to element
149
            ripple.setAttribute('data-hold', Date.now());
150
            ripple.setAttribute('data-x', relativeX);
151
            ripple.setAttribute('data-y', relativeY);
152
            ripple.setAttribute('data-scale', scale);
153
            ripple.setAttribute('data-translate', translate);
154
155
            // Set ripple position
156
            var rippleStyle = {
157
                top: relativeY + 'px',
158
                left: relativeX + 'px'
159
            };
160
161
            ripple.classList.add('waves-notransition');
162
            ripple.setAttribute('style', convertStyle(rippleStyle));
163
            ripple.classList.remove('waves-notransition');
164
165
            // Scale the ripple
166
            rippleStyle['-webkit-transform'] = scale + ' ' + translate;
167
            rippleStyle['-moz-transform'] = scale + ' ' + translate;
168
            rippleStyle['-ms-transform'] = scale + ' ' + translate;
169
            rippleStyle['-o-transform'] = scale + ' ' + translate;
170
            rippleStyle.transform = scale + ' ' + translate;
171
            rippleStyle.opacity = '1';
172
173
            var duration = e.type === 'mousemove' ? 2500 : Effect.duration;
174
            rippleStyle['-webkit-transition-duration'] = duration + 'ms';
175
            rippleStyle['-moz-transition-duration']    = duration + 'ms';
176
            rippleStyle['-o-transition-duration']      = duration + 'ms';
177
            rippleStyle['transition-duration']         = duration + 'ms';
178
179
            ripple.setAttribute('style', convertStyle(rippleStyle));
180
        },
181
182
        hide: function(e, element) {
183
            element = element || this;
184
185
            var ripples = element.getElementsByClassName('waves-rippling');
186
187
            for (var i = 0, len = ripples.length; i < len; i++) {
188
                removeRipple(e, element, ripples[i]);
189
            }
190
191
            if (isTouchAvailable) {
192
                element.removeEventListener('touchend', Effect.hide);
193
                element.removeEventListener('touchcancel', Effect.hide);
194
            }
195
196
            element.removeEventListener('mouseup', Effect.hide);
197
            element.removeEventListener('mouseleave', Effect.hide);
198
        }
199
    };
200
201
    /**
202
     * Collection of wrapper for HTML element that only have single tag
203
     * like <input> and <img>
204
     */
205
    var TagWrapper = {
206
207
        // Wrap <input> tag so it can perform the effect
208
        input: function(element) {
209
210
            var parent = element.parentNode;
211
212
            // If input already have parent just pass through
213
            if (parent.tagName.toLowerCase() === 'i' && parent.classList.contains('waves-effect')) {
214
                return;
215
            }
216
217
            // Put element class and style to the specified parent
218
            var wrapper       = document.createElement('i');
219
            wrapper.className = element.className + ' waves-input-wrapper';
220
            element.className = 'waves-button-input';
221
222
            // Put element as child
223
            parent.replaceChild(wrapper, element);
224
            wrapper.appendChild(element);
225
226
            // Apply element color and background color to wrapper
227
            var elementStyle    = window.getComputedStyle(element, null);
228
            var color           = elementStyle.color;
229
            var backgroundColor = elementStyle.backgroundColor;
230
231
            wrapper.setAttribute('style', 'color:' + color + ';background:' + backgroundColor);
232
            element.setAttribute('style', 'background-color:rgba(0,0,0,0);');
233
234
        },
235
236
        // Wrap <img> tag so it can perform the effect
237
        img: function(element) {
238
239
            var parent = element.parentNode;
240
241
            // If input already have parent just pass through
242
            if (parent.tagName.toLowerCase() === 'i' && parent.classList.contains('waves-effect')) {
243
                return;
244
            }
245
246
            // Put element as child
247
            var wrapper  = document.createElement('i');
248
            parent.replaceChild(wrapper, element);
249
            wrapper.appendChild(element);
250
251
        }
252
    };
253
254
    /**
255
     * Hide the effect and remove the ripple. Must be
256
     * a separate function to pass the JSLint...
257
     */
258
    function removeRipple(e, el, ripple) {
259
260
        // Check if the ripple still exist
261
        if (!ripple) {
262
            return;
263
        }
264
265
        ripple.classList.remove('waves-rippling');
266
267
        var relativeX = ripple.getAttribute('data-x');
268
        var relativeY = ripple.getAttribute('data-y');
269
        var scale     = ripple.getAttribute('data-scale');
270
        var translate = ripple.getAttribute('data-translate');
271
272
        // Get delay beetween mousedown and mouse leave
273
        var diff = Date.now() - Number(ripple.getAttribute('data-hold'));
274
        var delay = 350 - diff;
275
276
        if (delay < 0) {
277
            delay = 0;
278
        }
279
280
        if (e.type === 'mousemove') {
281
            delay = 150;
282
        }
283
284
        // Fade out ripple after delay
285
        var duration = e.type === 'mousemove' ? 2500 : Effect.duration;
286
287
        setTimeout(function() {
288
289
            var style = {
290
                top: relativeY + 'px',
291
                left: relativeX + 'px',
292
                opacity: '0',
293
294
                // Duration
295
                '-webkit-transition-duration': duration + 'ms',
296
                '-moz-transition-duration': duration + 'ms',
297
                '-o-transition-duration': duration + 'ms',
298
                'transition-duration': duration + 'ms',
299
                '-webkit-transform': scale + ' ' + translate,
300
                '-moz-transform': scale + ' ' + translate,
301
                '-ms-transform': scale + ' ' + translate,
302
                '-o-transform': scale + ' ' + translate,
303
                'transform': scale + ' ' + translate
304
            };
305
306
            ripple.setAttribute('style', convertStyle(style));
307
308
            setTimeout(function() {
309
                try {
310
                    el.removeChild(ripple);
311
                } catch (e) {
312
                    return false;
313
                }
314
            }, duration);
315
316
        }, delay);
317
    }
318
319
320
    /**
321
     * Disable mousedown event for 500ms during and after touch
322
     */
323
    var TouchHandler = {
324
325
        /* uses an integer rather than bool so there's no issues with
326
         * needing to clear timeouts if another touch event occurred
327
         * within the 500ms. Cannot mouseup between touchstart and
328
         * touchend, nor in the 500ms after touchend. */
329
        touches: 0,
330
331
        allowEvent: function(e) {
332
333
            var allow = true;
334
335
            if (/^(mousedown|mousemove)$/.test(e.type) && TouchHandler.touches) {
336
                allow = false;
337
            }
338
339
            return allow;
340
        },
341
        registerEvent: function(e) {
342
            var eType = e.type;
343
344
            if (eType === 'touchstart') {
345
346
                TouchHandler.touches += 1; // push
347
348
            } else if (/^(touchend|touchcancel)$/.test(eType)) {
349
350
                setTimeout(function() {
351
                    if (TouchHandler.touches) {
352
                        TouchHandler.touches -= 1; // pop after 500ms
353
                    }
354
                }, 500);
355
356
            }
357
        }
358
    };
359
360
361
    /**
362
     * Delegated click handler for .waves-effect element.
363
     * returns null when .waves-effect element not in "click tree"
364
     */
365
    function getWavesEffectElement(e) {
366
367
        if (TouchHandler.allowEvent(e) === false) {
368
            return null;
369
        }
370
371
        var element = null;
372
        var target = e.target || e.srcElement;
373
374
        while (target.parentElement) {
375
            if ( (!(target instanceof SVGElement)) && target.classList.contains('waves-effect')) {
376
                element = target;
377
                break;
378
            }
379
            target = target.parentElement;
380
        }
381
382
        return element;
383
    }
384
385
    /**
386
     * Bubble the click and show effect if .waves-effect elem was found
387
     */
388
    function showEffect(e) {
389
390
        // Disable effect if element has "disabled" property on it
391
        // In some cases, the event is not triggered by the current element
392
        // if (e.target.getAttribute('disabled') !== null) {
393
        //     return;
394
        // }
395
396
        var element = getWavesEffectElement(e);
397
398
        if (element !== null) {
399
400
            // Make it sure the element has either disabled property, disabled attribute or 'disabled' class
401
            if (element.disabled || element.getAttribute('disabled') || element.classList.contains('disabled')) {
402
                return;
403
            }
404
405
            TouchHandler.registerEvent(e);
406
407
            if (e.type === 'touchstart' && Effect.delay) {
408
409
                var hidden = false;
410
411
                var timer = setTimeout(function () {
412
                    timer = null;
413
                    Effect.show(e, element);
414
                }, Effect.delay);
415
416
                var hideEffect = function(hideEvent) {
417
418
                    // if touch hasn't moved, and effect not yet started: start effect now
419
                    if (timer) {
420
                        clearTimeout(timer);
421
                        timer = null;
422
                        Effect.show(e, element);
423
                    }
424
                    if (!hidden) {
425
                        hidden = true;
426
                        Effect.hide(hideEvent, element);
427
                    }
428
429
                    removeListeners();
430
                };
431
432
                var touchMove = function(moveEvent) {
433
                    if (timer) {
434
                        clearTimeout(timer);
435
                        timer = null;
436
                    }
437
                    hideEffect(moveEvent);
438
439
                    removeListeners();
440
                };
441
442
                element.addEventListener('touchmove', touchMove, false);
443
                element.addEventListener('touchend', hideEffect, false);
444
                element.addEventListener('touchcancel', hideEffect, false);
445
446
                var removeListeners = function() {
447
                    element.removeEventListener('touchmove', touchMove);
448
                    element.removeEventListener('touchend', hideEffect);
449
                    element.removeEventListener('touchcancel', hideEffect);
450
                };
451
            } else {
452
453
                Effect.show(e, element);
454
455
                if (isTouchAvailable) {
456
                    element.addEventListener('touchend', Effect.hide, false);
457
                    element.addEventListener('touchcancel', Effect.hide, false);
458
                }
459
460
                element.addEventListener('mouseup', Effect.hide, false);
461
                element.addEventListener('mouseleave', Effect.hide, false);
462
            }
463
        }
464
    }
465
466
    Waves.init = function(options) {
467
        var body = document.body;
468
469
        options = options || {};
470
471
        if ('duration' in options) {
472
            Effect.duration = options.duration;
473
        }
474
475
        if ('delay' in options) {
476
            Effect.delay = options.delay;
477
        }
478
479
        if (isTouchAvailable) {
480
            body.addEventListener('touchstart', showEffect, false);
481
            body.addEventListener('touchcancel', TouchHandler.registerEvent, false);
482
            body.addEventListener('touchend', TouchHandler.registerEvent, false);
483
        }
484
485
        body.addEventListener('mousedown', showEffect, false);
486
    };
487
488
489
    /**
490
     * Attach Waves to dynamically loaded inputs, or add .waves-effect and other
491
     * waves classes to a set of elements. Set drag to true if the ripple mouseover
492
     * or skimming effect should be applied to the elements.
493
     */
494
    Waves.attach = function(elements, classes) {
495
496
        elements = getWavesElements(elements);
497
498
        if (toString.call(classes) === '[object Array]') {
499
            classes = classes.join(' ');
500
        }
501
502
        classes = classes ? ' ' + classes : '';
503
504
        var element, tagName;
505
506
        for (var i = 0, len = elements.length; i < len; i++) {
507
508
            element = elements[i];
509
            tagName = element.tagName.toLowerCase();
510
511
            if (['input', 'img'].indexOf(tagName) !== -1) {
512
                TagWrapper[tagName](element);
513
                element = element.parentElement;
514
            }
515
516
            if (element.className.indexOf('waves-effect') === -1) {
517
                element.className += ' waves-effect' + classes;
518
            }
519
        }
520
    };
521
522
523
    /**
524
     * Cause a ripple to appear in an element via code.
525
     */
526
    Waves.ripple = function(elements, options) {
527
        elements = getWavesElements(elements);
528
        var elementsLen = elements.length;
529
530
        options          = options || {};
531
        options.wait     = options.wait || 0;
532
        options.position = options.position || null; // default = centre of element
533
534
535
        if (elementsLen) {
536
            var element, pos, off, centre = {}, i = 0;
537
            var mousedown = {
538
                type: 'mousedown',
539
                button: 1
540
            };
541
            var hideRipple = function(mouseup, element) {
542
                return function() {
543
                    Effect.hide(mouseup, element);
544
                };
545
            };
546
547
            for (; i < elementsLen; i++) {
548
                element = elements[i];
549
                pos = options.position || {
550
                    x: element.clientWidth / 2,
551
                    y: element.clientHeight / 2
552
                };
553
554
                off      = offset(element);
555
                centre.x = off.left + pos.x;
556
                centre.y = off.top + pos.y;
557
558
                mousedown.pageX = centre.x;
559
                mousedown.pageY = centre.y;
560
561
                Effect.show(mousedown, element);
562
563
                if (options.wait >= 0 && options.wait !== null) {
564
                    var mouseup = {
565
                        type: 'mouseup',
566
                        button: 1
567
                    };
568
569
                    setTimeout(hideRipple(mouseup, element), options.wait);
570
                }
571
            }
572
        }
573
    };
574
575
    /**
576
     * Remove all ripples from an element.
577
     */
578
    Waves.calm = function(elements) {
579
        elements = getWavesElements(elements);
580
        var mouseup = {
581
            type: 'mouseup',
582
            button: 1
583
        };
584
585
        for (var i = 0, len = elements.length; i < len; i++) {
586
            Effect.hide(mouseup, elements[i]);
587
        }
588
    };
589
590
    /**
591
     * Deprecated API fallback
592
     */
593
    Waves.displayEffect = function(options) {
594
        console.error('Waves.displayEffect() has been deprecated and will be removed in future version. Please use Waves.init() to initialize Waves effect');
595
        Waves.init(options);
596
    };
597
598
    return Waves;
599
});
600