PageSourceSearch

https://riddimbank.com/js/free-riddim-popup.js

js riddimbank.com collected 2026-10-07 22:24:19 UTC 9,125 bytes, 138 lines download raw bytes

1/* free-riddim-popup.js (28 Sep 2026) — riddimBank's own free-riddim popup,
2   replacing OptiMonk. Loaded on every page from _includes/_head.njk.
3
4   SHOWS: after 45 seconds on a page, or (desktop) when the mouse leaves the top
5   of the window, whichever is first. At most once every 7 days. Never again after
6   the visitor signs up, or after they close it 3 times.
7   NEVER ON: /free-riddim*, cart, checkout/success, account, set-password,
8   manage-subscription, admin-*, *-download pages, contributor/referrer/contractor portals,
9   or for anyone signed in (Firebase Auth).
10   NO TRACKING: nothing is sent anywhere until the visitor submits the form. It
11   only keeps a small note in this browser (localStorage "rbFreeRiddimPopup") so it
12   doesn't nag, which is why it needs no cookie consent and shows to everyone.
13   Testing: add ?rbpopup=now to any page to see it straight away.
14   The form posts to the same Cloud Function as /free-riddim (freeRiddimSignup),
15   so delivery is instant (Mailgun) and the person lands on the INBOX list
16   "Free riddim (website)". Kill switch: set window.RB_NO_POPUP = true. */
17(function () {
18  if (window.RB_NO_POPUP) return;
19  var ENDPOINT = 'https://us-central1-riddimbank-webauth.cloudfunctions.net/freeRiddimSignup';
20  var KEY = 'rbFreeRiddimPopup';
21  var DELAY_MS = 45000, GAP_MS = 7 * 24 * 60 * 60 * 1000, MAX_CLOSES = 3;
22  var path = location.pathname;
23  if (/^\/(free-riddim|cart|checkout|success|account|set-password|manage-subscription|admin|contributor-portal|referrer-portal|contractor-portal|deposit|upload|become-a-contributor|become-a-referrer)/.test(path)
24      || /-download\/?$/.test(path)) return;
25
26  function load() { try { return JSON.parse(localStorage.getItem(KEY) || '{}'); } catch (e) { return {}; } }
27  function save(s) { try { localStorage.setItem(KEY, JSON.stringify(s)); } catch (e) {} }
28  var st = load();
29  var force = /[?&]rbpopup=now\b/.test(location.search);   /* testing: ?rbpopup=now shows it at once, ignoring the rules above */
30  if (force) { st = {}; }
31  else if (st.signedUp || (st.closes || 0) >= MAX_CLOSES) return;
32  if (st.lastShown && Date.now() - st.lastShown < GAP_MS) return;
33
34  function signedIn() {
35    try { return !!(window.firebase && firebase.auth && firebase.auth().currentUser); } catch (e) { return false; }
36  }
37
38  var shown = false, timer = null;
39  function show() {
40    if (shown || (!force && signedIn())) return;
41    shown = true;
42    clearTimeout(timer);
43    document.removeEventListener('mouseout', onLeave);
44    st.lastShown = Date.now(); save(st);
45    build();
46  }
47  function onLeave(e) { if (!e.relatedTarget && e.clientY <= 0) show(); }
48
49  function build() {
50    var css = document.createElement('style');
51    css.textContent =
52      '.rbfr-ov{position:fixed;inset:0;background:rgba(0,0,0,.72);z-index:99999;display:flex;align-items:center;justify-content:center;padding:16px;animation:rbfrIn .25s ease}' +
53      '@keyframes rbfrIn{from{opacity:0}to{opacity:1}}' +
54      '.rbfr-box{position:relative;width:100%;max-width:440px;background:#0a0a0a;border:1px solid #2a2a2a;border-radius:14px;padding:28px 24px 22px;color:#f2f2f2;font-family:Arial,Helvetica,sans-serif;box-shadow:0 20px 60px rgba(0,0,0,.6)}' +
55      '.rbfr-x{position:absolute;top:10px;right:12px;background:none;border:0;color:#aaa;font-size:26px;line-height:1;cursor:pointer;padding:4px}' +
56      '.rbfr-k{margin:0;color:#1DB954;text-transform:uppercase;letter-spacing:.08em;font-size:12px}' +
57      '.rbfr-h{margin:6px 0 8px;font-family:"Archivo Black","Arial Black",Arial,sans-serif;font-size:26px;line-height:1.15;color:#fff}' +
58      '.rbfr-p{margin:0 0 16px;color:#ccc;font-size:15px;line-height:1.5}' +
59      '.rbfr-box input[type=text],.rbfr-box input[type=email]{width:100%;box-sizing:border-box;padding:12px;margin:0 0 10px;border-radius:8px;border:1px solid #333;background:#000;color:#fff;font-size:16px}' +
60      '.rbfr-box input:focus{outline:2px solid #1DB954;outline-offset:1px}' +
61      '.rbfr-hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}' +
62      '.rbfr-btn{display:block;width:100%;background:#1DB954;color:#000;border:0;border-radius:8px;padding:14px;font-weight:700;font-size:17px;cursor:pointer}' +
63      '.rbfr-btn:disabled{opacity:.6}' +
64      '.rbfr-s{margin:10px 0 0;color:#888;font-size:12px;line-height:1.4}.rbfr-s a{color:#1DB954}' +
65      '.rbfr-m{margin:8px 0 0;color:#ffcf66;font-size:14px;min-height:1em}';
66    document.head.appendChild(css);
67
68    var ov = document.createElement('div');
69    ov.className = 'rbfr-ov';
70    ov.setAttribute('role', 'dialog');
71    ov.setAttribute('aria-modal', 'true');
72    ov.setAttribute('aria-labelledby', 'rbfrTitle');
73    ov.innerHTML =
74      '<div class="rbfr-box">' +
75        '<button type="button" class="rbfr-x" aria-label="Close">&times;</button>' +
76        '<div class="rbfr-body">' +
77          '<p class="rbfr-k">Free for you</p>' +
78          '<h2 class="rbfr-h" id="rbfrTitle">Get a FREE multitrack riddim</h2>' +
79          '<p class="rbfr-p">&ldquo;First Class Citizen&rdquo;, recorded for Edi Fitzroy&rsquo;s final album. 12 tracks, 24-bit WAV. We&rsquo;ll email it to you in seconds.</p>' +
80          '<form novalidate>' +
81            '<input type="text" name="firstName" placeholder="First name" autocomplete="given-name" maxlength="60" aria-label="First name">' +
82            '<input type="email" name="email" placeholder="Email" autocomplete="email" maxlength="200" aria-label="Email">' +
83            '<div class="rbfr-hp" aria-hidden="true"><input name="website" tabindex="-1" autocomplete="off"><input name="url" tabindex="-1" autocomplete="off"><input name="phone_number" tabindex="-1" autocomplete="off"></div>' +
84            '<button type="submit" class="rbfr-btn">Send me the free riddim &rarr;</button>' +
85            '<p class="rbfr-m" role="status"></p>' +
86          '</form>' +
87          '<p class="rbfr-s">We&rsquo;ll add you to our newsletter. Unsubscribe any time. <a href="/free-riddim/">See what&rsquo;s included</a></p>' +
88        '</div>' +
89      '</div>';
90    document.body.appendChild(ov);
91
92    var box = ov.querySelector('.rbfr-box'), form = ov.querySelector('form'), msg = ov.querySelector('.rbfr-m'), btn = ov.querySelector('.rbfr-btn');
93    var lastFocus = document.activeElement;
94    function close(counted) {
95      if (counted) { st.closes = (st.closes || 0) + 1; save(st); }
96      ov.remove();
97      document.removeEventListener('keydown', onKey);
98      if (lastFocus && lastFocus.focus) try { lastFocus.focus(); } catch (e) {}
99    }
100    function onKey(e) { if (e.key === 'Escape') close(true); }
101    document.addEventListener('keydown', onKey);
102    ov.querySelector('.rbfr-x').addEventListener('click', function () { close(true); });
103    ov.addEventListener('click', function (e) { if (e.target === ov) close(true); });
104    setTimeout(function () { var f = form.querySelector('input[name=firstName]'); if (f) f.focus(); }, 50);
105
106    form.addEventListener('submit', function (e) {
107      e.preventDefault();
108      msg.textContent = '';
109      var fd = new FormData(form);
110      var firstName = String(fd.get('firstName') || '').trim(), email = String(fd.get('email') || '').trim();
111      if (!firstName) { msg.textContent = 'Please enter your first name.'; return; }
112      if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) { msg.textContent = 'Please enter a valid email address.'; return; }
113      btn.disabled = true; btn.textContent = 'Sending…';
114      fetch(ENDPOINT, {
115        method: 'POST', headers: { 'Content-Type': 'application/json' },
116        body: JSON.stringify({ firstName: firstName, email: email, website: fd.get('website') || '', url: fd.get('url') || '', phone_number: fd.get('phone_number') || '',
117          fromPage: 'popup:' + location.pathname, referrer: document.referrer || '' })
118      }).then(function (r) { return r.json().catch(function () { return {}; }).then(function (j) { return { ok: r.ok, j: j }; }); })
119        .then(function (res) {
120          if (res.ok && res.j.success) {
121            st.signedUp = true; save(st);
122            ov.querySelector('.rbfr-body').innerHTML =
123              '<p class="rbfr-k">On its way</p><h2 class="rbfr-h">Check your inbox</h2>' +
124              '<p class="rbfr-p">Your free riddim should arrive within a minute. If you don&rsquo;t see it, check your spam or promotions folder.</p>' +
125              '<button type="button" class="rbfr-btn">Keep browsing</button>';
126            ov.querySelector('.rbfr-body .rbfr-btn').addEventListener('click', function () { close(false); });
127            return;
128          }
129          msg.textContent = res.j.error || 'Something went wrong. Please try again in a minute.';
130          btn.disabled = false; btn.innerHTML = 'Send me the free riddim &rarr;';
131        })
132        .catch(function () { msg.textContent = 'Could not connect. Please try again.'; btn.disabled = false; btn.innerHTML = 'Send me the free riddim &rarr;'; });
133    });
134  }
135
136  timer = setTimeout(show, force ? 300 : DELAY_MS);
137  if (window.matchMedia && window.matchMedia('(pointer:fine)').matches) document.addEventListener('mouseout', onLeave);
138})();

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.