@rtstic.dev/pulse 0.0.54 → 0.0.56

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.
@@ -1,2 +1,679 @@
1
- "use strict";(()=>{var P=Object.create;var T=Object.defineProperty;var M=Object.getOwnPropertyDescriptor;var S=Object.getOwnPropertyNames;var k=Object.getPrototypeOf,w=Object.prototype.hasOwnProperty;var D=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,r)=>(typeof require<"u"?require:t)[r]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var C=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of S(t))!w.call(e,o)&&o!==r&&T(e,o,{get:()=>t[o],enumerable:!(n=M(t,o))||n.enumerable});return e};var x=(e,t,r)=>(r=e!=null?P(k(e)):{},C(t||!e||!e.__esModule?T(r,"default",{value:e,enumerable:!0}):r,e));console.log("Book a demo form script loaded");var _=["text","email","phone","checkbox","radio","select","legal"],h=[{type:"pulse",name:"pulse","calender-name":"david","embed-url":"https://meetings.hubspot.com/valeria-angelini/pulse-valeria-and-alessandra?embed=true"},{type:"industry",name:"Financial Services","calender-name":"david","embed-url":"https://meetings.hubspot.com/david-zarate2?embed=true"},{type:"industry",name:"Sports","calender-name":"steven","embed-url":"https://meetings.hubspot.com/steven-campos?embed=true"},{type:"industry",name:"Beauty","calender-name":"albany","embed-url":"https://meetings.hubspot.com/david-zarate2?embed=true"},{type:"industry",name:"Music","calender-name":"steven","embed-url":"https://meetings.hubspot.com/steven-campos?embed=true"},{type:"industry",name:"Customer Packaged Goods","calender-name":"david","embed-url":"https://meetings.hubspot.com/david-zarate2?embed=true"},{type:"industry",name:"Other","calender-name":"david","embed-url":"https://meetings.hubspot.com/david-zarate2?embed=true"}],q=["true","atleast-one","only-one"],l={REQUIRED_INPUT:"ERR_REQUIRED",REQUIRED_LEGAL:"ERR_LEGAL_REQUIRED",REQUIRED_SELECT:"ERR_SELECT_REQUIRED",ATLEAST_ONE:"ERR_ATLEAST_ONE",ONLY_ONE_NONE:"ERR_ONLY_ONE_NONE",ONLY_ONE_EXCEEDED:"ERR_ONLY_ONE_EXCEEDED",INVALID_EMAIL:"ERR_INVALID_EMAIL",INVALID_PHONE:"ERR_INVALID_PHONE",PHONE_TOO_SHORT:"ERR_PHONE_TOO_SHORT",PHONE_TOO_LONG:"ERR_PHONE_TOO_LONG",PHONE_INVALID_COUNTRY:"ERR_PHONE_INVALID_COUNTRY"},d={[l.REQUIRED_INPUT]:"This field is required.",[l.REQUIRED_LEGAL]:"You must accept this to continue.",[l.REQUIRED_SELECT]:"Please select an option.",[l.ATLEAST_ONE]:"Please select at least one option.",[l.ONLY_ONE_NONE]:"Please select one option.",[l.ONLY_ONE_EXCEEDED]:"Only one option can be selected.",[l.INVALID_EMAIL]:"Please enter a valid email address.",[l.INVALID_PHONE]:"Please enter a valid phone number.",[l.PHONE_TOO_SHORT]:"Phone number is too short.",[l.PHONE_TOO_LONG]:"Phone number is too long.",[l.PHONE_INVALID_COUNTRY]:"Invalid country code."},p="pulse-form-error",I=new Map;function N(e){try{let t=e.getSelectedCountryData?.();if(!t?.dialCode)return;document.querySelectorAll('input[pulse-form-field="country-code"]').forEach(n=>{n.value=`+${t.dialCode}`})}catch(t){console.warn("[PulseForm] Error filling country code:",t)}}function V(){let e=window.intlTelInput;if(!e){console.warn(`[PulseForm] intlTelInput not found on window. Make sure you include the intl-tel-input script before this script.
2
- e.g. <script src="https://cdn.jsdelivr.net/npm/intl-tel-input@25/build/js/intlTelInput.min.js"><\/script>`);return}document.querySelectorAll('[pulse-form-block="phone"] input[type="tel"]').forEach(r=>{try{let n=e(r,{initialCountry:"us",separateDialCode:!0,strictMode:!1,loadUtils:()=>import("https://cdn.jsdelivr.net/npm/intl-tel-input@25/build/js/utils.js")});I.set(r,n),N(n),r.addEventListener("countrychange",()=>{N(n)}),console.log(`[PulseForm] intl-tel-input initialized for "${r.closest("[pulse-form-block]")?.getAttribute("pulse-field-name")||"unknown"}"`)}catch(n){console.warn("[PulseForm] Failed to init intl-tel-input:",n)}})}var U={0:l.INVALID_PHONE,1:l.PHONE_INVALID_COUNTRY,2:l.PHONE_TOO_SHORT,3:l.PHONE_TOO_LONG,4:l.INVALID_PHONE};function B(e){return e?/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e)?null:{code:l.INVALID_EMAIL,message:d[l.INVALID_EMAIL]}:null}function Y(e){let t=e.value.trim();if(!t)return null;let r=I.get(e);if(r)try{if(r.isValidNumber())return null;let o=r.getValidationError?.()??0,s=U[o]||l.INVALID_PHONE;return{code:s,message:d[s]}}catch{return{code:l.INVALID_PHONE,message:d[l.INVALID_PHONE]}}let n=t.replace(/\D/g,"");return n.length<7?{code:l.PHONE_TOO_SHORT,message:d[l.PHONE_TOO_SHORT]}:n.length>15?{code:l.PHONE_TOO_LONG,message:d[l.PHONE_TOO_LONG]}:null}function $(e){let t=e.querySelector("input, textarea");return t?t.value.trim():null}function Q(e){let t=e.querySelector('input[type="email"]');return t?t.value.trim():null}function G(e){let t=e.querySelector('input[type="tel"]');return t?t.value.trim():null}function j(e){let t=e.querySelectorAll('input[type="checkbox"]'),r=[];return t.forEach(n=>{if(n.checked){let o=n.getAttribute("data-name")?.trim();r.push(o||n.value||"on")}}),r}function z(e){let t=e.querySelectorAll('input[type="radio"]');for(let r of t)if(r.checked)return r.closest("label")?.textContent?.trim()||r.value||"on";return null}function X(e){let t=e.querySelector("select");if(!t)return null;let r=t.value.trim();return!r||t.selectedIndex===0?null:r}function W(e){let t=e.querySelector('input[type="checkbox"]');return t?t.checked:!1}function v(e,t){switch(e){case"text":return $(t);case"email":return Q(t);case"phone":return G(t);case"checkbox":return j(t);case"radio":return z(t);case"select":return X(t);case"legal":return W(t);default:return null}}function A(e){let t=e.getAttribute("required-selection")?.trim().toLowerCase();return!t||!q.includes(t)?null:t}function J(e){return e.getAttribute("mirror-value")?.trim().toLowerCase()==="true"}function b(e,t,r,n){if(t)switch(t){case"true":{if(e==="legal"&&r!==!0)return{code:l.REQUIRED_LEGAL,message:d[l.REQUIRED_LEGAL]};if(e==="select"&&!r)return{code:l.REQUIRED_SELECT,message:d[l.REQUIRED_SELECT]};if((e==="text"||e==="email"||e==="phone")&&(!r||typeof r=="string"&&r===""))return{code:l.REQUIRED_INPUT,message:d[l.REQUIRED_INPUT]};break}case"atleast-one":{if(Array.isArray(r)?r.length===0:!r)return{code:l.ATLEAST_ONE,message:d[l.ATLEAST_ONE]};break}case"only-one":{if(Array.isArray(r)){if(r.length===0)return{code:l.ONLY_ONE_NONE,message:d[l.ONLY_ONE_NONE]};if(r.length>1)return{code:l.ONLY_ONE_EXCEEDED,message:d[l.ONLY_ONE_EXCEEDED]}}else if(!r)return{code:l.ONLY_ONE_NONE,message:d[l.ONLY_ONE_NONE]};break}}if(e==="email"&&typeof r=="string"&&r){let o=B(r);if(o)return o}if(e==="phone"&&typeof r=="string"&&r){let o=n.querySelector('input[type="tel"]');if(o){let s=Y(o);if(s)return s}}return null}function y(e){let t=document.querySelectorAll("[pulse-form-block]"),r=[];return t.forEach(n=>{let o=n.getAttribute("pulse-form-block")?.trim().toLowerCase();if(!o||!_.includes(o))return;let s=o,u=n.getAttribute("pulse-field-name")?.trim();if(!u)return;let a=A(n),c=J(n),g=v(s,n),F=e?b(s,a,g,n):null;r.push({"form-block":s,"field-name":u,value:g,required:a,"mirror-value":c,error:F})}),r}function R(){document.querySelectorAll(`.${p}`).forEach(e=>e.remove()),document.querySelectorAll("[pulse-form-block]").forEach(e=>e.classList.remove("has-error"))}function K(e){let t=e.querySelector(`.${p}`);t&&t.remove(),e.classList.remove("has-error")}function H(e,t){let r=e.querySelector(`.${p}`);r&&r.remove();let n=document.createElement("span");n.className=p,n.textContent=t.message,n.setAttribute("data-error-code",t.code),n.setAttribute("data-field-name",e.getAttribute("pulse-field-name")?.trim()||""),e.classList.add("has-error"),e.appendChild(n)}function Z(e){R(),document.querySelectorAll("[pulse-form-block]").forEach(r=>{let n=r.getAttribute("pulse-field-name")?.trim();if(!n)return;let o=e.find(s=>s["field-name"]===n);!o||!o.error||H(r,o.error)})}function ee(){let e=document.querySelector("[pulse-form-block].has-error");if(!e)return;e.scrollIntoView({behavior:"smooth",block:"center"});let t=e.querySelector("input, textarea, select");t&&setTimeout(()=>t.focus(),400)}function m(){let e=i.filter(r=>r["mirror-value"]===!0);if(e.length===0)return;let t=document.querySelectorAll("[pulse-form-block-mirror]");e.forEach(r=>{let n=r["field-name"],o=!1;t.forEach(s=>{if(s.getAttribute("pulse-form-block-mirror")?.trim()!==n)return;o=!0;let a=s.querySelector("input, textarea, select");if(!a){console.warn(`[PulseForm] Mirror target for "${n}" found, but no input element inside it.`);return}let c=r.value;typeof c=="string"?a.value=c:typeof c=="boolean"?a instanceof HTMLInputElement&&a.type==="checkbox"?a.checked=c:a.value=String(c):Array.isArray(c)?a.value=c.join(", "):a.value=""}),o||console.warn(`[PulseForm] No mirror target found for field "${n}". Add an element with attribute pulse-form-block-mirror="${n}" to mirror this value.`)})}function te(){let e=document.querySelector("[hs-calender-block]");if(!e){console.warn("[PulseForm] No element with attribute 'hs-calender-block' found.");return}let r=i.find(u=>u["field-name"]==="flow-type")?.value,n;if(r==="Pulse demo")n=h.find(u=>u.type==="pulse");else if(r==="White Glove services"){let a=i.find(c=>c["field-name"]==="industry")?.value;typeof a=="string"&&a&&(n=h.find(c=>c.type==="industry"&&c.name.toLowerCase()===a.toLowerCase()))}if(n||(n=h.find(u=>u.type==="industry"&&u.name==="Other")),!n){console.warn("[PulseForm] No matching calendar entry found.");return}e.innerHTML="";let o=document.createElement("div");o.className="meetings-iframe-container",o.setAttribute("data-src",n["embed-url"]),e.appendChild(o);let s=document.createElement("script");s.type="text/javascript",s.src="https://static.hsappstatic.net/MeetingsEmbed/ex/MeetingsEmbedCode.js",e.appendChild(s),console.log(`[PulseForm] Loaded calendar: ${n["calender-name"]} (${n.name})`)}function E(){let e=i.find(n=>n["field-name"]==="flow-type");if(!e)return;let t=e.value;document.querySelectorAll("[form-flow]").forEach(n=>{n.getAttribute("form-flow")?.trim()===t?n.setAttribute("form-flow-active","true"):n.setAttribute("form-flow-active","false")})}function f(){let e=i.find(o=>o["field-name"]==="flow-type");if(!e)return;let t=e.value,r=i.findIndex(o=>o["field-name"]==="industry"),n=i.findIndex(o=>o["field-name"]==="what-are-you-looking-for");r!==-1&&(i[r]={...i[r],required:null}),n!==-1&&(i[n]={...i[n],required:null}),t==="White Glove services"&&r!==-1?i[r]={...i[r],required:"true"}:t==="Pulse demo"&&n!==-1&&(i[n]={...i[n],required:"atleast-one"}),window.PulseFormData=i}var i=[];function L(e,t,r){let n=i.findIndex(o=>o["field-name"]===e);n!==-1&&(i[n]={...i[n],value:t,error:r},window.PulseFormData=i,f(),m(),E())}function ne(e){let t=e.getAttribute("pulse-form-block")?.trim().toLowerCase(),r=e.getAttribute("pulse-field-name")?.trim();if(!r)return;let n=A(e),o=v(t,e);if(e.classList.contains("has-error")){let u=b(t,n,o,e);u?(H(e,u),L(r,o,u)):(K(e),L(r,o,null))}else L(r,o,null)}function re(){document.querySelectorAll("[pulse-form-block]").forEach(t=>{let r=t.getAttribute("pulse-form-block")?.trim().toLowerCase();if(!r||!_.includes(r))return;let n=()=>ne(t);switch(r){case"text":case"email":{let o=t.querySelector("input, textarea");o&&(o.addEventListener("input",n),o.addEventListener("change",n));break}case"phone":{let o=t.querySelector('input[type="tel"]');o&&(o.addEventListener("input",n),o.addEventListener("change",n),o.addEventListener("keyup",n),o.addEventListener("countrychange",n));break}case"checkbox":case"legal":{t.querySelectorAll('input[type="checkbox"]').forEach(o=>o.addEventListener("change",n));break}case"radio":{t.querySelectorAll('input[type="radio"]').forEach(o=>o.addEventListener("change",n));break}case"select":{let o=t.querySelector("select");o&&o.addEventListener("change",n);break}}})}var O={get fields(){return i},refresh(){return i=y(!1),window.PulseFormData=i,f(),m(),E(),i},validate(){i=y(!0),window.PulseFormData=i,f();let t=document.querySelectorAll("[pulse-form-block]");i.forEach((n,o)=>{let s=Array.from(t).find(a=>a.getAttribute("pulse-field-name")?.trim()===n["field-name"]);if(!s)return;let u=b(n["form-block"],n.required,n.value,s);i[o]={...i[o],error:u}}),window.PulseFormData=i;let r=i.every(n=>n.error===null);return m(),E(),Z(i),r||ee(),{fields:i,isValid:r}},clearErrors(){R(),i=i.map(e=>({...e,error:null})),window.PulseFormData=i,f(),m(),E()},ERROR_CODES:l,ERROR_MESSAGES:d};document.addEventListener("DOMContentLoaded",()=>{let e=document.querySelectorAll("[pulse-form-block]").length;console.log(`[PulseForm] Initialized \u2014 found ${e} form blocks.`),V(),i=y(!1),window.PulseFormData=i,f(),m(),E(),re();let t=document.getElementById("trigger-webflow-form-submit");t&&t.addEventListener("click",r=>{r.preventDefault();let{isValid:n}=O.validate();if(!n)return;let o=t.textContent;t.textContent="Submitting...",te();let s=document.getElementById("webflow-form-submit");s?s.click():console.warn('[PulseForm] Button with id "webflow-form-submit" not found.');let u=document.getElementById("hubspot-form-submit");u?u.click():console.warn('[PulseForm] Button with id "hubspot-form-submit" not found.'),setTimeout(()=>{window.scrollTo({top:0,behavior:"smooth"})},1200)}),window.PulseForm=O});})();
1
+ "use strict";
2
+ (() => {
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
10
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
11
+ }) : x)(function(x) {
12
+ if (typeof require !== "undefined") return require.apply(this, arguments);
13
+ throw Error('Dynamic require of "' + x + '" is not supported');
14
+ });
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
31
+
32
+ // src/form/book-a-demo.ts
33
+ console.log("Book a demo form script loaded");
34
+ var VALID_BLOCK_TYPES = [
35
+ "text",
36
+ "email",
37
+ "phone",
38
+ "checkbox",
39
+ "radio",
40
+ "select",
41
+ "legal"
42
+ ];
43
+ var CALENDAR_ENTRIES = [
44
+ {
45
+ type: "pulse",
46
+ name: "pulse",
47
+ "calender-name": "david",
48
+ "embed-url": "https://meetings.hubspot.com/valeria-angelini/pulse-valeria-and-alessandra?embed=true"
49
+ },
50
+ {
51
+ type: "industry",
52
+ name: "Financial Services",
53
+ "calender-name": "david",
54
+ "embed-url": "https://meetings.hubspot.com/david-zarate2?embed=true"
55
+ },
56
+ {
57
+ type: "industry",
58
+ name: "Sports",
59
+ "calender-name": "steven",
60
+ "embed-url": "https://meetings.hubspot.com/steven-campos?embed=true"
61
+ },
62
+ {
63
+ type: "industry",
64
+ name: "Beauty",
65
+ "calender-name": "albany",
66
+ "embed-url": "https://meetings.hubspot.com/david-zarate2?embed=true"
67
+ },
68
+ {
69
+ type: "industry",
70
+ name: "Music",
71
+ "calender-name": "steven",
72
+ "embed-url": "https://meetings.hubspot.com/steven-campos?embed=true"
73
+ },
74
+ {
75
+ type: "industry",
76
+ name: "Customer Packaged Goods",
77
+ "calender-name": "david",
78
+ "embed-url": "https://meetings.hubspot.com/david-zarate2?embed=true"
79
+ },
80
+ {
81
+ type: "industry",
82
+ name: "Other",
83
+ "calender-name": "david",
84
+ "embed-url": "https://meetings.hubspot.com/david-zarate2?embed=true"
85
+ }
86
+ ];
87
+ var VALID_REQUIRED_VALUES = [
88
+ "true",
89
+ "atleast-one",
90
+ "only-one"
91
+ ];
92
+ var ERROR_CODES = {
93
+ REQUIRED_INPUT: "ERR_REQUIRED",
94
+ REQUIRED_LEGAL: "ERR_LEGAL_REQUIRED",
95
+ REQUIRED_SELECT: "ERR_SELECT_REQUIRED",
96
+ ATLEAST_ONE: "ERR_ATLEAST_ONE",
97
+ ONLY_ONE_NONE: "ERR_ONLY_ONE_NONE",
98
+ ONLY_ONE_EXCEEDED: "ERR_ONLY_ONE_EXCEEDED",
99
+ INVALID_EMAIL: "ERR_INVALID_EMAIL",
100
+ INVALID_PHONE: "ERR_INVALID_PHONE",
101
+ PHONE_TOO_SHORT: "ERR_PHONE_TOO_SHORT",
102
+ PHONE_TOO_LONG: "ERR_PHONE_TOO_LONG",
103
+ PHONE_INVALID_COUNTRY: "ERR_PHONE_INVALID_COUNTRY"
104
+ };
105
+ var ERROR_MESSAGES = {
106
+ [ERROR_CODES.REQUIRED_INPUT]: "This field is required.",
107
+ [ERROR_CODES.REQUIRED_LEGAL]: "You must accept this to continue.",
108
+ [ERROR_CODES.REQUIRED_SELECT]: "Please select an option.",
109
+ [ERROR_CODES.ATLEAST_ONE]: "Please select at least one option.",
110
+ [ERROR_CODES.ONLY_ONE_NONE]: "Please select one option.",
111
+ [ERROR_CODES.ONLY_ONE_EXCEEDED]: "Only one option can be selected.",
112
+ [ERROR_CODES.INVALID_EMAIL]: "Please enter a valid email address.",
113
+ [ERROR_CODES.INVALID_PHONE]: "Please enter a valid phone number.",
114
+ [ERROR_CODES.PHONE_TOO_SHORT]: "Phone number is too short.",
115
+ [ERROR_CODES.PHONE_TOO_LONG]: "Phone number is too long.",
116
+ [ERROR_CODES.PHONE_INVALID_COUNTRY]: "Invalid country code."
117
+ };
118
+ var ERROR_SPAN_CLASS = "pulse-form-error";
119
+ var itiInstances = /* @__PURE__ */ new Map();
120
+ function fillCountryCodeInput(iti) {
121
+ try {
122
+ const countryData = iti.getSelectedCountryData?.();
123
+ if (!countryData?.dialCode) return;
124
+ const inputs = document.querySelectorAll(
125
+ 'input[pulse-form-field="country-code"]'
126
+ );
127
+ inputs.forEach((input) => {
128
+ input.value = `+${countryData.dialCode}`;
129
+ });
130
+ } catch (err) {
131
+ console.warn("[PulseForm] Error filling country code:", err);
132
+ }
133
+ }
134
+ function initIntlTelInput() {
135
+ const intlTelInput = window.intlTelInput;
136
+ if (!intlTelInput) {
137
+ console.warn(
138
+ '[PulseForm] intlTelInput not found on window. Make sure you include the intl-tel-input script before this script.\ne.g. <script src="https://cdn.jsdelivr.net/npm/intl-tel-input@25/build/js/intlTelInput.min.js"><\/script>'
139
+ );
140
+ return;
141
+ }
142
+ const phoneInputs = document.querySelectorAll(
143
+ '[pulse-form-block="phone"] input[type="tel"]'
144
+ );
145
+ phoneInputs.forEach((input) => {
146
+ try {
147
+ const iti = intlTelInput(input, {
148
+ initialCountry: "us",
149
+ separateDialCode: true,
150
+ strictMode: false,
151
+ // @ts-ignore — runtime CDN import, not a TS module
152
+ loadUtils: () => import("https://cdn.jsdelivr.net/npm/intl-tel-input@25/build/js/utils.js")
153
+ });
154
+ itiInstances.set(input, iti);
155
+ fillCountryCodeInput(iti);
156
+ input.addEventListener("countrychange", () => {
157
+ fillCountryCodeInput(iti);
158
+ });
159
+ console.log(
160
+ `[PulseForm] intl-tel-input initialized for "${input.closest("[pulse-form-block]")?.getAttribute("pulse-field-name") || "unknown"}"`
161
+ );
162
+ } catch (err) {
163
+ console.warn("[PulseForm] Failed to init intl-tel-input:", err);
164
+ }
165
+ });
166
+ }
167
+ var PHONE_ITI_ERROR_MAP = {
168
+ 0: ERROR_CODES.INVALID_PHONE,
169
+ 1: ERROR_CODES.PHONE_INVALID_COUNTRY,
170
+ 2: ERROR_CODES.PHONE_TOO_SHORT,
171
+ 3: ERROR_CODES.PHONE_TOO_LONG,
172
+ 4: ERROR_CODES.INVALID_PHONE
173
+ };
174
+ function validateEmail(value) {
175
+ if (!value) return null;
176
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
177
+ if (!emailRegex.test(value)) {
178
+ return { code: ERROR_CODES.INVALID_EMAIL, message: ERROR_MESSAGES[ERROR_CODES.INVALID_EMAIL] };
179
+ }
180
+ return null;
181
+ }
182
+ function validatePhone(input) {
183
+ const value = input.value.trim();
184
+ if (!value) return null;
185
+ const iti = itiInstances.get(input);
186
+ if (iti) {
187
+ try {
188
+ if (iti.isValidNumber()) return null;
189
+ const errorCode = iti.getValidationError?.() ?? 0;
190
+ const code = PHONE_ITI_ERROR_MAP[errorCode] || ERROR_CODES.INVALID_PHONE;
191
+ return { code, message: ERROR_MESSAGES[code] };
192
+ } catch {
193
+ return { code: ERROR_CODES.INVALID_PHONE, message: ERROR_MESSAGES[ERROR_CODES.INVALID_PHONE] };
194
+ }
195
+ }
196
+ const digits = value.replace(/\D/g, "");
197
+ if (digits.length < 7)
198
+ return { code: ERROR_CODES.PHONE_TOO_SHORT, message: ERROR_MESSAGES[ERROR_CODES.PHONE_TOO_SHORT] };
199
+ if (digits.length > 15)
200
+ return { code: ERROR_CODES.PHONE_TOO_LONG, message: ERROR_MESSAGES[ERROR_CODES.PHONE_TOO_LONG] };
201
+ return null;
202
+ }
203
+ function extractTextValue(wrapper) {
204
+ const input = wrapper.querySelector(
205
+ "input, textarea"
206
+ );
207
+ if (!input) return null;
208
+ return input.value.trim();
209
+ }
210
+ function extractEmailValue(wrapper) {
211
+ const input = wrapper.querySelector('input[type="email"]');
212
+ if (!input) return null;
213
+ return input.value.trim();
214
+ }
215
+ function extractPhoneValue(wrapper) {
216
+ const input = wrapper.querySelector('input[type="tel"]');
217
+ if (!input) return null;
218
+ return input.value.trim();
219
+ }
220
+ function extractCheckboxValues(wrapper) {
221
+ const checkboxes = wrapper.querySelectorAll(
222
+ 'input[type="checkbox"]'
223
+ );
224
+ const values = [];
225
+ checkboxes.forEach((cb) => {
226
+ if (cb.checked) {
227
+ const name = cb.getAttribute("data-name")?.trim();
228
+ values.push(name || cb.value || "on");
229
+ }
230
+ });
231
+ return values;
232
+ }
233
+ function extractRadioValue(wrapper) {
234
+ const radios = wrapper.querySelectorAll(
235
+ 'input[type="radio"]'
236
+ );
237
+ for (const radio of radios) {
238
+ if (radio.checked) {
239
+ const label = radio.closest("label")?.textContent?.trim();
240
+ return label || radio.value || "on";
241
+ }
242
+ }
243
+ return null;
244
+ }
245
+ function extractSelectValue(wrapper) {
246
+ const select = wrapper.querySelector("select");
247
+ if (!select) return null;
248
+ const val = select.value.trim();
249
+ if (!val || select.selectedIndex === 0) return null;
250
+ return val;
251
+ }
252
+ function extractLegalValue(wrapper) {
253
+ const checkbox = wrapper.querySelector(
254
+ 'input[type="checkbox"]'
255
+ );
256
+ if (!checkbox) return false;
257
+ return checkbox.checked;
258
+ }
259
+ function extractValue(type, wrapper) {
260
+ switch (type) {
261
+ case "text":
262
+ return extractTextValue(wrapper);
263
+ case "email":
264
+ return extractEmailValue(wrapper);
265
+ case "phone":
266
+ return extractPhoneValue(wrapper);
267
+ case "checkbox":
268
+ return extractCheckboxValues(wrapper);
269
+ case "radio":
270
+ return extractRadioValue(wrapper);
271
+ case "select":
272
+ return extractSelectValue(wrapper);
273
+ case "legal":
274
+ return extractLegalValue(wrapper);
275
+ default:
276
+ return null;
277
+ }
278
+ }
279
+ function parseRequired(wrapper) {
280
+ const raw = wrapper.getAttribute("required-selection")?.trim().toLowerCase();
281
+ if (!raw || !VALID_REQUIRED_VALUES.includes(raw))
282
+ return null;
283
+ return raw;
284
+ }
285
+ function parseMirrorValue(wrapper) {
286
+ return wrapper.getAttribute("mirror-value")?.trim().toLowerCase() === "true";
287
+ }
288
+ function getFieldError(type, required, value, wrapper) {
289
+ if (required) {
290
+ switch (required) {
291
+ case "true": {
292
+ if (type === "legal" && value !== true)
293
+ return { code: ERROR_CODES.REQUIRED_LEGAL, message: ERROR_MESSAGES[ERROR_CODES.REQUIRED_LEGAL] };
294
+ if (type === "select" && !value)
295
+ return { code: ERROR_CODES.REQUIRED_SELECT, message: ERROR_MESSAGES[ERROR_CODES.REQUIRED_SELECT] };
296
+ if ((type === "text" || type === "email" || type === "phone") && (!value || typeof value === "string" && value === ""))
297
+ return { code: ERROR_CODES.REQUIRED_INPUT, message: ERROR_MESSAGES[ERROR_CODES.REQUIRED_INPUT] };
298
+ break;
299
+ }
300
+ case "atleast-one": {
301
+ const empty = Array.isArray(value) ? value.length === 0 : !value;
302
+ if (empty)
303
+ return { code: ERROR_CODES.ATLEAST_ONE, message: ERROR_MESSAGES[ERROR_CODES.ATLEAST_ONE] };
304
+ break;
305
+ }
306
+ case "only-one": {
307
+ if (Array.isArray(value)) {
308
+ if (value.length === 0)
309
+ return { code: ERROR_CODES.ONLY_ONE_NONE, message: ERROR_MESSAGES[ERROR_CODES.ONLY_ONE_NONE] };
310
+ if (value.length > 1)
311
+ return { code: ERROR_CODES.ONLY_ONE_EXCEEDED, message: ERROR_MESSAGES[ERROR_CODES.ONLY_ONE_EXCEEDED] };
312
+ } else if (!value) {
313
+ return { code: ERROR_CODES.ONLY_ONE_NONE, message: ERROR_MESSAGES[ERROR_CODES.ONLY_ONE_NONE] };
314
+ }
315
+ break;
316
+ }
317
+ }
318
+ }
319
+ if (type === "email" && typeof value === "string" && value) {
320
+ const emailError = validateEmail(value);
321
+ if (emailError) return emailError;
322
+ }
323
+ if (type === "phone" && typeof value === "string" && value) {
324
+ const phoneInput = wrapper.querySelector('input[type="tel"]');
325
+ if (phoneInput) {
326
+ const phoneError = validatePhone(phoneInput);
327
+ if (phoneError) return phoneError;
328
+ }
329
+ }
330
+ return null;
331
+ }
332
+ function collectFormFields(withValidation) {
333
+ const wrappers = document.querySelectorAll("[pulse-form-block]");
334
+ const fields = [];
335
+ wrappers.forEach((wrapper) => {
336
+ const rawType = wrapper.getAttribute("pulse-form-block")?.trim().toLowerCase();
337
+ if (!rawType || !VALID_BLOCK_TYPES.includes(rawType))
338
+ return;
339
+ const blockType = rawType;
340
+ const fieldName = wrapper.getAttribute("pulse-field-name")?.trim();
341
+ if (!fieldName) return;
342
+ const required = parseRequired(wrapper);
343
+ const mirrorValue = parseMirrorValue(wrapper);
344
+ const value = extractValue(blockType, wrapper);
345
+ const error = withValidation ? getFieldError(blockType, required, value, wrapper) : null;
346
+ fields.push({
347
+ "form-block": blockType,
348
+ "field-name": fieldName,
349
+ value,
350
+ required,
351
+ "mirror-value": mirrorValue,
352
+ error
353
+ });
354
+ });
355
+ return fields;
356
+ }
357
+ function clearAllErrors() {
358
+ document.querySelectorAll(`.${ERROR_SPAN_CLASS}`).forEach((el) => el.remove());
359
+ document.querySelectorAll("[pulse-form-block]").forEach((el) => el.classList.remove("has-error"));
360
+ }
361
+ function clearErrorOnWrapper(wrapper) {
362
+ const span = wrapper.querySelector(`.${ERROR_SPAN_CLASS}`);
363
+ if (span) span.remove();
364
+ wrapper.classList.remove("has-error");
365
+ }
366
+ function showErrorOnWrapper(wrapper, error) {
367
+ const existing = wrapper.querySelector(`.${ERROR_SPAN_CLASS}`);
368
+ if (existing) existing.remove();
369
+ const span = document.createElement("span");
370
+ span.className = ERROR_SPAN_CLASS;
371
+ span.textContent = error.message;
372
+ span.setAttribute("data-error-code", error.code);
373
+ span.setAttribute(
374
+ "data-field-name",
375
+ wrapper.getAttribute("pulse-field-name")?.trim() || ""
376
+ );
377
+ wrapper.classList.add("has-error");
378
+ wrapper.appendChild(span);
379
+ }
380
+ function renderErrors(fields) {
381
+ clearAllErrors();
382
+ const wrappers = document.querySelectorAll("[pulse-form-block]");
383
+ wrappers.forEach((wrapper) => {
384
+ const fieldName = wrapper.getAttribute("pulse-field-name")?.trim();
385
+ if (!fieldName) return;
386
+ const field = fields.find((f) => f["field-name"] === fieldName);
387
+ if (!field || !field.error) return;
388
+ showErrorOnWrapper(wrapper, field.error);
389
+ });
390
+ }
391
+ function focusFirstError() {
392
+ const first = document.querySelector(
393
+ `[pulse-form-block].has-error`
394
+ );
395
+ if (!first) return;
396
+ first.scrollIntoView({ behavior: "smooth", block: "center" });
397
+ const focusable = first.querySelector(
398
+ "input, textarea, select"
399
+ );
400
+ if (focusable) setTimeout(() => focusable.focus(), 400);
401
+ }
402
+ function fillMirrorValues() {
403
+ const mirrorFields = _currentFields.filter((f) => f["mirror-value"] === true);
404
+ if (mirrorFields.length === 0) return;
405
+ const mirrorTargets = document.querySelectorAll("[pulse-form-block-mirror]");
406
+ mirrorFields.forEach((field) => {
407
+ const fieldName = field["field-name"];
408
+ let found = false;
409
+ mirrorTargets.forEach((target) => {
410
+ const mirrorAttr = target.getAttribute("pulse-form-block-mirror")?.trim();
411
+ if (mirrorAttr !== fieldName) return;
412
+ found = true;
413
+ const input = target.querySelector(
414
+ "input, textarea, select"
415
+ );
416
+ if (!input) {
417
+ console.warn(
418
+ `[PulseForm] Mirror target for "${fieldName}" found, but no input element inside it.`
419
+ );
420
+ return;
421
+ }
422
+ const value = field.value;
423
+ if (typeof value === "string") {
424
+ input.value = value;
425
+ } else if (typeof value === "boolean") {
426
+ if (input instanceof HTMLInputElement && input.type === "checkbox") {
427
+ input.checked = value;
428
+ } else {
429
+ input.value = String(value);
430
+ }
431
+ } else if (Array.isArray(value)) {
432
+ input.value = value.join(", ");
433
+ } else {
434
+ input.value = "";
435
+ }
436
+ });
437
+ if (!found) {
438
+ console.warn(
439
+ `[PulseForm] No mirror target found for field "${fieldName}". Add an element with attribute pulse-form-block-mirror="${fieldName}" to mirror this value.`
440
+ );
441
+ }
442
+ });
443
+ }
444
+ function loadCalendarEmbed() {
445
+ const container = document.querySelector("[hs-calender-block]");
446
+ if (!container) {
447
+ console.warn("[PulseForm] No element with attribute 'hs-calender-block' found.");
448
+ return;
449
+ }
450
+ const flowField = _currentFields.find((f) => f["field-name"] === "flow-type");
451
+ const flowValue = flowField?.value;
452
+ let entry;
453
+ if (flowValue === "Pulse demo") {
454
+ entry = CALENDAR_ENTRIES.find((e) => e.type === "pulse");
455
+ } else if (flowValue === "White Glove services") {
456
+ const industryField = _currentFields.find((f) => f["field-name"] === "industry");
457
+ const industryValue = industryField?.value;
458
+ if (typeof industryValue === "string" && industryValue) {
459
+ entry = CALENDAR_ENTRIES.find(
460
+ (e) => e.type === "industry" && e.name.toLowerCase() === industryValue.toLowerCase()
461
+ );
462
+ }
463
+ }
464
+ if (!entry) {
465
+ entry = CALENDAR_ENTRIES.find((e) => e.type === "industry" && e.name === "Other");
466
+ }
467
+ if (!entry) {
468
+ console.warn("[PulseForm] No matching calendar entry found.");
469
+ return;
470
+ }
471
+ container.innerHTML = "";
472
+ const meetingsDiv = document.createElement("div");
473
+ meetingsDiv.className = "meetings-iframe-container";
474
+ meetingsDiv.setAttribute("data-src", entry["embed-url"]);
475
+ container.appendChild(meetingsDiv);
476
+ const script = document.createElement("script");
477
+ script.type = "text/javascript";
478
+ script.src = "https://static.hsappstatic.net/MeetingsEmbed/ex/MeetingsEmbedCode.js";
479
+ container.appendChild(script);
480
+ console.log(`[PulseForm] Loaded calendar: ${entry["calender-name"]} (${entry.name})`);
481
+ }
482
+ function updateFlowType() {
483
+ const flowField = _currentFields.find((f) => f["field-name"] === "flow-type");
484
+ if (!flowField) return;
485
+ const flowValue = flowField.value;
486
+ const flowElements = document.querySelectorAll("[form-flow]");
487
+ flowElements.forEach((el) => {
488
+ const elFlowValue = el.getAttribute("form-flow")?.trim();
489
+ if (elFlowValue === flowValue) {
490
+ el.setAttribute("form-flow-active", "true");
491
+ } else {
492
+ el.setAttribute("form-flow-active", "false");
493
+ }
494
+ });
495
+ }
496
+ function updateConditionalRequired() {
497
+ const flowField = _currentFields.find((f) => f["field-name"] === "flow-type");
498
+ if (!flowField) return;
499
+ const flowValue = flowField.value;
500
+ const industryIdx = _currentFields.findIndex((f) => f["field-name"] === "industry");
501
+ const lookingForIdx = _currentFields.findIndex((f) => f["field-name"] === "what-are-you-looking-for");
502
+ if (industryIdx !== -1) {
503
+ _currentFields[industryIdx] = { ..._currentFields[industryIdx], required: null };
504
+ }
505
+ if (lookingForIdx !== -1) {
506
+ _currentFields[lookingForIdx] = { ..._currentFields[lookingForIdx], required: null };
507
+ }
508
+ if (flowValue === "White Glove services" && industryIdx !== -1) {
509
+ _currentFields[industryIdx] = { ..._currentFields[industryIdx], required: "true" };
510
+ } else if (flowValue === "Pulse demo" && lookingForIdx !== -1) {
511
+ _currentFields[lookingForIdx] = { ..._currentFields[lookingForIdx], required: "atleast-one" };
512
+ }
513
+ window.PulseFormData = _currentFields;
514
+ }
515
+ var _currentFields = [];
516
+ function updateFieldInState(fieldName, value, error) {
517
+ const idx = _currentFields.findIndex((f) => f["field-name"] === fieldName);
518
+ if (idx === -1) return;
519
+ _currentFields[idx] = { ..._currentFields[idx], value, error };
520
+ window.PulseFormData = _currentFields;
521
+ updateConditionalRequired();
522
+ fillMirrorValues();
523
+ updateFlowType();
524
+ }
525
+ function handleFieldChange(wrapper) {
526
+ const rawType = wrapper.getAttribute("pulse-form-block")?.trim().toLowerCase();
527
+ const fieldName = wrapper.getAttribute("pulse-field-name")?.trim();
528
+ if (!fieldName) return;
529
+ const required = parseRequired(wrapper);
530
+ const value = extractValue(rawType, wrapper);
531
+ const hasError = wrapper.classList.contains("has-error");
532
+ if (hasError) {
533
+ const error = getFieldError(rawType, required, value, wrapper);
534
+ if (!error) {
535
+ clearErrorOnWrapper(wrapper);
536
+ updateFieldInState(fieldName, value, null);
537
+ } else {
538
+ showErrorOnWrapper(wrapper, error);
539
+ updateFieldInState(fieldName, value, error);
540
+ }
541
+ } else {
542
+ updateFieldInState(fieldName, value, null);
543
+ }
544
+ }
545
+ function bindChangeListeners() {
546
+ const wrappers = document.querySelectorAll("[pulse-form-block]");
547
+ wrappers.forEach((wrapper) => {
548
+ const type = wrapper.getAttribute("pulse-form-block")?.trim().toLowerCase();
549
+ if (!type || !VALID_BLOCK_TYPES.includes(type)) return;
550
+ const onChange = () => handleFieldChange(wrapper);
551
+ switch (type) {
552
+ case "text":
553
+ case "email": {
554
+ const input = wrapper.querySelector(
555
+ "input, textarea"
556
+ );
557
+ if (input) {
558
+ input.addEventListener("input", onChange);
559
+ input.addEventListener("change", onChange);
560
+ }
561
+ break;
562
+ }
563
+ case "phone": {
564
+ const phoneInput = wrapper.querySelector('input[type="tel"]');
565
+ if (phoneInput) {
566
+ phoneInput.addEventListener("input", onChange);
567
+ phoneInput.addEventListener("change", onChange);
568
+ phoneInput.addEventListener("keyup", onChange);
569
+ phoneInput.addEventListener("countrychange", onChange);
570
+ }
571
+ break;
572
+ }
573
+ case "checkbox":
574
+ case "legal": {
575
+ wrapper.querySelectorAll('input[type="checkbox"]').forEach((cb) => cb.addEventListener("change", onChange));
576
+ break;
577
+ }
578
+ case "radio": {
579
+ wrapper.querySelectorAll('input[type="radio"]').forEach((r) => r.addEventListener("change", onChange));
580
+ break;
581
+ }
582
+ case "select": {
583
+ const select = wrapper.querySelector("select");
584
+ if (select) select.addEventListener("change", onChange);
585
+ break;
586
+ }
587
+ }
588
+ });
589
+ }
590
+ var PulseForm = {
591
+ get fields() {
592
+ return _currentFields;
593
+ },
594
+ refresh() {
595
+ _currentFields = collectFormFields(false);
596
+ window.PulseFormData = _currentFields;
597
+ updateConditionalRequired();
598
+ fillMirrorValues();
599
+ updateFlowType();
600
+ return _currentFields;
601
+ },
602
+ validate() {
603
+ const fields = collectFormFields(true);
604
+ _currentFields = fields;
605
+ window.PulseFormData = _currentFields;
606
+ updateConditionalRequired();
607
+ const wrappers = document.querySelectorAll("[pulse-form-block]");
608
+ _currentFields.forEach((field, idx) => {
609
+ const wrapper = Array.from(wrappers).find(
610
+ (w) => w.getAttribute("pulse-field-name")?.trim() === field["field-name"]
611
+ );
612
+ if (!wrapper) return;
613
+ const error = getFieldError(
614
+ field["form-block"],
615
+ field.required,
616
+ field.value,
617
+ wrapper
618
+ );
619
+ _currentFields[idx] = { ..._currentFields[idx], error };
620
+ });
621
+ window.PulseFormData = _currentFields;
622
+ const isValid = _currentFields.every((f) => f.error === null);
623
+ fillMirrorValues();
624
+ updateFlowType();
625
+ renderErrors(_currentFields);
626
+ if (!isValid) focusFirstError();
627
+ return { fields: _currentFields, isValid };
628
+ },
629
+ clearErrors() {
630
+ clearAllErrors();
631
+ _currentFields = _currentFields.map((f) => ({ ...f, error: null }));
632
+ window.PulseFormData = _currentFields;
633
+ updateConditionalRequired();
634
+ fillMirrorValues();
635
+ updateFlowType();
636
+ },
637
+ ERROR_CODES,
638
+ ERROR_MESSAGES
639
+ };
640
+ document.addEventListener("DOMContentLoaded", () => {
641
+ const count = document.querySelectorAll("[pulse-form-block]").length;
642
+ console.log(`[PulseForm] Initialized \u2014 found ${count} form blocks.`);
643
+ initIntlTelInput();
644
+ _currentFields = collectFormFields(false);
645
+ window.PulseFormData = _currentFields;
646
+ updateConditionalRequired();
647
+ fillMirrorValues();
648
+ updateFlowType();
649
+ bindChangeListeners();
650
+ const triggerBtn = document.getElementById("trigger-webflow-form-submit");
651
+ if (triggerBtn) {
652
+ triggerBtn.addEventListener("click", (e) => {
653
+ e.preventDefault();
654
+ const { isValid } = PulseForm.validate();
655
+ if (!isValid) return;
656
+ const originalText = triggerBtn.textContent;
657
+ triggerBtn.textContent = "Submitting...";
658
+ loadCalendarEmbed();
659
+ const submitBtn = document.getElementById("webflow-form-submit");
660
+ if (submitBtn) {
661
+ submitBtn.click();
662
+ } else {
663
+ console.warn('[PulseForm] Button with id "webflow-form-submit" not found.');
664
+ }
665
+ const hubspotBtn = document.getElementById("hubspot-form-submit");
666
+ if (hubspotBtn) {
667
+ hubspotBtn.click();
668
+ } else {
669
+ console.warn('[PulseForm] Button with id "hubspot-form-submit" not found.');
670
+ }
671
+ setTimeout(() => {
672
+ window.scrollTo({ top: 0, behavior: "smooth" });
673
+ }, 1200);
674
+ });
675
+ }
676
+ window.PulseForm = PulseForm;
677
+ });
678
+ })();
679
+ //# sourceMappingURL=book-a-demo.js.map