PageSourceSearch

https://applytop.com/js/login/index-1.5.107.js

js applytop.com collected 2026-09-24 12:55:58 UTC 42,844 bytes, 1,275 lines download raw bytes

1// applytop/app-1.5.107/src/helpers/native-bridge.js
2function nativePlugins() {
3  try {
4    const cap = window.Capacitor;
5    if (!cap || typeof cap.isNativePlatform !== "function" || !cap.isNativePlatform()) return null;
6    return cap.Plugins || {};
7  } catch (_) {
8    return null;
9  }
10}
11function install(P) {
12  const App = P.App;
13  const Share = P.Share;
14  const Camera = P.Camera;
15  const PushNotifications = P.PushNotifications;
16  const Network = P.Network;
17  const Browser = P.Browser;
18  const SecureStorage = P.SecureStoragePlugin;
19  const BiometricAuth = P.BiometricAuthNative;
20  function getStore() {
21    return typeof window.CdvPurchase !== "undefined" ? window.CdvPurchase.store : null;
22  }
23  const GPB_PRODUCTS = [
24    { id: "credits_small", type: "consumable" },
25    { id: "credits_medium", type: "consumable" },
26    { id: "credits_large", type: "consumable" },
27    { id: "pro_lifetime", type: "non consumable" },
28    { id: "pro_weekly", type: "paid subscription" },
29    { id: "pro_monthly", type: "paid subscription" },
30    { id: "pro_yearly", type: "paid subscription" },
31    { id: "career_growth_weekly", type: "paid subscription" }
32  ];
33  const _pendingPurchases = /* @__PURE__ */ new Map();
34  let _storeInitialized = false;
35  function initStore() {
36    const store = getStore();
37    if (!store || _storeInitialized) return;
38    _storeInitialized = true;
39    try {
40      const CdvPurchase = window.CdvPurchase;
41      const Platform = CdvPurchase.Platform;
42      const ProductType = CdvPurchase.ProductType;
43      const typeMap = {
44        "consumable": ProductType.CONSUMABLE,
45        "non consumable": ProductType.NON_CONSUMABLE,
46        "paid subscription": ProductType.PAID_SUBSCRIPTION
47      };
48      store.register(GPB_PRODUCTS.map((p) => ({
49        id: p.id,
50        type: typeMap[p.type] || ProductType.CONSUMABLE,
51        platform: Platform.GOOGLE_PLAY
52      })));
53      store.when().approved((transaction) => {
54        for (const productId of (transaction.products || []).map((p) => p.id)) {
55          const waiter = _pendingPurchases.get(productId);
56          if (waiter) {
57            waiter.resolve({
58              purchaseToken: transaction.purchaseId || transaction.transactionId,
59              productId,
60              orderId: transaction.transactionId || null
61            });
62            _pendingPurchases.delete(productId);
63          }
64        }
65        transaction.finish();
66      }).cancelled((transaction) => {
67        for (const productId of (transaction.products || []).map((p) => p.id)) {
68          const waiter = _pendingPurchases.get(productId);
69          if (waiter) {
70            waiter.resolve(null);
71            _pendingPurchases.delete(productId);
72          }
73        }
74      });
75      store.error((err) => {
76        console.warn("[AT bridge] Store error:", err);
77      });
78      store.initialize([Platform.GOOGLE_PLAY]).catch((err) => {
79        console.warn("[AT bridge] Store initialize failed:", err);
80      });
81    } catch (err) {
82      console.warn("[AT bridge] initStore failed:", err);
83    }
84  }
85  async function startPurchase(productId) {
86    const store = getStore();
87    if (!store) {
88      console.warn("[AT bridge] cordova-plugin-purchase not loaded");
89      return null;
90    }
91    initStore();
92    const offer = store.get(productId)?.getOffer();
93    if (!offer) {
94      console.warn("[AT bridge] No Play offer for product:", productId);
95      return { error: true, message: "This purchase is not available on this build" };
96    }
97    return new Promise((resolve, reject) => {
98      _pendingPurchases.set(productId, { resolve, reject });
99      offer.order().catch((err) => {
100        console.warn("[AT bridge] order() failed:", err);
101        const waiter = _pendingPurchases.get(productId);
102        if (waiter) {
103          waiter.resolve(null);
104          _pendingPurchases.delete(productId);
105        }
106      });
107    });
108  }
109  const TOKEN_KEY = "at.token";
110  const USER_ID_KEY = "at.userId";
111  const USER_EMAIL_KEY = "at.email";
112  async function saveToken({ token, userId, email }) {
113    if (!SecureStorage) return false;
114    try {
115      await SecureStorage.set({ key: TOKEN_KEY, value: token });
116      if (userId) await SecureStorage.set({ key: USER_ID_KEY, value: userId });
117      if (email) await SecureStorage.set({ key: USER_EMAIL_KEY, value: email });
118      return true;
119    } catch (err) {
120      console.warn("[AT bridge] saveToken failed:", err);
121      return false;
122    }
123  }
124  async function getToken() {
125    if (!SecureStorage) return null;
126    try {
127      const tokenRes = await SecureStorage.get({ key: TOKEN_KEY }).catch(() => null);
128      const userIdRes = await SecureStorage.get({ key: USER_ID_KEY }).catch(() => null);
129      const emailRes = await SecureStorage.get({ key: USER_EMAIL_KEY }).catch(() => null);
130      if (!tokenRes?.value || !userIdRes?.value) return null;
131      return { token: tokenRes.value, userId: userIdRes.value, email: emailRes?.value || null };
132    } catch (err) {
133      console.warn("[AT bridge] getToken failed:", err);
134      return null;
135    }
136  }
137  async function clearToken() {
138    if (!SecureStorage) return;
139    try {
140      await SecureStorage.remove({ key: TOKEN_KEY }).catch(() => {
141      });
142      await SecureStorage.remove({ key: USER_ID_KEY }).catch(() => {
143      });
144      await SecureStorage.remove({ key: USER_EMAIL_KEY }).catch(() => {
145      });
146    } catch (_) {
147    }
148  }
149  let _cachedFcmToken = null;
150  let _fcmTokenWaiters = [];
151  function setFcmToken(token) {
152    _cachedFcmToken = token;
153    _fcmTokenWaiters.forEach((fn) => fn(token));
154    _fcmTokenWaiters = [];
155  }
156  async function pushIsSafe() {
157    try {
158      const ATPush = P.ATPush;
159      if (!ATPush || typeof ATPush.isAvailable !== "function") return false;
160      const res = await ATPush.isAvailable();
161      return !!(res && res.available);
162    } catch (_) {
163      return false;
164    }
165  }
166  async function getFcmToken() {
167    if (_cachedFcmToken) return _cachedFcmToken;
168    if (!PushNotifications) return null;
169    if (!await pushIsSafe()) {
170      console.warn("[AT bridge] Push unavailable (Firebase not initialised in this build); skipping registration");
171      return null;
172    }
173    try {
174      const perm = await PushNotifications.requestPermissions();
175      if (perm.receive !== "granted") return null;
176      await PushNotifications.register();
177      return await new Promise((resolve) => {
178        const timeout = setTimeout(() => resolve(null), 1e4);
179        _fcmTokenWaiters.push((token) => {
180          clearTimeout(timeout);
181          resolve(token);
182        });
183      });
184    } catch (err) {
185      console.warn("[AT bridge] getFcmToken failed:", err);
186      return null;
187    }
188  }
189  async function share({ title, text, url }) {
190    if (!Share) return false;
191    try {
192      await Share.share({ title, text, url, dialogTitle: title });
193      return true;
194    } catch (_) {
195      return false;
196    }
197  }
198  async function cameraCapture() {
199    if (!Camera) return null;
200    try {
201      const photo = await Camera.getPhoto({
202        quality: 80,
203        allowEditing: false,
204        resultType: "base64",
205        source: "CAMERA"
206      });
207      return { base64: photo.base64String, mimeType: "image/" + (photo.format || "jpeg") };
208    } catch (_) {
209      return null;
210    }
211  }
212  async function enableBiometric() {
213    if (!BiometricAuth) return false;
214    try {
215      const info = await BiometricAuth.checkBiometry();
216      return !!info?.isAvailable;
217    } catch (_) {
218      return false;
219    }
220  }
221  async function promptBiometric() {
222    if (!BiometricAuth) return true;
223    try {
224      const info = await BiometricAuth.checkBiometry();
225      if (!info?.isAvailable) return true;
226      await BiometricAuth.authenticate({
227        reason: "Unlock ApplyTop",
228        cancelTitle: "Use password",
229        allowDeviceCredential: true
230      });
231      return true;
232    } catch (_) {
233      return false;
234    }
235  }
236  async function logout() {
237    await clearToken();
238    return true;
239  }
240  async function openExternal2({ url }) {
241    if (!Browser) {
242      window.open(url, "_blank");
243      return true;
244    }
245    try {
246      await Browser.open({ url });
247      return true;
248    } catch (err) {
249      console.warn("[AT bridge] openExternal failed:", err);
250      return false;
251    }
252  }
253  function wireLifecycleEvents() {
254    if (App) {
255      try {
256        App.addListener("appStateChange", ({ isActive }) => {
257          window.dispatchEvent(new CustomEvent(isActive ? "at:resume" : "at:pause"));
258        });
259        App.addListener("backButton", ({ canGoBack }) => {
260          if (window.history.length > 1 || canGoBack) window.history.back();
261          else App.exitApp();
262        });
263        App.addListener("appUrlOpen", ({ url }) => {
264          try {
265            const parsed = new URL(url);
266            if (parsed.protocol === "applytop:") {
267              window.location.href = "/" + parsed.host + parsed.pathname + (parsed.search || "");
268            } else if (parsed.host && parsed.host.endsWith("applytop.com")) {
269              window.location.href = parsed.pathname + parsed.search + parsed.hash;
270            }
271          } catch (err) {
272            console.warn("[AT bridge] appUrlOpen parse failed:", err);
273          }
274        });
275      } catch (err) {
276        console.warn("[AT bridge] App listener wiring failed:", err);
277      }
278    }
279    if (Network) {
280      try {
281        Network.addListener("networkStatusChange", (status) => {
282          window.dispatchEvent(new CustomEvent("at:networkchange", {
283            detail: { connected: status.connected, type: status.connectionType }
284          }));
285        });
286        Network.getStatus().then((status) => {
287          window.dispatchEvent(new CustomEvent("at:networkchange", {
288            detail: { connected: status.connected, type: status.connectionType }
289          }));
290        }).catch(() => {
291        });
292      } catch (err) {
293        console.warn("[AT bridge] Network listener wiring failed:", err);
294      }
295    }
296    if (PushNotifications) {
297      try {
298        PushNotifications.addListener("registration", ({ value }) => setFcmToken(value));
299        PushNotifications.addListener("registrationError", (err) => {
300          console.warn("[AT bridge] FCM registration error:", err);
301        });
302        PushNotifications.addListener("pushNotificationActionPerformed", ({ notification }) => {
303          const data = notification?.data || {};
304          if (data.url) window.location.href = data.url;
305          else if (data.alertId) window.location.href = "/dashboard/matches?alert=" + encodeURIComponent(data.alertId);
306          else window.location.href = "/dashboard/matches";
307        });
308      } catch (err) {
309        console.warn("[AT bridge] Push listener wiring failed:", err);
310      }
311    }
312  }
313  window.AT = {
314    saveToken,
315    getToken,
316    clearToken,
317    purchase: ({ productId }) => startPurchase(productId),
318    purchaseSubscription: ({ productId }) => startPurchase(productId),
319    share,
320    cameraCapture,
321    getFcmToken,
322    enableBiometric,
323    promptBiometric,
324    logout,
325    openExternal: openExternal2
326  };
327  wireLifecycleEvents();
328  if (typeof window.CdvPurchase !== "undefined") initStore();
329  else document.addEventListener("deviceready", initStore, { once: true });
330}
331try {
332  const P = nativePlugins();
333  if (P) install(P);
334} catch (err) {
335  console.warn("[AT bridge] install failed:", err);
336}
337
338// applytop/app-1.5.107/src/helpers/net.js
339function nativeHeaders() {
340  try {
341    if (window.Capacitor && window.Capacitor.isNativePlatform && window.Capacitor.isNativePlatform()) {
342      return {
343        "X-AT-Client": "android",
344        "X-AT-App-Version": window.constant && window.constant.appVersion || ""
345      };
346    }
347  } catch (_) {
348  }
349  return {};
350}
351function clearSession() {
352  localStorage.removeItem("token");
353  localStorage.removeItem("user");
354  try {
355    if (window.AT && window.AT.logout) window.AT.logout();
356  } catch (_) {
357  }
358}
359async function call(path, method, data) {
360  const cleanPath = path.replace(/^\/+/, "").replace(/\/+$/, "");
361  const apiUrl = window.constant.apiUrl.replace(/\/+$/, "");
362  let fullUrl = `${apiUrl}/${cleanPath}`;
363  if (method === "POST") {
364    let response = await fetch(fullUrl, {
365      method: "POST",
366      headers: {
367        "Content-Type": "application/json;charset=utf-8",
368        ...nativeHeaders()
369      },
370      body: JSON.stringify(data)
371    });
372    let result = await response.json();
373    if (result.error && result.errorMessage === "Auth Error") {
374      clearSession();
375      window.location.href = "/login";
376      return result;
377    }
378    return result;
379  } else if (method === "FORMDATA") {
380    let response = await fetch(fullUrl, {
381      method: "POST",
382      headers: { ...nativeHeaders() },
383      body: data
384    });
385    let result = await response.json();
386    if (result.error && result.errorMessage === "Auth Error") {
387      clearSession();
388      window.location.href = "/login";
389      return result;
390    }
391    return result;
392  } else if (method === "GET") {
393    const authHeaders = {};
394    if (data) {
395      const separator = fullUrl.includes("?") ? "&" : "?";
396      const queryParams = [];
397      if (data.token) {
398        authHeaders["Authorization"] = `Bearer ${data.token}`;
399        delete data.token;
400      }
401      if (data.userId) {
402        queryParams.push(`userId=${encodeURIComponent(data.userId)}`);
403        delete data.userId;
404      }
405      for (const key in data) {
406        if (data[key] !== void 0 && data[key] !== null) {
407          queryParams.push(`${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`);
408        }
409      }
410      if (queryParams.length > 0) {
411        fullUrl += separator + queryParams.join("&");
412      }
413    }
414    let response = await fetch(fullUrl, {
415      method: "GET",
416      headers: authHeaders
417    });
418    if (response.ok) {
419      let result = await response.json();
420      if (result.error && result.errorMessage === "Auth Error") {
421        clearSession();
422        window.location.href = "/login";
423        return result;
424      }
425      return result;
426    } else {
427      console.error("GET request failed:", response.status, response.statusText);
428      let result = await response.json();
429      if (result.error && result.errorMessage === "Auth Error") {
430        clearSession();
431        window.location.href = "/login";
432        return result;
433      }
434      return {
435        success: false,
436        error: `Request failed with status ${response.status}`
437      };
438    }
439  }
440}
441
442// applytop/app-1.5.107/src/helpers/attribution.js
443var STORAGE_KEY = "applytop.attribution";
444var FIRST_TOUCH_KEYS = ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content", "gclid", "fbclid", "ttclid", "ref"];
445var LAST_TOUCH_KEYS = ["click_id", "sub_id"];
446function readStoredRaw() {
447  try {
448    return localStorage.getItem(STORAGE_KEY);
449  } catch (e) {
450    return null;
451  }
452}
453function isInternalReferrer(referrer) {
454  if (!referrer) return false;
455  try {
456    const ref = new URL(referrer);
457    const here = window.location.hostname || "";
458    if (ref.hostname === here) return true;
459    const refApex = ref.hostname.replace(/^.*?([^.]+\.[^.]+)$/, "$1");
460    const hereApex = here.replace(/^.*?([^.]+\.[^.]+)$/, "$1");
461    return !!hereApex && refApex === hereApex;
462  } catch (e) {
463    return false;
464  }
465}
466function captureAttribution() {
467  if (typeof window === "undefined" || typeof localStorage === "undefined") return;
468  let params;
469  try {
470    params = new URLSearchParams(window.location.search || "");
471  } catch (e) {
472    return;
473  }
474  let existing = {};
475  const raw = readStoredRaw();
476  if (raw) {
477    try {
478      const parsed = JSON.parse(raw);
479      if (parsed && typeof parsed === "object") existing = parsed;
480    } catch (e) {
481    }
482  }
483  let changed = false;
484  for (const key of LAST_TOUCH_KEYS) {
485    const v = params.get(key);
486    if (v !== null && v !== "" && existing[key] !== v) {
487      existing[key] = v.slice(0, 512);
488      changed = true;
489    }
490  }
491  for (const key of FIRST_TOUCH_KEYS) {
492    if (existing[key]) continue;
493    const v = params.get(key);
494    if (v !== null && v !== "") {
495      existing[key] = v.slice(0, 512);
496      changed = true;
497    }
498  }
499  if (!existing.referrer_url) {
500    const rawReferrer = (document.referrer || "").slice(0, 2048);
501    if (rawReferrer && !isInternalReferrer(rawReferrer)) {
502      existing.referrer_url = rawReferrer;
503      changed = true;
504    }
505  }
506  if (!existing.landing_page) {
507    existing.landing_page = (window.location.pathname || "/") + (window.location.search || "");
508    existing.landing_page_at = (/* @__PURE__ */ new Date()).toISOString();
509    changed = true;
510  }
511  if (!changed) return;
512  try {
513    localStorage.setItem(STORAGE_KEY, JSON.stringify(existing));
514  } catch (e) {
515  }
516}
517function readAttribution() {
518  const raw = readStoredRaw();
519  if (!raw) return {};
520  try {
521    const obj = JSON.parse(raw);
522    return obj && typeof obj === "object" ? obj : {};
523  } catch (e) {
524    return {};
525  }
526}
527function clearAttribution() {
528  try {
529    localStorage.removeItem(STORAGE_KEY);
530  } catch (e) {
531  }
532}
533captureAttribution();
534
535// applytop/app-1.5.107/src/helpers/track.js
536var SESSION_KEY = "applytop.anon_session";
537function getOrCreateSessionId() {
538  try {
539    let id = localStorage.getItem(SESSION_KEY);
540    if (id) return id;
541    const rand = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Date.now().toString(36) + Math.random().toString(36).slice(2);
542    id = `anon_${rand}`.slice(0, 64);
543    localStorage.setItem(SESSION_KEY, id);
544    return id;
545  } catch (_) {
546    return null;
547  }
548}
549function readAnonSessionId() {
550  try {
551    return localStorage.getItem(SESSION_KEY) || null;
552  } catch (_) {
553    return null;
554  }
555}
556function clearAnonSessionId() {
557  try {
558    localStorage.removeItem(SESSION_KEY);
559  } catch (_) {
560  }
561}
562function abVariant() {
563  const key = getOrCreateSessionId();
564  if (!key) return "A";
565  let h = 5381;
566  const s = String(key);
567  for (let i = 0; i < s.length; i++) h = (h << 5) + h + s.charCodeAt(i) | 0;
568  return (h & 1) === 0 ? "A" : "B";
569}
570function isLandingPath(pathname) {
571  const p = (pathname || "/").replace(/\/+$/, "") || "/";
572  return p === "/" || p === "/index" || p === "/pl" || p === "/es" || p === "/hi";
573}
574function track(eventType, opts = {}) {
575  if (!eventType) return;
576  try {
577    let user = null;
578    try {
579      user = JSON.parse(localStorage.getItem("user") || "null");
580    } catch (_) {
581    }
582    const body = {
583      eventType,
584      metadata: {
585        ...readAttribution(),
586        ...opts.metadata || {}
587      }
588    };
589    if (user && user.id && user.token) {
590      body.userId = user.id;
591      body.token = user.token;
592    } else {
593      body.sessionId = getOrCreateSessionId();
594    }
595    if (opts.cvId) body.cvId = opts.cvId;
596    if (opts.jobId) body.jobId = opts.jobId;
597    if (opts.variant) body.variant = opts.variant;
598    if (opts.trigger) body.trigger = opts.trigger;
599    if (opts.path) body.path = opts.path;
600    if (opts.referrer) body.referrer = opts.referrer;
601    call("events/track", "POST", body).catch(() => {
602    });
603  } catch (_) {
604  }
605}
606var bootstrapped = false;
607function bootstrapAutoTracking() {
608  if (typeof window === "undefined" || typeof document === "undefined") return;
609  if (bootstrapped) return;
610  bootstrapped = true;
611  if (typeof location !== "undefined" && location.pathname && location.pathname.startsWith("/roppongi")) {
612    return;
613  }
614  const firePageView = () => {
615    try {
616      const path = (location.pathname || "/") + (location.search || "");
617      const referrer = document.referrer || null;
618      const screenStr = typeof screen !== "undefined" && screen.width && screen.height ? `${screen.width}x${screen.height}` : null;
619      track("page_view", {
620        path,
621        referrer,
622        metadata: screenStr ? { screen: screenStr } : {},
623        // Stamp the hero A/B bucket on landing views so page_view is the
624        // denominator of the variant funnel.
625        variant: isLandingPath(location.pathname) ? abVariant() : void 0
626      });
627    } catch (_) {
628    }
629  };
630  if (document.readyState === "loading") {
631    document.addEventListener("DOMContentLoaded", firePageView, { once: true });
632  } else {
633    firePageView();
634  }
635  document.addEventListener("click", (e) => {
636    try {
637      const el = e.target && e.target.closest && e.target.closest("[data-track-cta]");
638      if (!el) return;
639      const cta = el.getAttribute("data-track-cta");
640      if (!cta) return;
641      const body = {
642        eventType: "landing_cta_clicked",
643        metadata: { ...readAttribution(), cta },
644        path: (location.pathname || "/") + (location.search || "")
645      };
646      if (isLandingPath(location.pathname)) body.variant = abVariant();
647      let user = null;
648      try {
649        user = JSON.parse(localStorage.getItem("user") || "null");
650      } catch (_) {
651      }
652      if (user && user.id && user.token) {
653        body.userId = user.id;
654        body.token = user.token;
655      } else {
656        body.sessionId = getOrCreateSessionId();
657      }
658      const apiUrl = typeof window !== "undefined" && window.constant && window.constant.apiUrl ? window.constant.apiUrl.replace(/\/+$/, "") : null;
659      if (apiUrl && navigator.sendBeacon) {
660        navigator.sendBeacon(`${apiUrl}/events/track`, new Blob([JSON.stringify(body)], { type: "application/json" }));
661      } else {
662        track("landing_cta_clicked", { metadata: { cta }, variant: body.variant, path: body.path });
663      }
664    } catch (_) {
665    }
666  }, true);
667  document.addEventListener("click", (e) => {
668    try {
669      const a = e.target && e.target.closest && e.target.closest("a[href]");
670      if (!a) return;
671      const href = a.getAttribute("href") || "";
672      if (!href || href.startsWith("#") || /^(mailto:|tel:|javascript:)/i.test(href)) return;
673      let url;
674      try {
675        url = new URL(href, location.href);
676      } catch (_) {
677        return;
678      }
679      if (!/^https?:$/i.test(url.protocol)) return;
680      if (url.hostname === location.hostname) return;
681      track("outbound_click", {
682        metadata: {
683          url: url.href.slice(0, 512),
684          text: (a.textContent || "").trim().slice(0, 80) || null
685        }
686      });
687    } catch (_) {
688    }
689  }, true);
690}
691bootstrapAutoTracking();
692
693// applytop/app-1.5.107/src/helpers/i18n.js
694var STORAGE_KEY2 = "applytopLang";
695var supportedLanguages = ["en", "pl", "es", "hi"];
696var dictionaries = { en: {} };
697var namespaceCache = /* @__PURE__ */ new Map();
698function normalizeLanguage(language) {
699  const normalized = String(language || "").toLowerCase().split("-")[0];
700  return supportedLanguages.includes(normalized) ? normalized : "";
701}
702function getCurrentLanguage() {
703  const pathMatch = window.location.pathname.match(/^\/([a-z]{2})(?:\/|$)/i);
704  const pathLanguage = normalizeLanguage(pathMatch && pathMatch[1]);
705  if (pathLanguage) {
706    try {
707      localStorage.setItem(STORAGE_KEY2, pathLanguage);
708    } catch {
709    }
710    return pathLanguage;
711  }
712  let storedLanguage = "";
713  try {
714    storedLanguage = normalizeLanguage(localStorage.getItem(STORAGE_KEY2));
715  } catch {
716  }
717  if (storedLanguage) return storedLanguage;
718  return normalizeLanguage(navigator.language) || "en";
719}
720function setCurrentLanguage(language) {
721  const normalized = normalizeLanguage(language) || "en";
722  try {
723    localStorage.setItem(STORAGE_KEY2, normalized);
724  } catch {
725  }
726  return normalized;
727}
728async function loadNamespace(namespace, language) {
729  const normalizedLanguage = normalizeLanguage(language) || "en";
730  const cacheKey = `${namespace}:${normalizedLanguage}`;
731  if (namespaceCache.has(cacheKey)) {
732    return namespaceCache.get(cacheKey);
733  }
734  const v = typeof window !== "undefined" && window.__STATICA_VERSION ? `?v=${encodeURIComponent(window.__STATICA_VERSION)}` : "";
735  const request = fetch(`/i18n/${namespace}/${normalizedLanguage}.json${v}`).then((response) => {
736    if (!response.ok) throw new Error(`Missing i18n namespace: ${cacheKey}`);
737    return response.json();
738  }).then((messages) => {
739    dictionaries[normalizedLanguage] = {
740      ...dictionaries[normalizedLanguage] || {},
741      ...messages
742    };
743    return messages;
744  }).catch((error) => {
745    console.warn("[i18n] Failed to load namespace:", cacheKey, error);
746    return {};
747  });
748  namespaceCache.set(cacheKey, request);
749  return request;
750}
751async function loadI18nNamespaces(namespaces, language = getCurrentLanguage()) {
752  const normalizedLanguage = normalizeLanguage(language) || "en";
753  const uniqueNamespaces = [...new Set(Array.isArray(namespaces) ? namespaces : [namespaces])];
754  await Promise.all(uniqueNamespaces.map((namespace) => loadNamespace(namespace, normalizedLanguage)));
755  if (normalizedLanguage !== "en") {
756    await Promise.all(uniqueNamespaces.map((namespace) => loadNamespace(namespace, "en")));
757  }
758}
759function t(key, replacements = {}) {
760  const language = getCurrentLanguage();
761  const dictionary = dictionaries[language] || {};
762  let value = dictionary[key] || dictionaries.en[key] || key;
763  Object.keys(replacements).forEach((replacementKey) => {
764    value = value.replaceAll(`{${replacementKey}}`, replacements[replacementKey]);
765  });
766  return value;
767}
768function translatePage(root = document) {
769  root.querySelectorAll("[data-i18n]").forEach((element) => {
770    element.textContent = t(element.getAttribute("data-i18n"));
771  });
772  root.querySelectorAll("[data-i18n-title]").forEach((element) => {
773    element.setAttribute("title", t(element.getAttribute("data-i18n-title")));
774  });
775  root.querySelectorAll("[data-i18n-placeholder]").forEach((element) => {
776    element.setAttribute("placeholder", t(element.getAttribute("data-i18n-placeholder")));
777  });
778  root.querySelectorAll("[data-i18n-aria-label]").forEach((element) => {
779    element.setAttribute("aria-label", t(element.getAttribute("data-i18n-aria-label")));
780  });
781}
782window.applytopT = t;
783window.applytopTranslatePage = translatePage;
784window.applytopSetLanguage = setCurrentLanguage;
785window.applytopLoadI18nNamespaces = loadI18nNamespaces;
786
787// applytop/app-1.5.107/src/constant.js
788var buildEnv = "production";
789function resolveEnv() {
790  const h = (typeof window !== "undefined" && window.location && window.location.hostname || "").toLowerCase();
791  if (!h) return buildEnv === "production" || buildEnv === "beta" ? buildEnv : "production";
792  if (h === "dev.applytop.com" || h.endsWith(".dev.applytop.com") || h === "localhost" || h === "127.0.0.1") return "development";
793  if (h === "betaapp.applytop.com" || h === "beta.applytop.com") return "beta";
794  return "production";
795}
796var env = resolveEnv();
797var config = {};
798console.log("Initializing constants...");
799console.log("Environment:", env);
800var GOOGLE_CLIENT_ID = "703868592758-vku0qghr1auocbr4kro1442h33aon53m.apps.googleusercontent.com";
801var TURNSTILE_SITE_KEY = "0x4AAAAAADICj3XqreulgznT";
802if (env === "production") {
803  config = {
804    apiKey: "DDe80f841ea7bc9e78946e0b9d6d9b7e200ed5468a",
805    apiUrl: "https://applytop.com/api/",
806    appUrl: "https://applytop.com/",
807    toolsUrl: "https://applytop.com/",
808    websiteUrl: "https://applytop.com/",
809    stripePublicKey: "",
810    // TODO: set after running stripe-products.js --prod
811    googleClientId: GOOGLE_CLIENT_ID,
812    turnstileSiteKey: TURNSTILE_SITE_KEY,
813    env,
814    version: "1.9.0.0"
815  };
816} else if (env === "beta") {
817  config = {
818    apiKey: "DDe80f841ea7bc9e78946e0b9d6d9b7e200ed5468a",
819    apiUrl: "https://betaapp.applytop.com/api/",
820    appUrl: "https://betaapp.applytop.com/",
821    toolsUrl: "https://betaapp.applytop.com/",
822    websiteUrl: "https://beta.applytop.com/",
823    stripePublicKey: "",
824    // TODO: set from Stripe Dashboard
825    googleClientId: GOOGLE_CLIENT_ID,
826    turnstileSiteKey: TURNSTILE_SITE_KEY,
827    env,
828    version: "1.9.0.0"
829  };
830} else {
831  config = {
832    apiKey: "DDe80f841ea7bc9e78946e0b9d6d9b7e200ed5468a",
833    apiUrl: "https://dev.applytop.com/api/",
834    appUrl: "https://dev.applytop.com/",
835    toolsUrl: "https://dev.applytop.com/",
836    websiteUrl: "https://dev.applytop.com/",
837    stripePublicKey: "pk_test_51RMqqQQ2YLWu4XdPOLFVk4nmnZJsQe8MHPq3BbNfwBo67bexLbB34vCQmybInxcaAKJQXFywwp9sUytySqALlh2G00mSYfcySm",
838    // TODO: set from Stripe Dashboard (test mode)
839    googleClientId: GOOGLE_CLIENT_ID,
840    turnstileSiteKey: TURNSTILE_SITE_KEY,
841    env,
842    version: "1.9.0.0"
843  };
844}
845console.log("Config initialized:", config);
846window.constant = config;
847console.log("window.constant set:", window.constant);
848loadI18nNamespaces(["index"]).then(() => translatePage());
849
850// applytop/app-1.5.107/src/helpers/google.js
851function setCookie(cname, cvalue, exdays) {
852  const d = /* @__PURE__ */ new Date();
853  d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1e3);
854  const expires = "expires=" + d.toUTCString();
855  document.cookie = `${cname}=${cvalue};${expires};path=/`;
856}
857async function handleGoogle(response) {
858  if (!response || !response.credential) return;
859  const attribution = readAttribution();
860  const anonSessionId = readAnonSessionId();
861  const { ref: referralCode, ...restAttribution } = attribution;
862  try {
863    const resp = await call("auth/googleAuthUser", "POST", {
864      data: {
865        credential: response.credential,
866        referrer: document.referrer || document.location.href,
867        ...restAttribution,
868        ...referralCode ? { referral_code: referralCode } : {},
869        ...anonSessionId ? { anon_session_id: anonSessionId } : {}
870      }
871    });
872    if (resp && resp.success && resp.data && resp.data.user && resp.data.token) {
873      const { user, token, redirect, isNewUser } = resp.data;
874      localStorage.setItem("token", token);
875      localStorage.setItem("user", JSON.stringify({ ...user, token }));
876      clearAttribution();
877      clearAnonSessionId();
878      setCookie("gsid", "true", 90);
879      if (isNewUser && typeof window.gtag === "function") {
880        window.gtag("event", "sign_up", { method: "google" });
881      }
882      const target = redirect || "/dashboard";
883      window.location.href = target;
884    } else {
885      console.warn("Google sign-in failed", resp);
886    }
887  } catch (err) {
888    console.error("Google sign-in error", err);
889  }
890}
891window.handleGoogle = handleGoogle;
892
893// applytop/app-1.5.107/src/helpers/dom.js
894function addClass(path, classNames) {
895  [...document.querySelectorAll(path)].map((i) => i.classList.add(...classNames));
896}
897function removeClass(path, classNames) {
898  [...document.querySelectorAll(path)].map((i) => i.classList.remove(...classNames));
899}
900function getFormValues(path, prefix = "") {
901  let form = document.querySelector(path);
902  console.log("FORM", form);
903  if (!form) {
904    console.warn(`Form not found: ${path}`);
905    return {};
906  }
907  let formValues = Object.values(form).reduce((obj, field) => {
908    if (field.type != "submit") {
909      obj[field.id.replace(prefix, "")] = field.type == "checkbox" ? field.checked : field.value;
910    }
911    return obj;
912  }, {});
913  console.log("FV", formValues);
914  return formValues;
915}
916function showToast(type, message, duration = 3e3) {
917  let toastContainer = document.getElementById("toast");
918  if (!toastContainer) {
919    toastContainer = document.createElement("div");
920    toastContainer.id = "toast";
921    toastContainer.className = "fixed top-4 right-4 z-50 transform transition-transform duration-300 translate-x-full";
922    document.body.appendChild(toastContainer);
923  }
924  let toastSuccess = document.getElementById("toast-success");
925  let toastError = document.getElementById("toast-error");
926  let toastInfo = document.getElementById("toast-info");
927  if (!toastSuccess) {
928    toastSuccess = document.createElement("div");
929    toastSuccess.id = "toast-success";
930    toastSuccess.className = "hidden items-center p-4 mb-4 rounded-lg shadow text-white bg-green-500";
931    toastSuccess.innerHTML = `
932      <svg class="size-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
933        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
934      </svg>
935      <span class="text-sm font-medium"></span>
936    `;
937    toastContainer.appendChild(toastSuccess);
938  }
939  if (!toastError) {
940    toastError = document.createElement("div");
941    toastError.id = "toast-error";
942    toastError.className = "hidden items-center p-4 mb-4 rounded-lg shadow text-white bg-red-500";
943    toastError.innerHTML = `
944      <svg class="size-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
945        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
946      </svg>
947      <span class="text-sm font-medium"></span>
948    `;
949    toastContainer.appendChild(toastError);
950  }
951  if (!toastInfo) {
952    toastInfo = document.createElement("div");
953    toastInfo.id = "toast-info";
954    toastInfo.className = "hidden items-center p-4 mb-4 rounded-lg shadow text-white bg-blue-500";
955    toastInfo.innerHTML = `
956      <svg class="size-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
957        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
958      </svg>
959      <span class="text-sm font-medium"></span>
960    `;
961    toastContainer.appendChild(toastInfo);
962  }
963  toastSuccess.classList.add("hidden");
964  toastError.classList.add("hidden");
965  toastInfo.classList.add("hidden");
966  let targetToast;
967  if (type === "success") {
968    targetToast = toastSuccess;
969  } else if (type === "error") {
970    targetToast = toastError;
971  } else if (type === "info") {
972    targetToast = toastInfo;
973  } else {
974    targetToast = toastInfo;
975  }
976  targetToast.querySelector("span").textContent = message;
977  targetToast.classList.remove("hidden");
978  targetToast.classList.add("flex");
979  toastContainer.classList.remove("translate-x-full");
980  toastContainer.classList.add("translate-x-0");
981  setTimeout(() => {
982    toastContainer.classList.remove("translate-x-0");
983    toastContainer.classList.add("translate-x-full");
984    setTimeout(() => {
985      targetToast.classList.remove("flex");
986      targetToast.classList.add("hidden");
987    }, 300);
988  }, duration);
989}
990
991// applytop/app-1.5.107/src/helpers/platform.js
992function isNativeShell() {
993  try {
994    return !!(window.Capacitor && window.Capacitor.isNativePlatform && window.Capacitor.isNativePlatform());
995  } catch (_) {
996    return false;
997  }
998}
999function isNative() {
1000  try {
1001    return isNativeShell() && !!window.AT;
1002  } catch (_) {
1003    return false;
1004  }
1005}
1006var PLAN_TO_PLAY = {
1007  weekly: "pro_weekly",
1008  monthly: "pro_monthly",
1009  yearly: "pro_yearly",
1010  career_growth: "career_growth_weekly",
1011  careerGrowth: "career_growth_weekly",
1012  lifetime: "pro_lifetime"
1013};
1014var PACK_TO_PLAY = {
1015  small: "credits_small",
1016  medium: "credits_medium",
1017  large: "credits_large"
1018};
1019function currentUser() {
1020  try {
1021    return JSON.parse(localStorage.getItem("user")) || null;
1022  } catch (_) {
1023    return null;
1024  }
1025}
1026function authBody(extra = {}) {
1027  const user = currentUser();
1028  return { userId: user?._id || user?.id, token: user?.token, ...extra };
1029}
1030async function purchasePlan(planId) {
1031  if (!isNative()) return null;
1032  const productId = PLAN_TO_PLAY[planId];
1033  if (!productId) return { error: true, message: `Unknown plan: ${planId}` };
1034  if (productId === "pro_lifetime") {
1035    const result2 = await window.AT.purchase({ productId });
1036    if (!result2) return null;
1037    return call("googleplay/verifyPurchase", "POST", authBody({
1038      purchaseToken: result2.purchaseToken,
1039      productId: result2.productId,
1040      orderId: result2.orderId
1041    }));
1042  }
1043  const result = await window.AT.purchaseSubscription({ productId });
1044  if (!result) return null;
1045  return call("googleplay/verifySubscription", "POST", authBody({
1046    purchaseToken: result.purchaseToken,
1047    productId: result.productId
1048  }));
1049}
1050async function purchasePack(packId) {
1051  if (!isNative()) return null;
1052  const productId = PACK_TO_PLAY[packId];
1053  if (!productId) return { error: true, message: `Unknown pack: ${packId}` };
1054  const result = await window.AT.purchase({ productId });
1055  if (!result) return null;
1056  return call("googleplay/verifyPurchase", "POST", authBody({
1057    purchaseToken: result.purchaseToken,
1058    productId: result.productId,
1059    orderId: result.orderId
1060  }));
1061}
1062async function registerForPush() {
1063  if (!isNative()) return false;
1064  try {
1065    const fcmToken = await window.AT.getFcmToken();
1066    if (!fcmToken) return false;
1067    await call("notifications/registerDevice", "POST", authBody({
1068      fcmToken,
1069      appVersion: window.constant && window.constant.appVersion || ""
1070    }));
1071    return true;
1072  } catch (_) {
1073    return false;
1074  }
1075}
1076async function unregisterForPush() {
1077  if (!isNative()) return;
1078  try {
1079    const fcmToken = await window.AT.getFcmToken();
1080    await call("notifications/unregisterDevice", "POST", authBody({ fcmToken }));
1081  } catch (_) {
1082  }
1083}
1084async function persistNativeToken(user) {
1085  if (!isNative() || !user) return;
1086  try {
1087    await window.AT.saveToken({ token: user.token, userId: user._id || user.id, email: user.email });
1088  } catch (_) {
1089  }
1090}
1091async function restoreNativeSession() {
1092  if (!isNative()) return false;
1093  try {
1094    if (currentUser()?.token) return false;
1095    const stored = await window.AT.getToken();
1096    if (!stored?.token) return false;
1097    localStorage.setItem("token", stored.token);
1098    localStorage.setItem("user", JSON.stringify({ _id: stored.userId, id: stored.userId, email: stored.email, token: stored.token }));
1099    return true;
1100  } catch (_) {
1101    return false;
1102  }
1103}
1104async function nativeLogout() {
1105  if (!isNative()) return;
1106  try {
1107    await unregisterForPush();
1108  } catch (_) {
1109  }
1110  try {
1111    await window.AT.logout();
1112  } catch (_) {
1113  }
1114}
1115async function biometricGate() {
1116  if (!isNative()) return true;
1117  try {
1118    return await window.AT.promptBiometric();
1119  } catch (_) {
1120    return true;
1121  }
1122}
1123async function nativeShare({ title, text, url }) {
1124  if (!isNative()) return false;
1125  try {
1126    return await window.AT.share({ title, text, url });
1127  } catch (_) {
1128    return false;
1129  }
1130}
1131async function nativeCameraCapture() {
1132  if (!isNative()) return null;
1133  try {
1134    return await window.AT.cameraCapture();
1135  } catch (_) {
1136    return null;
1137  }
1138}
1139async function openExternal(url) {
1140  if (!isNative()) {
1141    window.open(url, "_blank");
1142    return true;
1143  }
1144  try {
1145    return await window.AT.openExternal({ url });
1146  } catch (_) {
1147    return false;
1148  }
1149}
1150if (typeof window !== "undefined") {
1151  window.atPlatform = {
1152    isNative,
1153    isNativeShell,
1154    purchasePlan,
1155    purchasePack,
1156    registerForPush,
1157    unregisterForPush,
1158    persistNativeToken,
1159    restoreNativeSession,
1160    nativeLogout,
1161    biometricGate,
1162    nativeShare,
1163    nativeCameraCapture,
1164    openExternal,
1165    PLAN_TO_PLAY,
1166    PACK_TO_PLAY
1167  };
1168}
1169
1170// applytop/app-1.5.107/src/components/login/login.js
1171var login = {
1172  init() {
1173    const form = document.querySelector('form[data-form="login"]');
1174    if (form) {
1175      form.addEventListener("submit", this.handleSubmit.bind(this));
1176    }
1177  },
1178  async handleSubmit(event) {
1179    event.preventDefault();
1180    const formData = getFormValues("#login-form");
1181    const errorElement = document.querySelector(".error-message");
1182    if (errorElement) {
1183      addClass(".error-message", ["hidden"]);
1184    }
1185    try {
1186      const response = await call("auth/login", "POST", {
1187        data: {
1188          email: formData.email,
1189          password: formData.password,
1190          rememberMe: formData["remember-me"] || false
1191        }
1192      });
1193      if (response.success && response.data && response.data.user && response.data.token) {
1194        const { user, token } = response.data;
1195        localStorage.setItem("token", token);
1196        localStorage.setItem("user", JSON.stringify({ ...user, token }));
1197        try {
1198          await persistNativeToken({ ...user, token });
1199          registerForPush();
1200        } catch (_) {
1201        }
1202        showToast("success", "Login successful! Checking your profile...");
1203        const userId = user.userId || user.id || user._id;
1204        if (!userId) {
1205          showToast("error", "User identification failed after login. Please try again.");
1206          return;
1207        }
1208        try {
1209          const cvsResponse = await call("cvs/list", "POST", { token, userId });
1210          if (cvsResponse.success && cvsResponse.cvs && cvsResponse.cvs.length === 0) {
1211            showToast("info", "Welcome! Let's complete your profile by uploading a CV.");
1212            setTimeout(() => {
1213              window.location.href = "/onboarding";
1214            }, 1500);
1215            return;
1216          }
1217          if (cvsResponse.success && cvsResponse.cvs && cvsResponse.cvs.length > 0) {
1218            let hasAlerts = false;
1219            try {
1220              const alertsResp = await call("job-alerts", "POST", { token, userId, limit: 1 });
1221              hasAlerts = alertsResp.success && (alertsResp.count > 0 || alertsResp.alerts && alertsResp.alerts.length > 0);
1222            } catch (e) {
1223              console.warn("job-alerts existence check failed; proceeding to dashboard", e);
1224              hasAlerts = true;
1225            }
1226            if (hasAlerts) {
1227              setTimeout(() => {
1228                window.location.href = "/dashboard";
1229              }, 500);
1230              return;
1231            }
1232            showToast("info", "Please complete your profile by adding job positions.");
1233            if (cvsResponse.cvs[0]) {
1234              localStorage.setItem("currentCvData", JSON.stringify(cvsResponse.cvs[0]));
1235            }
1236            setTimeout(() => {
1237              window.location.href = "/onboarding?step=job-positions";
1238            }, 1500);
1239            return;
1240          }
1241          console.warn("Could not reliably determine CV count:", cvsResponse);
1242          showToast("info", "Proceeding to dashboard.");
1243          setTimeout(() => {
1244            window.location.href = "/dashboard";
1245          }, 1e3);
1246        } catch (cvError) {
1247          console.error("Error checking profile after login:", cvError);
1248          showToast("warning", "Could not check profile status, redirecting to dashboard.");
1249          setTimeout(() => {
1250            window.location.href = "/dashboard";
1251          }, 1e3);
1252        }
1253      } else {
1254        if (errorElement) {
1255          errorElement.textContent = response.message || response.data?.message || "Invalid email or password";
1256          removeClass(".error-message", ["hidden"]);
1257        }
1258        showToast("error", response.message || response.data?.message || "Login failed");
1259      }
1260    } catch (error) {
1261      console.error("Login error:", error);
1262      if (errorElement) {
1263        errorElement.textContent = "An error occurred. Please try again.";
1264        removeClass(".error-message", ["hidden"]);
1265      }
1266      showToast("error", "An error occurred. Please try again.");
1267    }
1268  }
1269};
1270var login_default = login;
1271
1272// applytop/app-1.5.107/src/components/login/index.js
1273document.addEventListener("DOMContentLoaded", () => {
1274  login_default.init();
1275});

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.