Completed
Push — master ( 3a5d30...efd5c6 )
by Jeroen De
16s
created

skins/cat17/src/app/lib/scrolling.js   A

Complexity

Total Complexity 23
Complexity/F 1.77

Size

Lines of Code 130
Function Count 13

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 0
wmc 23
c 2
b 0
f 0
nc 1
mnd 1
bc 21
fnc 13
dl 0
loc 130
rs 10
bpm 1.6153
cpm 1.7692
noi 3

10 Functions

Rating   Name   Duplication   Size   Complexity  
A scrolling.js ➔ calculateElementMargin 0 8 2
A AnimatedScroller.scrollTo 0 16 1
A scrolling.js ➔ calculateElementPadding 0 8 2
A scrolling.js ➔ calculateElementOffset 0 11 3
A scrolling.js ➔ calculateFixedHeaderElementHeight 0 9 1
A LinkScroller.linkIsInsideCompletedSummaryOnSmallScreen 0 4 1
A LinkScroller.scrollToTarget 0 13 4
A module.exports.scrollOnSuboptionChange 0 8 1
A module.exports.createAnimatedScroller 0 3 1
A module.exports.addScrollToLinkAnchors 0 7 1
1
'use strict';
2
3
var objectAssign = require( 'object-assign' ),
4
	_ = require( 'underscore' ),
5
6
	ElementStart = {
7
		MARGIN: 'MARGIN',
8
		ELEMENT: 'ELEMENT',
9
		PADDDING: 'PADDING'
10
	},
11
12
	calculateFixedHeaderElementHeight = function ( $fixedHeaderElements ) {
13
		return _.reduce( $fixedHeaderElements.get(), function ( offset, element ) {
14
			var $elm = $( element );
15
			if ( $elm.is( ':visible' ) ) {
16
				offset += $elm.height();
17
			}
18
			return offset;
19
		}, 0 );
20
	},
21
22
	calculateElementPadding = function ( $element ) {
23
		var matchedElemPadding = $element.css( 'padding-top' ).match( /^(\d+)px$/ );
24
25
		if ( !matchedElemPadding ) {
26
			return 0;
27
		}
28
		return parseInt( matchedElemPadding[ 1 ] );
29
	},
30
31
	calculateElementMargin = function ( $element ) {
32
		var matchedElemPadding = $element.css( 'margin-top' ).match( /^(\d+)px$/ );
33
34
		if ( !matchedElemPadding ) {
35
			return 0;
36
		}
37
		return parseInt( matchedElemPadding[ 1 ] );
38
	},
39
40
	/**
41
	 *
42
	 * @param {jQuery} $element Element whose offset will be taken
43
	 * @param {[jQuery]} $fixedHeaderElements Elements whose height will be subtracted from the offset
44
	 * @param {Object} options
45
	 * @param {string} options.elementStart
46
	 * @return {number}
47
	 */
48
	calculateElementOffset = function ( $element, $fixedHeaderElements, options ) {
49
		options = _.extend( { elementStart: ElementStart.ELEMENT }, options );
50
		var offset = $element.offset().top - calculateFixedHeaderElementHeight( $fixedHeaderElements );
51
		switch ( options.elementStart ) {
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
52
			case ElementStart.PADDDING:
0 ignored issues
show
Bug introduced by
The variable ElementStart seems to be never declared. If this is a global, consider adding a /** global: ElementStart */ 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...
53
				return offset + calculateElementPadding( $element );
54
			case ElementStart.MARGIN:
55
				return offset - calculateElementMargin( $element);
56
		}
57
		return offset;
58
	},
59
60
	AnimatedScroller = {
61
		fixedHeaderElements: null,
62
		scrollTo: function( $element, options ) {
63
			$( 'html, body' ).stop( true ).animate( {
64
				scrollTop: calculateElementOffset( $element, this.fixedHeaderElements, options )
65
			}, 1000, function () {
66
				// Callback after animation
67
				// Must change focus!
68
				$element.focus();
69
				if ($element.is( ':focus' ) ) { // Checking if the target was focused
70
					return false;
71
				} else {
72
					$element.attr( 'tabindex', '-1' ); // Adding tabindex for elements not focusable
73
					$element.focus(); // Set focus again
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
74
				}
75
			} );
76
77
		}
78
	},
79
80
	LinkScroller = {
81
		scroller: null,
82
		linkIsInsideCompletedSummaryOnSmallScreen: function( link ) {
83
			// only the completed fields at the bottom summary are inside a .wrap-field.completed
84
			return $( window ).width() < 1200 && $( link ).closest( '.wrap-field.has-longtext.completed .wrap-input' ).length > 0;
85
		},
86
		scrollToTarget: function( evt ) {
87
			evt.preventDefault();
88
89
			if ( this.linkIsInsideCompletedSummaryOnSmallScreen( evt.currentTarget ) ) {
90
				return;
91
			}
92
93
			var target = $( evt.currentTarget.hash );
94
			target = target.length ? target : $( '[name=' + evt.currentTarget.hash.slice( 1 ) + ']' );
95
			if ( target.length > 0 ) {
96
				this.scroller.scrollTo( target, { elementStart: ElementStart.PADDDING } );
97
			}
98
		}
99
	}
100
;
101
102
module.exports ={
103
	createAnimatedScroller: function ( fixedHeaderElements ) {
104
		return objectAssign( Object.create( AnimatedScroller ), { fixedHeaderElements: fixedHeaderElements } );
105
	},
106
	scrollOnSuboptionChange: function( $suboptionInput, $suboptionContainer, scroller ) {
107
		$suboptionInput.on( 'change', function ( evt ) {
108
			var wrapper = $suboptionContainer.find( '.wrap-field input[value=' + evt.target.value + ']' ).parents( '.wrap-field' ).find( '.info-text' );
109
			if (wrapper.length) {
110
				scroller.scrollTo( wrapper, { elementStart: ElementStart.ELEMENT } );
111
			}
112
		} )
113
	},
114
	/**
115
	 * Ensure smooth scroll to the given anchor links. Make sure to only pass links on the same page that can be scrolled to.
116
	 *
117
	 * @param {jQuery} $links
118
	 * @param {object} scroller
119
	 */
120
	addScrollToLinkAnchors: function( $links, scroller ) {
121
		var linkScroller = objectAssign( Object.create( LinkScroller ), { scroller: scroller } );
122
		$links.not('[href="#"]')
123
			.not('[href="#0"]')
124
			.not('.state-overview .wrap-field.completed .wrap-input')
125
			.click( linkScroller.scrollToTarget.bind( linkScroller ) );
126
	},
127
	ElementStart: ElementStart,
128
	// exposed for testing
129
	calculateElementOffset: calculateElementOffset
130
};
131