@@ -3,29 +3,16 @@ const animator = require('./animator.js')
3
3
const Loader = require ( './loader.js' )
4
4
const COOKIE_NAME = 'mctb_bar_hidden'
5
5
6
- function throttle ( fn , threshold , scope ) {
7
- threshold || ( threshold = 600 )
8
- let last
9
- let deferTimer
10
- return function ( ) {
11
- const context = scope || this
12
- const now = + new Date ( )
13
- const args = arguments
14
- if ( last && now < last + threshold ) {
15
- // hold on to it
16
- clearTimeout ( deferTimer )
17
- deferTimer = setTimeout ( function ( ) {
18
- last = now
19
- fn . apply ( context , args )
20
- } , threshold )
21
- } else {
22
- last = now
23
- fn . apply ( context , args )
24
- }
25
- }
6
+ // holder for debounce timeout
7
+ let timeout
8
+ function debounce ( fn , delay ) {
9
+ clearTimeout ( timeout )
10
+ timeout = setTimeout ( fn , delay )
26
11
}
27
12
28
- function Bar ( wrapperEl , config ) {
13
+ function Bar ( ) {
14
+ const wrapperEl = document . getElementById ( 'mailchimp-top-bar' )
15
+ const config = window . mctb
29
16
const barEl = wrapperEl . querySelector ( '.mctb-bar' )
30
17
const iconEl = document . createElement ( 'span' )
31
18
const formEl = barEl . querySelector ( 'form' )
@@ -36,66 +23,60 @@ function Bar (wrapperEl, config) {
36
23
const isBottomBar = ( config . position === 'bottom' )
37
24
const state = config . state
38
25
39
- // Functions
40
- function init ( ) {
41
- // remove "no_js" field
42
- const noJsField = barEl . querySelector ( 'input[name="_mctb_no_js"]' )
43
- noJsField . parentElement . removeChild ( noJsField )
26
+ // remove "no_js" field (which is used to detect bots and prevent spam)
27
+ const noJsField = barEl . querySelector ( 'input[name="_mctb_no_js"]' )
28
+ noJsField . parentElement . removeChild ( noJsField )
44
29
45
- formEl . addEventListener ( 'submit' , submitForm )
46
-
47
- // save original bodyPadding
48
- if ( isBottomBar ) {
49
- wrapperEl . insertBefore ( iconEl , barEl )
50
- originalBodyPadding = ( parseInt ( document . body . style . paddingBottom ) || 0 )
51
- } else {
52
- wrapperEl . insertBefore ( iconEl , barEl . nextElementSibling )
53
- originalBodyPadding = ( parseInt ( document . body . style . paddingTop ) || 0 )
54
- }
30
+ formEl . addEventListener ( 'submit' , submitForm )
55
31
56
- // configure icon
57
- iconEl . className = 'mctb-close'
58
- iconEl . innerHTML = config . icons . show
59
- iconEl . addEventListener ( 'click' , toggle )
32
+ // save original bodyPadding
33
+ if ( isBottomBar ) {
34
+ wrapperEl . insertBefore ( iconEl , barEl )
35
+ originalBodyPadding = ( parseInt ( document . body . style . paddingBottom ) || 0 )
36
+ } else {
37
+ wrapperEl . insertBefore ( iconEl , barEl . nextElementSibling )
38
+ originalBodyPadding = ( parseInt ( document . body . style . paddingTop ) || 0 )
39
+ }
60
40
61
- // count input fields (3 because of hidden input honeypot)
62
- if ( barEl . querySelectorAll ( 'input:not([type="hidden"])' ) . length > 3 ) {
63
- wrapperEl . className += ' multiple-input-fields'
64
- }
41
+ // configure icon
42
+ iconEl . className = 'mctb-close'
43
+ iconEl . innerHTML = config . icons . show
44
+ iconEl . addEventListener ( 'click' , toggle )
65
45
66
- // calculate initial dimensions
67
- calculateDimensions ( )
68
-
69
- // on dom repaint, bar height changes. re-calculate in next repaint.
70
- window . requestAnimationFrame ( calculateDimensions )
46
+ // count input fields (3 because of hidden input honeypot)
47
+ if ( barEl . querySelectorAll ( 'input:not([type="hidden"])' ) . length > 3 ) {
48
+ wrapperEl . className += ' multiple-input-fields'
49
+ }
71
50
72
- // Show the bar straight away?
73
- if ( cookies . exists ( COOKIE_NAME ) ) {
74
- show ( )
75
- }
51
+ // on dom repaint, bar height changes. re-calculate in next repaint.
52
+ window . requestAnimationFrame ( calculateDimensions )
76
53
77
- // fade response 4 seconds after showing bar
78
- if ( responseEl ) {
79
- window . setTimeout ( fadeResponse , 4000 )
80
- }
54
+ // Show the bar straight away?
55
+ if ( ! cookies . exists ( COOKIE_NAME ) ) {
56
+ show ( )
57
+ }
81
58
82
- window . addEventListener ( 'resize' , throttle ( calculateDimensions ) )
59
+ // fade response 4 seconds after showing bar
60
+ if ( responseEl ) {
61
+ window . setTimeout ( fadeResponse , 4000 )
83
62
}
84
63
64
+ window . addEventListener ( 'resize' , debounce ( calculateDimensions , 100 ) )
65
+
85
66
function submitForm ( evt ) {
86
67
const loader = new Loader ( formEl )
87
68
const data = new FormData ( formEl )
88
69
let request = new XMLHttpRequest ( )
89
70
request . onreadystatechange = function ( ) {
90
- let response
91
-
92
- // are we done?
93
71
if ( this . readyState !== 4 ) {
94
72
return
95
73
}
96
74
75
+ // remove loading indicator
97
76
loader . stop ( )
98
77
78
+ // parse json response
79
+ let response
99
80
if ( this . status >= 200 && this . status < 400 ) {
100
81
try {
101
82
response = JSON . parse ( this . responseText )
@@ -107,6 +88,7 @@ function Bar (wrapperEl, config) {
107
88
state . success = ! ! response . success
108
89
state . submitted = true
109
90
91
+ // maybe redirect to url from settings
110
92
if ( response . success && response . redirect_url ) {
111
93
window . location . href = response . redirect_url
112
94
return
@@ -150,28 +132,32 @@ function Bar (wrapperEl, config) {
150
132
window . setTimeout ( fadeResponse , 4000 )
151
133
}
152
134
135
+ function iconFitsInsideBar ( ) {
136
+ // would the close icon fit inside the bar?
137
+ let elementsWidth = 0
138
+ for ( let i = 0 ; i < barEl . firstElementChild . children . length ; i ++ ) {
139
+ elementsWidth += barEl . firstElementChild . children [ i ] . clientWidth
140
+ }
141
+
142
+ return ( elementsWidth + iconEl . clientWidth + 200 ) < barEl . clientWidth
143
+ }
144
+
153
145
function calculateDimensions ( ) {
154
146
// make sure bar is visible
155
147
const origBarDisplay = barEl . style . display
156
148
if ( origBarDisplay !== 'block' ) {
157
149
barEl . style . visibility = 'hidden'
150
+ barEl . style . display = 'block'
158
151
}
159
- barEl . style . display = 'block'
160
152
161
153
// calculate & set new body padding if bar is currently visible
162
154
bodyPadding = ( originalBodyPadding + barEl . clientHeight ) + 'px'
163
155
if ( visible ) {
164
156
document . body . style [ isBottomBar ? 'paddingBottom' : 'paddingTop' ] = bodyPadding
165
157
}
166
158
167
- // would the close icon fit inside the bar?
168
- let elementsWidth = 0
169
- for ( let i = 0 ; i < barEl . firstElementChild . children . length ; i ++ ) {
170
- elementsWidth += barEl . firstElementChild . children [ i ] . clientWidth
171
- }
172
-
173
159
wrapperEl . className = wrapperEl . className . replace ( 'mctb-icon-inside-bar' , '' )
174
- if ( elementsWidth + iconEl . clientWidth + 200 < barEl . clientWidth ) {
160
+ if ( iconFitsInsideBar ( ) ) {
175
161
wrapperEl . className += ' mctb-icon-inside-bar'
176
162
177
163
// since icon is now absolutely positioned, we need to set a min height
@@ -283,9 +269,6 @@ function Bar (wrapperEl, config) {
283
269
return visible ? hide ( true ) : show ( true )
284
270
}
285
271
286
- // Code to run upon object instantiation
287
- init ( )
288
-
289
272
// Return values
290
273
return {
291
274
element : wrapperEl ,
@@ -296,6 +279,5 @@ function Bar (wrapperEl, config) {
296
279
}
297
280
298
281
document . addEventListener ( 'DOMContentLoaded' , ( ) => {
299
- const element = document . getElementById ( 'mailchimp-top-bar' )
300
- window . MailChimpTopBar = new Bar ( element , window . mctb )
282
+ window . MailChimpTopBar = new Bar ( )
301
283
} )
0 commit comments