PageSourceSearch

https://itlearn360.com/dist/js/components/registrationForm.js?v=20260915-contact-v319

js itlearn360.com collected 2026-10-10 00:10:30 UTC 10,747 bytes, 240 lines download raw bytes

1function escReg(value) {
2  return String(value ?? "")
3    .replaceAll("&", "&")
4    .replaceAll("<", "&lt;")
5    .replaceAll(">", "&gt;")
6    .replaceAll('"', "&quot;")
7    .replaceAll("'", "&#039;");
8}
9
10
11const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/i;
12const US_PHONE_REGEX = /^(?:\+?1[\s.-]?)?(?:\([0-9]{3}\)|[0-9]{3})[\s.-]?[0-9]{3}[\s.-]?[0-9]{4}$/;
13
14function normalizeUsPhone(value) {
15  const digits = String(value || "").replace(/\D/g, "");
16  const ten = digits.length === 11 && digits.startsWith("1") ? digits.slice(1) : digits;
17  if (ten.length !== 10) return String(value || "").trim();
18  return `(${ten.slice(0, 3)}) ${ten.slice(3, 6)}-${ten.slice(6)}`;
19}
20
21function isValidEmail(value) {
22  return EMAIL_REGEX.test(String(value || "").trim());
23}
24
25function isValidUsPhone(value) {
26  const phone = String(value || "").trim();
27  if (!phone) return true;
28  return US_PHONE_REGEX.test(phone);
29}
30
31function regInputStyle(theme = "dark") {
32  if (theme === "light") {
33    return `width:100%;box-sizing:border-box;border:1px solid #cbd5e1;background:#ffffff;color:#0f172a;border-radius:14px;padding:13px 14px;outline:none;font-size:14px;font-weight:650;font-family:inherit;box-shadow:0 1px 2px rgba(15,23,42,.04);`;
34  }
35  return `width:100%;box-sizing:border-box;border:1px solid rgba(255,255,255,.22);background:rgba(255,255,255,.12);color:white;border-radius:14px;padding:13px 14px;outline:none;font-size:14px;font-weight:650;font-family:inherit;`;
36}
37
38export const ENQUIRY_COURSE_OPTIONS = [
39  "AI Tools for Work: ChatGPT, Copilot, Excel, Emails & Meetings — Free Demo",
40  "Business Analyst with AI, Agile & Jira — Free Demo for US Learners",
41  "Cybersecurity GRC Analyst with AI Governance & Responsible AI — Live Demo",
42  "Cybersecurity Analyst Foundation: Start with CompTIA Security+ — Live Demo",
43  "Software Testing Demo for US Learners: API Testing, Selenium, Python, JIRA",
44];
45
46export function renderRegistrationForm({
47  pageType = "general",
48  itemId = "",
49  itemTitle = "",
50  title = "Request Registration Info",
51  subtitle = "Share your details and our team will contact you shortly.",
52  compact = false,
53  containerId = "",
54  theme = "dark",
55  showCourseField = false,
56  courseOptions = ENQUIRY_COURSE_OPTIONS,
57  coursePlaceholder = "Course or program you are interested in",
58  badgeLabel = "Free Guidance",
59  submitLabel = "Request Registration Info",
60} = {}) {
61  const idAttr = containerId ? ` id="${escReg(containerId)}"` : "";
62  const isLight = theme === "light";
63
64  const cardStyle = isLight
65    ? `background:#ffffff;border-radius:${compact ? "22px" : "26px"};padding:${compact ? "24px" : "30px"};color:#0f172a;box-shadow:0 18px 45px rgba(15,23,42,.10);border:1px solid #dbeafe;position:relative;overflow:hidden;`
66    : `background:linear-gradient(135deg,#0f172a 0%,#134e4a 55%,#10b981 100%);border-radius:${compact ? "22px" : "26px"};padding:${compact ? "22px" : "30px"};color:white;box-shadow:0 20px 48px rgba(15,23,42,.18);border:1px solid rgba(255,255,255,.14);position:relative;overflow:hidden;`;
67
68  const badgeStyle = isLight
69    ? `display:inline-flex;align-items:center;gap:8px;padding:7px 12px;border-radius:999px;background:#ecfdf5;border:1px solid #bbf7d0;color:#047857;font-size:11px;font-weight:900;text-transform:uppercase;letter-spacing:.08em;margin-bottom:14px;`
70    : `display:inline-flex;align-items:center;gap:8px;padding:7px 12px;border-radius:999px;background:rgba(255,255,255,.14);border:1px solid rgba(255,255,255,.22);font-size:11px;font-weight:900;text-transform:uppercase;letter-spacing:.08em;margin-bottom:14px;`;
71
72  const subtitleColor = isLight ? "#475569" : "rgba(255,255,255,.78)";
73  const programColor = isLight ? "#059669" : "#a7f3d0";
74
75  return `
76    <div${idAttr}
77      class="itl-registration-card ${isLight ? "itl-registration-card-light" : ""}"
78      data-registration-form
79      data-page-type="${escReg(pageType)}"
80      data-item-id="${escReg(itemId)}"
81      data-item-title="${escReg(itemTitle)}"
82      data-theme="${escReg(theme)}"
83      style="${cardStyle}"
84    >
85      <div style="position:absolute;width:220px;height:220px;right:-90px;top:-100px;background:${isLight ? "#dcfce7" : "rgba(255,255,255,.16)"};filter:blur(24px);border-radius:999px;pointer-events:none;opacity:${isLight ? ".65" : "1"};"></div>
86
87      <div style="position:relative;z-index:1;">
88        <div style="${badgeStyle}">${escReg(badgeLabel)}</div>
89
90        <h3 style="margin:0;font-size:${compact ? "22px" : "clamp(24px,3vw,34px)"};line-height:1.12;font-weight:900;letter-spacing:-.03em;color:${isLight ? "#0f172a" : "white"};">
91          ${escReg(title)}
92        </h3>
93
94        <p style="margin:12px 0 0;color:${subtitleColor};font-size:14px;line-height:1.65;font-weight:500;">
95          ${escReg(subtitle)}
96        </p>
97
98        <form data-registration-submit style="display:grid;gap:12px;margin-top:20px;">
99          <input name="name" required placeholder="Full name" autocomplete="name" style="${regInputStyle(theme)}" />
100          <input name="email" type="email" required placeholder="Email address" autocomplete="email" style="${regInputStyle(theme)}" />
101          <input name="phone" placeholder="US phone number, e.g. (703) 555-1212" autocomplete="tel" inputmode="tel" title="Enter a valid US phone number, for example (703) 555-1212 or 703-555-1212" style="${regInputStyle(theme)}" />
102          ${showCourseField
103            ? (Array.isArray(courseOptions) && courseOptions.length
104              ? `<select name="itemTitle" style="${regInputStyle(theme)}">
105                  <option value="">${escReg(coursePlaceholder)}</option>
106                  ${courseOptions.map((c) => `<option value="${escReg(c.title || c.name || c.id || c)}">${escReg(c.title || c.name || c.id || c)}</option>`).join("")}
107                </select>`
108              : `<input name="itemTitle" placeholder="${escReg(coursePlaceholder)}" style="${regInputStyle(theme)}" />`)
109            : ""}
110          <textarea name="message" rows="4" placeholder="Tell us your question" style="${regInputStyle(theme)}resize:vertical;min-height:96px;"></textarea>
111
112          <button
113            type="submit"
114            data-submit-button
115            style="width:100%;border:none;border-radius:16px;padding:15px 18px;background:${isLight ? "#0f172a" : "white"};
115color:${isLight ? "white" : "#0f172a"};font-weight:900;font-size:15px;cursor:pointer;box-shadow:${isLight ? "0 12px 26px rgba(15,23,42,.18)" : "0 14px 30px rgba(0,0,0,.18)"};font-family:inherit;"
116          >
117            ${escReg(submitLabel)}
118          </button>
119
120          <div data-registration-message style="display:none;border-radius:14px;padding:12px;font-size:13px;font-weight:800;line-height:1.45;"></div>
121        </form>
122      </div>
123    </div>`;
124}
125
126export function bindRegistrationForms(root = document) {
127  root.querySelectorAll("[data-registration-form]").forEach((card) => {
128    const form = card.querySelector("[data-registration-submit]");
129    const btn = card.querySelector("[data-submit-button]");
130    const msg = card.querySelector("[data-registration-message]");
131
132    if (!form || form.dataset.bound === "true") return;
133    form.dataset.bound = "true";
134
135    form.addEventListener("submit", async (e) => {
136      e.preventDefault();
137
138      const fd = new FormData(form);
139
140      const submittedItemTitle = String(fd.get("itemTitle") || "").trim();
141
142      const payload = {
143        pageType: card.dataset.pageType || "general",
144        itemId: card.dataset.itemId || "",
145        itemTitle: submittedItemTitle || card.dataset.itemTitle || "",
146        name: String(fd.get("name") || "").trim(),
147        email: String(fd.get("email") || "").trim(),
148        phone: String(fd.get("phone") || "").trim(),
149        message: String(fd.get("message") || "").trim(),
150        sourceUrl: window.location.href,
151        createdFrom: "website",
152      };
153
154      if (!payload.name || !payload.email) {
155        showRegMessage(card, msg, "Please enter your name and email.", "error");
156        return;
157      }
158
159      if (!isValidEmail(payload.email)) {
160        showRegMessage(card, msg, "Please enter a valid email address.", "error");
161        return;
162      }
163
164      if (!isValidUsPhone(payload.phone)) {
165        showRegMessage(card, msg, "Please enter a valid US phone number, for example (703) 555-1212.", "error");
166        return;
167      }
168
169      if (payload.phone) {
170        payload.phone = normalizeUsPhone(payload.phone);
171      }
172
173      const oldText = btn?.textContent || "Submit";
174
175      try {
176        if (btn) {
177          btn.disabled = true;
178          btn.textContent = "Submitting...";
179          btn.style.opacity = ".75";
180        }
181
182        showRegMessage(card, msg, "Submitting your enquiry...", "loading");
183
184        const API_BASE = window.ITL_API_BASE || "https://api.itlearn360.com";
185        const ENQUIRY_PUBLIC_KEY = window.ITL_ENQUIRY_PUBLIC_KEY || "";
186
187        const headers = { "Content-Type": "application/json" };
188        if (ENQUIRY_PUBLIC_KEY) headers["X-Enquiry-Key"] = ENQUIRY_PUBLIC_KEY;
189
190        const res = await fetch(`${API_BASE}/api/enquiry`, {
191          method: "POST",
192          headers,
193          body: JSON.stringify(payload),
194        });
195
196        const data = await res.json().catch(() => ({}));
197
198        if (!res.ok) {
199          throw new Error(data.detail || data.error || "Unable to submit enquiry");
200        }
201
202        form.reset();
203        showRegMessage(card, msg, "Thank you. Your enquiry has been received. Our team will c
203ontact you shortly.", "success");
204      } catch (err) {
205        showRegMessage(card, msg, err?.message || "Unable to submit enquiry. Please try again.", "error");
206      } finally {
207        if (btn) {
208          btn.disabled = false;
209          btn.textContent = oldText;
210          btn.style.opacity = "1";
211        }
212      }
213    });
214  });
215}
216
217function showRegMessage(card, el, text, type) {
218  if (!el) return;
219
220  const isLight =
221    card?.dataset?.theme === "light" ||
222    card?.classList?.contains("itl-registration-card-light");
223
224  el.style.display = "block";
225  el.textContent = text;
226
227  if (type === "success") {
228    el.style.background = isLight ? "#ecfdf5" : "rgba(16,185,129,.18)";
229    el.style.border = isLight ? "1px solid #bbf7d0" : "1px solid rgba(167,243,208,.45)";
230    el.style.color = isLight ? "#047857" : "#ecfdf5";
231  } else if (type === "error") {
232    el.style.background = isLight ? "#fef2f2" : "rgba(239,68,68,.18)";
233    el.style.border = isLight ? "1px solid #fecaca" : "1px solid rgba(254,202,202,.45)";
234    el.style.color = isLight ? "#b91c1c" : "#fff1f2";
235  } else {
236    el.style.background = isLight ? "#eff6ff" : "rgba(255,255,255,.14)";
237    el.style.border = isLight ? "1px solid #bfdbfe" : "1px solid rgba(255,255,255,.24)";
238    el.style.color = isLight ? "#1d4ed8" : "white";
239  }
240}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.