Completed
Pull Request — master (#1090)
by Gabriel
163:05 queued 101:20
created

scrolling.js ➔ calculateElementPadding   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
c 0
b 0
f 0
nc 2
dl 0
loc 8
rs 9.4285
nop 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.completed .wrap-input' ).length > 0;
85
		},
86
		linkIsOnDifferentPage: function( link ) {
87
			return location.pathname.replace(/^\//, '') !== link.pathname.replace(/^\//, '') ||
88
				location.hostname !== link.hostname
89
		},
90
		scrollToTarget: function( evt ) {
91
			evt.preventDefault();
92
			if ( this.linkIsInsideCompletedSummaryOnSmallScreen( evt.currentTarget ) || this.linkIsOnDifferentPage( evt.currentTarget ) ) {
93
				return;
94
			}
95
			var target = $( evt.currentTarget.hash );
96
			target = target.length ? target : $( '[name=' + evt.currentTarget.hash.slice( 1 ) + ']' );
97
			if ( target.length > 0 ) {
98
				this.scroller.scrollTo( target, { elementStart: ElementStart.PADDDING } );
99
			}
100
		}
101
	}
102
	;
103
104
module.exports ={
105
	createAnimatedScroller: function ( fixedHeaderElements ) {
106
		return objectAssign( Object.create( AnimatedScroller ), { fixedHeaderElements: fixedHeaderElements } );
107
	},
108
	scrollOnSuboptionChange: function( $suboptionInput, $suboptionContainer, scroller ) {
109
		$suboptionInput.on( 'change', function ( evt ) {
110
			var wrapper = $suboptionContainer.find( '.wrap-field input[value=' + evt.target.value + ']' ).parents( '.wrap-field' ).find( '.info-text' );
111
			if (wrapper.length) {
112
				scroller.scrollTo( wrapper, { elementStart: ElementStart.ELEMENT } );
113
			}
114
		} )
115
	},
116
	addScrollToLinkAnchors: function( $links, scroller ) {
117
		var linkScroller = objectAssign( Object.create( LinkScroller ), { scroller: scroller } );
118
		$links.not('[href="#"]')
119
			.not('[href="#0"]')
120
			.not('.state-overview .wrap-field.completed .wrap-input')
121
			.click( linkScroller.scrollToTarget.bind( linkScroller ) );
122
	},
123
	ElementStart: ElementStart,
124
	// exposed for testing
125
	calculateElementOffset: calculateElementOffset
126
};