mod-build 4.0.78 → 4.0.79

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.0.79
4
+
5
+ - Add `grab-b2b-data` task to serve process
6
+
3
7
  ## 4.0.78
4
8
 
5
9
  - Updating `mergeDefaultFormFieldConfig` to be recursive and merge to nested fields (so we can wrap multiple fields in a `fieldset`).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mod-build",
3
- "version": "4.0.78",
3
+ "version": "4.0.79",
4
4
  "description": "Share components for S3 sites.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -0,0 +1,4 @@
1
+ {
2
+ "tcpaText": "We respect your privacy and want to make you aware of a few things. By submitting, you authorize QuinStreet and up to <a href=\"http://hsleadpost1.quinstage.com/clientList.jsp?c_level=default&aff=0\" target=\"_blank\" id=\"clientList\" style=\"color: inherit !important;\">four companies<\/a> to call you on the mobile or landline phone number provided. You understand that some may use automated dialing, prerecorded messages or SMS messages to contact you and that you are in no way required to purchase any products or services from them. It's entirely your choice."
3
+ }
4
+
@@ -0,0 +1,9 @@
1
+ <p>
2
+ {{company_name}} is a place to gather ideas and see how other homeowners have transformed their houses. It's for those who've just begun to think about what they want their new kitchen to look like, those who wonder what kind of siding and color will add the most value, those who want to know if running their home on solar energy is even a possibility.
3
+ </p>
4
+ <p>
5
+ {{company_name}} is where you come to get inspired, see what's possible, and connect with a professional who will make your dream home a reality.
6
+ </p>
7
+ <p>
8
+ We&rsquo;re based in Austin, Texas, right in the heart of downtown where we get to enjoy everything this wonderful city has to offer - SXSW, live music, fantastic restaurants, and best of all, friendly folks. We&rsquo;re constantly inspired by the many different types of design and architecture in this city, from the contemporary new homes peppered throughout Travis Heights, to historic mansions beautifully restored all over downtown and Old West Austin. We&rsquo;re proud to feature lots of Austin architects and contractors on {{company_name}} along with countless talented contractors from all over the country, so no matter where you live, you&rsquo;ll be able to connect with someone who can make your home design ideas a reality.
9
+ </p>
@@ -0,0 +1,9 @@
1
+ <p>
2
+ {{company_name}} is a place to gather ideas and see how other homeowners have transformed their houses. It's for those who've just begun to think about what they want their new kitchen to look like, those who wonder what kind of siding and color will add the most value, those who want to know if running their home on solar energy is even a possibility.
3
+ </p>
4
+ <p>
5
+ {{company_name}} is where you come to get inspired, see what's possible, and connect with a professional who will make your dream home a reality.
6
+ </p>
7
+ <p>
8
+ We&rsquo;re based in Austin, Texas, right in the heart of downtown where we get to enjoy everything this wonderful city has to offer - SXSW, live music, fantastic restaurants, and best of all, friendly folks. We&rsquo;re constantly inspired by the many different types of design and architecture in this city, from the contemporary new homes peppered throughout Travis Heights, to historic mansions beautifully restored all over downtown and Old West Austin. We&rsquo;re proud to feature lots of Austin architects and contractors on {{company_name}} along with countless talented contractors from all over the country, so no matter where you live, you&rsquo;ll be able to connect with someone who can make your home design ideas a reality.
9
+ </p>
@@ -0,0 +1,348 @@
1
+ <div class="contact-us">
2
+ <div class="contact-us__container">
3
+ <form class="contact-us__form">
4
+ <div class="step step--active" aria-hidden="false" tabindex="-1" aria-labelledby="contact-step-one-header">
5
+ <span id="contact-step-one-header" class="visually-hidden">Step 1: Select the radio button option that suits your inquiry.</span>
6
+ <div class="step__header">
7
+ <p class="step__header-number">
8
+ Step 1
9
+ </p>
10
+ <p class="step__header-text">
11
+ Select an option that suits your inquiry
12
+ </p>
13
+ </div>
14
+ <div class="step__content">
15
+ <div class="step__content-item">
16
+ <picture>
17
+ <source srcset="https://modernize.com/quote/resources/assets/images/illustrations/misc/handshake.webp" type="image/webp">
18
+ <img class="step__image" src="https://modernize.com/quote/resources/assets/images/illustrations/misc/handshake.jpg" alt="">
19
+ </picture>
20
+ <div class="step__fieldset">
21
+ <fieldset>
22
+ <legend>Partnerships</legend>
23
+ <div class="field">
24
+ <input type="radio" name="topic" value="SF_partnerships" id="SF_partnerships">
25
+ <label for="SF_partnerships">Publisher/Media Partnership</label>
26
+ </div>
27
+ <div class="field">
28
+ <input type="radio" name="topic" value="SF_advertisers" id="SF_advertisers">
29
+ <label for="SF_advertisers">Advertisers</label>
30
+ </div>
31
+ </fieldset>
32
+ </div>
33
+ </div>
34
+ <div class="step__content-item">
35
+ <picture>
36
+ <source srcset="https://modernize.com/quote/resources/assets/images/illustrations/misc/presentation.webp" type="image/webp">
37
+ <img class="step__image" src="https://modernize.com/quote/resources/assets/images/illustrations/misc/presentation.jpg" alt="">
38
+ </picture>
39
+ <div class="step__fieldset">
40
+ <fieldset>
41
+ <legend>Website Content, Data, Technical Issues</legend>
42
+ <div class="field">
43
+ <input type="radio" name="topic" value="reprint-permissions" id="reprint-permissions">
44
+ <label for="reprint-permissions">Reprint Permissions</label>
45
+ </div>
46
+ <div class="field">
47
+ <input type="radio" name="topic" value="technical-site-issue" id="technical-site-issue">
48
+ <label for="technical-site-issue">Technical Issues with Website</label>
49
+ </div>
50
+ </fieldset>
51
+ </div>
52
+ </div>
53
+ <div class="step__content-item">
54
+ <picture>
55
+ <source srcset="https://modernize.com/quote/resources/assets/images/illustrations/misc/man-on-couch.webp" type="image/webp">
56
+ <img class="step__image" src="https://modernize.com/quote/resources/assets/images/illustrations/misc/man-on-couch.jpg" alt="">
57
+ </picture>
58
+ <div class="step__fieldset">
59
+ <fieldset>
60
+ <legend>Unsubscribe Requests</legend>
61
+ <div class="field">
62
+ <input type="radio" name="topic" value="email-opt-out" id="email-opt-out">
63
+ <label for="email-opt-out">Consumer Email Unsubscribe</label>
64
+ </div>
65
+ <div class="field">
66
+ <input type="radio" name="topic" value="phone-opt-out" id="phone-opt-out">
67
+ <label for="phone-opt-out">Consumer Phone Calls/Text Message Unsubscribe</label>
68
+ </div>
69
+ <div class="field">
70
+ <input type="radio" name="topic" value="general-inquiry" id="general-inquiry">
71
+ <label for="general-inquiry">Other Consumer Issues</label>
72
+ </div>
73
+ </fieldset>
74
+ </div>
75
+ <ul class="step__list">
76
+ <li>
77
+ <a href="https://privacy-central.securiti.ai/#/dsr/edc7a01c-b34c-43d4-8da8-127895e8bd71" target="_parent">California - Request to Know</a>
78
+ <a href="tel:8448727854">(844) 872-7854</a>
79
+ </li>
80
+ <li>
81
+ <a href="https://privacy-central.securiti.ai/#/dsr/edc7a01c-b34c-43d4-8da8-127895e8bd71" target="_parent">California - Request to Delete</a>
82
+ </li>
83
+ </ul>
84
+ </div>
85
+ </div>
86
+ </div>
87
+ <div class="step" aria-hidden="true" tabindex="-1" aria-labelledby="contact-step-two-header">
88
+ <span id="contact-step-two-header" class="visually-hidden">Step 2: Submit your contact info and we'll address your request ASAP. Press tab to begin entering your information.</span>
89
+ <div class="step__header">
90
+ <p class="step__header-number">
91
+ Step 2
92
+ </p>
93
+ <p class="step__header-text">
94
+ Submit your contact info and we'll address your request ASAP
95
+ </p>
96
+ </div>
97
+ <div class="step__content">
98
+ <div class="step__error"></div>
99
+ <div class="form-group parent-error">
100
+ <label for="contact-name">Name</label>
101
+ <input class="form-control" type="text" name="name" id="contact-name">
102
+ <span class="step__error-message" id="error-contact-name">Please enter your name.</span>
103
+ </div>
104
+ <div class="form-group parent-error">
105
+ <label for="contact-phone">Phone</label>
106
+ <input class="form-control" type="tel" name="phone" id="contact-phone">
107
+ <span class="step__error-message" id="error-contact-phone">Please enter a valid phone number.</span>
108
+ </div>
109
+ <div class="form-group parent-error">
110
+ <label for="contact-email">Email</label>
111
+ <input class="form-control" type="email" name="email" id="contact-email">
112
+ <span class="step__error-message" id="error-contact-email">Please enter a valid email address.</span>
113
+ </div>
114
+ <div class="form-group parent-error">
115
+ <label for="contact-message">Message</label>
116
+ <textarea class="form-control" name="message" id="contact-message"></textarea>
117
+ <span class="step__error-message" id="error-contact-message">Please enter your message.</span>
118
+ </div>
119
+ <div>
120
+ <input type="hidden" name="domain" value="{{domain}}">
121
+ <input type="hidden" name="date">
122
+ <input type="hidden" name="time">
123
+ <input type="hidden" name="vertical" value="HomeServices">
124
+ <input type="hidden" name="g-recaptcha-response" value="">
125
+ </div>
126
+ </div>
127
+ <div class="captcha-container captcha-body">
128
+ <div class="g-recaptcha" data-callback="captcha_submit" data-sitekey="6LcsGEkUAAAAAHekw-AS_6Vcu_WeYk7LiPJB1mrR"></div>
129
+ </div>
130
+ <div class="form-controls">
131
+ <button class="btn btn-secondary btn-back" type="button" aria-label="Back"></button>
132
+ <button class="btn btn-submit" type="submit">
133
+ <span class="btn__text">Submit</span>
134
+ <span class="btn__spinner"></span>
135
+ </button>
136
+ </div>
137
+ </div>
138
+ </form>
139
+ <div class="contact-us__thankyou" aria-hidden="true" tabindex="-1" aria-labelledby="contact-success-header">
140
+ <span id="contact-success-header" class="visually-hidden"> Thank you. We'll address your request as soon as possible.</span>
141
+ <picture>
142
+ <source srcset="https://modernize.com/quote/resources/assets/images/illustrations/misc/laptop.webp" type="image/webp">
143
+ <img class="contact-us__thankyou-img" src="https://modernize.com/quote/resources/assets/images/illustrations/misc/laptop.jpg" alt="">
144
+ </picture>
145
+ <p class="contact-us__thankyou-title">Thank You!</p>
146
+ <p class="contact-us__thankyou-subtitle">We'll address your request as soon as possible.</p>
147
+ </div>
148
+ </div>
149
+ </div>
150
+ <script src="https://www.google.com/recaptcha/api.js"></script>
151
+ <script>
152
+ window.captcha_submit = function(response) {
153
+ document.querySelector('input[name="g-recaptcha-response"]').value = response;
154
+ }
155
+ </script>
156
+
157
+ <script>
158
+ var contactUs = {
159
+ $modal: document.getElementById('info-modal'),
160
+ $form: document.querySelector('.contact-us__form'),
161
+ $thankyou: document.querySelector('.contact-us__thankyou'),
162
+ url: 'https://contact.formfetch.com/v2_submit',
163
+ getFormData: function() {
164
+ const date = new Date();
165
+ const data = {};
166
+ const inputs = this.$form.elements;
167
+ if (inputs) {
168
+ for (let i = 0; i < inputs.length; i++) {
169
+ const input = inputs[i];
170
+ if (input.name) {
171
+ data[input.name] = input.value;
172
+ }
173
+ }
174
+ }
175
+
176
+ data.time = date.toLocaleTimeString();
177
+ data.date = date.toLocaleDateString();
178
+ return data;
179
+ },
180
+ validateData: function(data) {
181
+ const _this = this;
182
+ var isFormValid = true;
183
+ const emailPattern = /^([\w\+-]+(?:\.[\w\+-]+)*)(@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$)/i;
184
+
185
+ Object.keys(data).forEach(function(key) {
186
+ var isValid = true;
187
+ const value = data[key];
188
+ switch (key) {
189
+ case 'email':
190
+ isValid = emailPattern.test(value);
191
+ break;
192
+ default:
193
+ isValid = value !== '';
194
+ break;
195
+ }
196
+
197
+ if (!isValid) {
198
+ _this.markFieldAsInvalid(key);
199
+ _this.focusOnFirstError();
200
+ isFormValid = false;
201
+ }
202
+ });
203
+
204
+ return isFormValid;
205
+ },
206
+ focusOnFirstError: function() {
207
+ const $error = this.$form.querySelectorAll('.has-error');
208
+
209
+ if ($error.length > 0) {
210
+ const firstError = $error[0];
211
+ const $field = firstError.querySelector('input[type="text"], input[type="tel"], input[type="email"], textarea');
212
+ if ($field) {
213
+ $field.focus();
214
+ }
215
+ }
216
+ },
217
+ markFieldAsInvalid: function(fieldName) {
218
+ const $invalidField = this.$form.querySelector('#contact-' + fieldName);
219
+ if ($invalidField) {
220
+ $invalidField.parentElement.classList.add('has-error');
221
+ }
222
+
223
+ const $field = this.$form.querySelector('input[name="' + fieldName + '"]');
224
+ if ($field) {
225
+ $field.setAttribute('aria-invalid', 'true');
226
+ $field.setAttribute('aria-describedby', 'error-contact' + fieldName);
227
+ }
228
+ },
229
+ submit: function() {
230
+ this.$form.classList.add('form--loading');
231
+
232
+ const _this = this;
233
+ const data = this.getFormData();
234
+ const isFormValid = this.validateData(data);
235
+ const formData = Object.keys(data).map(function(key) {
236
+ return encodeURIComponent(key) + '=' + encodeURIComponent(data[key]).replace(/%20/g, '+');
237
+ }).join('&');
238
+
239
+ if (isFormValid) {
240
+ // Submit Form
241
+ var request = new XMLHttpRequest();
242
+ request.onreadystatechange = function() {
243
+ if (request.readyState === 4) {
244
+ if (request.status === 200) {
245
+ var response = JSON.parse(request.responseText);
246
+ if (response.success) {
247
+ _this.$form.style.display = 'none';
248
+ _this.$thankyou.style.display = 'block';
249
+ } else {
250
+ _this.$form.classList.remove('form--loading');
251
+ document.querySelector('.step__error').innerHTML = response.message;
252
+ }
253
+ } else {
254
+ console.error('Request failed with status:', request.status);
255
+ }
256
+ }
257
+ };
258
+
259
+ request.open('POST', _this.url, true);
260
+ request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
261
+ request.send(formData);
262
+ } else {
263
+ _this.$form.classList.remove('form--loading');
264
+ }
265
+ },
266
+ changeStep: function(direction) {
267
+ const $activeStep = this.$form.querySelector('.step--active');
268
+ const $nextStep = $activeStep.nextElementSibling;
269
+ const $prevStep = $activeStep.previousElementSibling;
270
+
271
+ $activeStep.classList.remove('step--active');
272
+ $activeStep.setAttribute('aria-hidden', 'true');
273
+ if (direction === 'next') {
274
+ $nextStep.classList.add('step--active');
275
+ $nextStep.setAttribute('aria-hidden', 'false');
276
+ $nextStep.focus();
277
+ } else {
278
+ $prevStep.classList.add('step--active');
279
+ $prevStep.setAttribute('aria-hidden', 'false');
280
+ $prevStep.focus();
281
+ }
282
+ },
283
+ watch: function() {
284
+ const _this = this;
285
+ var radioChangedWithMouse = false;
286
+
287
+ // Watch form submission
288
+ _this.$form.addEventListener('submit', function(e) {
289
+ e.preventDefault();
290
+ if (!_this.$form.classList.contains('form--loading')) {
291
+ _this.submit();
292
+ }
293
+ });
294
+
295
+ // Watch radio click with mouse only
296
+ const $radioLabelsAndInputs = _this.$form.querySelectorAll('fieldset label, input[type="radio"]');
297
+ $radioLabelsAndInputs.forEach(function(input) {
298
+ input.addEventListener('mouseup', function() {
299
+ radioChangedWithMouse = true;
300
+ });
301
+ })
302
+
303
+ const $radioInputs = _this.$form.querySelectorAll('input[type="radio"]');
304
+ $radioInputs.forEach(function(input) {
305
+ input.addEventListener('click', function(e) {
306
+ if (radioChangedWithMouse) {
307
+ _this.changeStep('next');
308
+ radioChangedWithMouse = false;
309
+ }
310
+ });
311
+ input.addEventListener('keypress', function(e) {
312
+ if(e.which == 13){
313
+ e.preventDefault();
314
+ _this.changeStep('next');
315
+
316
+ // Making sure radio button is actually selected on Enter
317
+ var focused = e.target;
318
+ if (!focused.checked) {
319
+ focused.checked = true;
320
+ }
321
+ }
322
+ })
323
+ });
324
+
325
+ // Watch back button
326
+ const $backBtn = _this.$form.querySelector('.btn-back');
327
+ if ($backBtn) {
328
+ $backBtn.addEventListener('click', function() {
329
+ _this.changeStep('prev');
330
+ });
331
+ }
332
+
333
+ // Remove error state when input/textarea changes
334
+ const $formInputs = _this.$form.querySelectorAll('input, textarea');
335
+ $formInputs.forEach(function(input) {
336
+ input.addEventListener('change', function() {
337
+ input.parentElement.classList.remove('has-error');
338
+ input.setAttribute('aria-invalid', 'false');
339
+ input.removeAttribute('aria-describedby');
340
+ });
341
+ });
342
+ },
343
+ init: function() {
344
+ this.watch();
345
+ }
346
+ };
347
+ contactUs.init();
348
+ </script>