PageSourceSearch

https://marketersmedia.com/wp-content/themes/mm/assets/js/plans-pricing-[11-2025]-v6.js

js marketersmedia.com collected 2026-10-02 03:04:20 UTC 17,679 bytes, 656 lines download raw bytes

1import Swiper from "https://cdn.jsdelivr.net/npm/swiper@8/swiper-bundle.esm.browser.min.js";
2
3// config
4const studioBaseApi = true
5  ? "https://apis.studio.marketersmedia.com"
6  : "http://localhost:8080";
7
8const fsCheckoutSecureApi = studioBaseApi + "/billing/fs-checkout";
9
10// globals
11let plan = "executive";
12let prx = 5;
13let planChosen = false;
14let fsGeneratedPackageId = null;
15let fsPurchaseCompleted = false;
16
17// packageIdMapper[plan][prx]
18const packageIdMapper = {
19  professional: {
20    1: 966,
21    5: 2996,
22    25: 2999,
23  },
24  executive: {
25    1: 967,
26    5: 2997,
27    25: 3000,
28  },
29  director: {
30    1: 968,
31    5: 2998,
32    25: 3001,
33  },
34};
35
36let hostedFieldInstance;
37
38// setup
39const firebaseApp = firebase.initializeApp({
40  apiKey: "AIzaSyAScvAditJVKqnfvjDSXrxRL9nqUwAxHUo",
41  authDomain: "marketersmedia-studio.firebaseapp.com",
42  projectId: "marketersmedia-studio",
43  appId: "1:459236083887:web:e88b0a3fd283b9b27dec5a",
44});
45
46const auth = firebaseApp.auth();
47
48// common
49const setLoader = (text, loading = true) => {
50  if (loading) {
51    document.getElementById("process-loader").classList.remove("hidden");
52    document.getElementById("loader-text").innerHTML = text;
53  } else {
54    document.getElementById("process-loader").classList.add("hidden");
55  }
56};
57
58const setAuthorizedState = async (result) => {
59  if (result.additionalUserInfo.isNewUser) {
60    const idToken = await auth.currentUser.getIdToken();
61
62    $.ajax({
63      url: studioBaseApi + "/auth/create-client",
64      type: "POST",
65      contentType: "application/json",
66      beforeSend: (xhr) => {
67        xhr.setRequestHeader("Authorization", "Bearer " + idToken);
68      },
69      data: JSON.stringify({
70        fname: result.user?.displayName || result.user?.email,
71        lname: "",
72        invitationId: "",
73      }),
74      success: (resp) => {
75        console.log({ resp });
76      },
77      error: (err) => {
78        console.error(err);
79        alert("Something went wrong!");
80      },
81    });
82  }
83};
84
85// FastSpring callbacks
86window.dataPopupWebhookReceived = function (data) {
87  if (
88    data &&
89    (data.completed === true ||
90      data.status === "success" ||
91      data.orderComplete === true)
92  ) {
93    fsPurchaseCompleted = true;
94    window.location.href = "https://studio.marketersmedia.com/";
95  } else if (data && data.error) {
96    alert("There was a problem processing your payment. Please try again.");
97  }
98};
99
100window.dataPopupClosed = function () {
101  if (!fsGeneratedPackageId || fsPurchaseCompleted) return;
102  auth.currentUser?.getIdToken().then((idToken) => {
103    if (!idToken) return;
104    $.ajax({
105      url: studioBaseApi + "/billing/remove-package",
106      type: "POST",
107      beforeSend: (xhr) =>
108        xhr.setRequestHeader("Authorization", "Bearer " + idToken),
109      contentType: "application/json",
110      data: JSON.stringify({ packageId: fsGeneratedPackageId }),
111    });
112  });
113};
114
115const triggerFastSpring = async (idToken) => {
116  const packageId = packageIdMapper[plan][prx];
117  const fsProductPath = `credit-${plan}`;
118
119  let payload = "",
120    key = "";
121
122  await new Promise((resolve) => {
123    $.ajax({
124      url: fsCheckoutSecureApi,
125      type: "POST",
126      beforeSend: (xhr) =>
127        xhr.setRequestHeader("Authorization", "Bearer " + idToken),
128      contentType: "application/json",
129      data: JSON.stringify({
130        packages: [{ id: packageId, quantity: 1 }],
131        productPath: fsProductPath,
132        exclusiveDealCid: packageId,
133      }),
134      success: (result) => {
135        console.log("[FS] fs-checkout raw response:", result);
136        if (result.secure?.payload) {
137          try {
138            console.log(
139              "[FS] payload decoded:",
140              JSON.parse(atob(result.secure.payload)),
141            );
142          } catch (e) {
143            console.log("[FS] payload (raw):", result.secure.payload);
144          }
145        }
146        if (result.secure?.payload && result.secure?.key) {
147          payload = result.secure.payload;
148          key = result.secure.key;
149          fsGeneratedPackageId = result.packageId;
150        }
151        resolve();
152      },
153      error: () => {
154        alert("Something went wrong. Please try again.");
155        resolve();
156      },
157    });
158  });
159
160  if (!payload || !key) {
161    return;
162  }
163
164  await new Promise((resolve, reject) => {
165    console.log({ payload, key });
166    fastspring.builder.secure(payload, key, (data) => {
167      if (data.error) return reject(data.error);
168      resolve();
169    });
170  });
171
172  fastspring.builder.checkout();
173  planChosen = false;
174};
175
176// account
177const setupAccountAndBraintree = () => {
178  $("#btn-sell, .btn-checkout").on("click", () => {
179    $("html, body").animate(
180      { scrollTop: $("#first-pp-table").offset().top },
181      600,
182    );
183  });
184
185  auth.onAuthStateChanged(async (authState) => {
186    if (authState && authState.uid && planChosen) {
187      const idToken = await auth.currentUser.getIdToken();
188      $("#popup-container").addClass("hidden");
189      await triggerFastSpring(idToken);
190    }
191  });
192
193  $(".table-add-to-cart-btn").on("click", async function () {
194    plan = $(this).attr("data-plan");
195    planChosen = true;
196
197    const priceInfo = PR_PACKAGES[prx][plan];
198    const originalPrice = PACKAGE_ORI_PRICE_PR[plan];
199    const totalPrice = priceInfo.total;
200    const fullOriPrice = originalPrice * prx;
201
202    $(".pp-package").text(plan.charAt(0).toUpperCase() + plan.slice(1));
203    $(".pp-ori-total-price").text("$" + fullOriPrice.toLocaleString());
204    $(".pp-prx").text(prx);
205    $(".pp-discount").text(function () {
206      const savedAmount = fullOriPrice - totalPrice;
207      return "$" + (savedAmount > 0 ? savedAmount.toLocaleString() : 0);
208    });
209    $(".pp-total").text("$" + totalPrice.toLocaleString());
210
211    const currentUser = auth.currentUser;
212    if (currentUser) {
213      const idToken = await currentUser.getIdToken();
214      await triggerFastSpring(idToken);
215    } else {
216      $("#popup-container").removeClass("hidden");
217    }
218  });
219
220  // Close popup when close button is clicked
221  $(".close-btn").on("click", function () {
222    $("#popup-container").addClass("hidden");
223  });
224
225  // Close popup when clicking on the overlay
226  $("#popup-container").on("click", function (event) {
227    console.log("");
228    // Only close if clicking directly on the overlay (not on popup content)
229    if (event.target == this) {
230      $(this).addClass("hidden");
231    }
232  });
233
234  // - switch panel
235  $("#bi-switch-signin").on("click", () => {
236    $("#signup").addClass("hidden");
237    $("#signin").removeClass("hidden");
238  });
239  $("#bi-switch-signup").on("click", () => {
240    $("#signin").addClass("hidden");
241    $("#signup").removeClass("hidden");
242  });
243
244  // google-signin
245  $(".bi-google-button").on("click", async () => {
246    try {
247      setLoader("Signing in...", true);
248
249      const provider = new firebase.auth.GoogleAuthProvider();
250      const userCredential = await auth.signInWithPopup(provider);
251      setAuthorizedState(userCredential);
252    } catch (error) {
253      console.error(error);
254      alert("Something went wrong");
255    } finally {
256      setLoader("", false);
257    }
258  });
259
260  // signup
261  $("#bi-signup-btn").on("click", async () => {
262    try {
263      setLoader("Signing up...", true);
264
265      const email = $("#bi-signup-email").val();
266      const password = $("#bi-signup-password").val();
267      const confirmPassword = $("#bi-signup-confirm-password").val();
268
269      if (!email || !password) {
270        alert("Please fill in required fields.");
271        return;
272      }
273
274      if (password !== confirmPassword) {
275        alert(
276          "Please make sure the password and confirm password are the same. ",
277        );
278        return;
279      }
280
281      const result = await auth.createUserWithEmailAndPassword(email, password);
282
283      const isNewUser =
284        result.user.metadata.creationTime ===
285        result.user.metadata.lastSignInTime;
286
287      if (isNewUser) {
288        dataLayer.push({
289          event: "new_user_signup_pp",
290          email: email,
291        });
292      }
293
294      setAuthorizedState(result);
295    } catch (error) {
296      console.error(error);
297
298      alert("Something went wrong");
299      setLoader("", false);
300    }
301  });
302
303  // login
304  window.loginStudio = (recaptchaResponse) => {
305    setLoader("Signing in...", true);
306
307    const email = $("#bi-signin-email").val().trim();
308    const password = $("#bi-signin-password").val();
309
310    if (!email || !password) {
311      alert("Please fill in required fields.");
312      return;
313    }
314
315    $.ajax({
316      url: studioBaseApi + "/auth/login",
317      type: "POST",
318      contentType: "application/json",
319      data: JSON.stringify({ recaptchaResponse, email, password }),
320      success: (result) => {
321        if (!result.success && result.error) {
322          grecaptcha.reset(1);
323          setLoader("", false);
324
325          switch (result.error) {
326            case "auth/user-not-found":
327              alert("Email not found. Please sign up with a new account. ");
328              break;
329
330            default:
331              alert("Error: " + result.error);
332              break;
333          }
334        }
335
336        if (result.success) {
337          auth
338            .signInWithEmailAndPassword(email, password)
339            .then((result) => {
340              setAuthorizedState(result);
341            })
342            .catch((error) => {
343              console.error(error);
344              grecaptcha.reset(1);
345              setLoader("", false);
346
347              if (error.code) {
348                switch (error.code) {
349                  case "auth/wrong-password":
350                    alert("Password is wrong. Please try again.");
351                    break;
352
353                  default:
354                    alert(
355                      `Something went wrong. Please contact us. (${error.code})`,
356                    );
357                    break;
358                }
359              }
360            });
361        }
362      },
363      error: (error) => {
364        grecaptcha.reset(1);
365        alert("Something went wrong");
366      },
367    });
368  };
369
370  // signout
371  // $('#signout-btn').on('click', async () => {
372  //  setLoader('Signing out...', true);
373  //  await auth.signOut();
374  //  setLoader('', false);
375  // });
376};
377
378function addAgencySolutionOnClickListener() {
379  document
380    .getElementById("agency-solution-btn")
381    .addEventListener("click", () => {
382      window.location.href = "https://marketersmedia.com/why-agency";
383    });
384}
385
386// Logistic to extend FAQ card
387function addFaqButtonOnClickListener() {
388  const btns = document.getElementsByClassName("faq-header-btn");
389
390  for (let i = 0; i < btns.length; i++) {
391    btns.item(i).addEventListener("click", () => extendFAQ(i + 1));
392  }
393}
394
395function getFirstElmentByClassName(object, className) {
396  return object.getElementsByClassName(className).item(0);
397}
398
399function extendFAQ(faq_no) {
400  let card = getFirstElmentByClassName(document, "faq" + String(faq_no));
401  // Changing style of the card
402  card.classList.remove("faq-card-minimize");
403  card.classList.add("faq-card-extend");
404
405  // Change visibility of components
406  let icons = getFirstElmentByClassName(
407    card,
408    "faq-header-icon",
409  ).getElementsByTagName("img");
410  icons.item(0).classList.add("hidden");
411  icons.item(1).classList.remove("hidden");
412  getFirstElmentByClassName(card, "faq-card-desc").style.display = "block";
413
414  // Remove onclickListener and add new one to
415  let btn = getFirstElmentByClassName(card, "faq-header-btn");
416  let newBtn = btn.cloneNode(true);
417  newBtn.addEventListener("click", () => minimizeFAQ(faq_no));
418  card.replaceChild(newBtn, btn);
419}
420
421function minimizeFAQ(faq_no) {
422  let card = getFirstElmentByClassName(document, "faq" + String(faq_no));
423  // Changing style of the card
424  card.classList.remove("faq-card-extend");
425  card.classList.add("faq-card-minimize");
426
427  // Change visibility of components
428  let icons = getFirstElmentByClassName(
429    card,
430    "faq-header-icon",
431  ).getElementsByTagName("img");
432  icons.item(0).classList.remove("hidden");
433  icons.item(1).classList.add("hidden");
434  getFirstElmentByClassName(card, "faq-card-desc").style.display = "none";
435
436  // Remove onclickListener and add new one to
437  let btn = getFirstElmentByClassName(card, "faq-header-btn");
438  let newBtn = btn.cloneNode(true);
439  newBtn.addEventListener("click", () => extendFAQ(faq_no));
440  card.replaceChild(newBtn, btn);
441}
442
443// // Swiper
444const swiper = new Swiper(".swiper", {
445  // Optional parameters
446  direction: "horizontal",
447  loop: true,
448  navigation: {
449    nextEl: ".slideshow-right-btn",
450    prevEl: ".slideshow-left-btn",
451  },
452  breakpoints: {
453    // when window width is >= 320px
454    320: {
455      slidesPerView: 1,
456    },
457    1024: {
458      slidesPerView: 2,
459    },
460  },
461});
462
463// Logistic for number of PR switch
464const PACKAGE_ORI_PRICE_PR = {
465  professional: 189,
466  executive: 389,
467  director: 989,
468};
469
470const PACKAGE_1_PR = {
471  professional: {
472    new_price: 189,
473    total: 189,
474  },
475  executive: {
476    new_price: 389,
477    total: 389,
478  },
479  director: {
480    new_price: 989,
481    total: 989,
482  },
483};
484
485const PACKAGE_5_PR = {
486  professional: {
487    new_price: 169,
488    total: 845,
489  },
490  executive: {
491    new_price: 350,
492    total: 1750,
493  },
494  director: {
495    new_price: 929,
496    total: 4645,
497  },
498};
499
500// TO-DO Update the price to real one
501const PACKAGE_25_PR = {
502  professional: {
503    new_price: 139,
504    total: 3475,
505  },
506  executive: {
507    new_price: 300,
508    total: 7500,
509  },
510  director: {
511    new_price: 890,
512    total: 22250,
513  },
514};
515
516const PR_PACKAGES = {
517  0: PACKAGE_ORI_PRICE_PR,
518  1: PACKAGE_1_PR,
519  5: PACKAGE_5_PR,
520  25: PACKAGE_25_PR,
521};
522
523const NUM_PR_BUTTON = [];
524
525function loadPRSwitch() {
526  const buttons = document
527    .getElementById("num-PR-switch")
528    .getElementsByTagName("button");
529  for (var i = 0; i < buttons.length; i++) {
530    // Get important values
531    let button = buttons.item(i);
532
533    // Add on click listener
534    button.addEventListener("click", () => onClickPRSwitch(button));
535
536    // Add to constant
537    NUM_PR_BUTTON.push(button);
538  }
539
540  // Select the 1st button
541  onClickPRSwitch(NUM_PR_BUTTON[2]);
542}
543
544function updateSwitchColour(button) {
545  // Update everything to unselected style
546  for (let i = 0; i < NUM_PR_BUTTON.length; i++) {
547    NUM_PR_BUTTON[i].classList.remove("pr-num-button-selected");
548    NUM_PR_BUTTON[i].classList.add("pr-num-button-not-selected");
549  }
550
551  // Update selected button
552  button.classList.remove("pr-num-button-not-selected");
553  button.classList.add("pr-num-button-selected");
554}
555
556function onClickPRSwitch(button) {
557  var num_of_PR = Number(button.textContent);
558  console.log({ num_of_PR });
559  setPackagePrice(num_of_PR);
560
561  prx = num_of_PR;
562
563  updateSwitchColour(button);
564}
565
566function setPackagePrice(n) {
567  const packages = ["professional", "executive", "director"];
568
569  for (var i = 0; i < packages.length; i++) {
570    // Calculations for values to be displayed
571    let name = packages[i];
572    let new_price = PR_PACKAGES[n][name].new_price;
573    let total = PR_PACKAGES[n][name].total;
574    const oriTotal = PACKAGE_ORI_PRICE_PR[name] * n;
575    let save = n > 1 ? Math.round(((oriTotal - total) / oriTotal) * 100) : 0;
576
577    // Get UI components required
578    let save_box = document.getElementById(name + "-save-box");
579    let save_text = document.getElementById(name + "-save-price");
580    let ori_price_text = document.getElementById(name + "-ori-price");
581    let new_price_text = document.getElementById(name + "-new-price");
582    let total_text = document.getElementById(name + "-total-price");
583
584    // Change UI based on saved amount
585    if (save === 0) {
586      // Hide elements and remove stroke
587      save_box.style.visibility = "hidden";
588      new_price_text.style.visibility = "hidden";
589      ori_price_text.style.textDecoration = "none";
590    } else {
591      // Make elements visible and add stroke
592      save_box.style.visibility = "visible";
593      new_price_text.style.visibility = "visible";
594      ori_price_text.style.textDecoration = "line-through";
595
596      // Update new values
597      // save_text.innerHTML = '$' + save.toLocaleString();
598      save_text.innerHTML = save.toLocaleString() + "%";
599      new_price_text.innerHTML = "$" + new_price.toLocaleString();
600    }
601
602    total_text.innerHTML = "$" + total.toLocaleString();
603  }
604}
605
606function contactFormTimer(contactSaleLink) {
607  const now = new Date();
608  const month = now.getMonth();
609  const day = now.getDate();
610  const year = now.getFullYear();
611
612  // Sales Enquiry form redirection time
613  const startHour = "00"; // 12.01 am
614  const startMin = "01";
615  const endHour = "07"; // 7.59 am
616  const endMin = "59";
617
618  // Start and end datetime
619  const startTimeDate = new Date(year, month, day, startHour, startMin, "00");
620  const endTimeDate = new Date(year, month, day, endHour, endMin, "00");
621
622  // Start date and time down to millisec
623  const startTimeDateMsecs = startTimeDate.getTime();
624  const endTimeDateMsecs = endTimeDate.getTime();
625
626  // Current date time
627  const currentTimeDateMsecs = now.getTime();
628
629  // If current date time between start and end date time, redirect to sales enquiry form
630  // Else open the Tawkto dialog
631  if (
632    currentTimeDateMsecs >= startTimeDateMsecs &&
633    currentTimeDateMsecs <= endTimeDateMsecs
634  ) {
635    contactSaleLink.href = "https://marketersmedia.com/sales-enquiry-form";
636  } else {
637    contactSaleLink.href = "javascript:void(Tawk_API.toggle())";
638  }
639}
640
641const contactSaleLinks = document.getElementsByClassName("contact-sales-link");
642
643Array.from(contactSaleLinks).forEach((contactSaleLink) => {
644  contactSaleLink.addEventListener("click", () => {
645    contactFormTimer(contactSaleLink);
646  });
647});
648
649function main() {
650  addAgencySolutionOnClickListener();
651  setupAccountAndBraintree();
652  loadPRSwitch();
653  addFaqButtonOnClickListener();
654}
655
656main();

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.