PageSourceSearch

https://good.you/static/js/growth.js?v=1

js good.you collected 2026-10-08 10:32:33 UTC 10,779 bytes, 161 lines download raw bytes

1(function () {
2  'use strict';
3  if (window.youGrowth) return;
4
5  var host = window.location.hostname.replace(/^www\./, '');
6  var key = 'you:growth:v1:' + host;
7  var blocked = /^\/(admin|auth|dashboard|account|api)(\/|$)/;
8  var legal = /^\/(about|contact|privacy|privacy-policy|terms|terms-of-service|cookies?|cookie-policy|accessibility)(\/|$)/;
9  var toolPath = /(tools?|calculator|check-?up|quiz|test|generator|planner|assessment|tracker)/i.test(location.pathname);
10  var allowedEvents = ['page_view', 'return_visit', 'save', 'unsave', 'share', 'copy_link', 'resume', 'tool_complete', 'subscribe', 'dismiss_resume', 'push_granted', 'push_denied', 'push_subscribed', 'push_error'];
11
12  function readState() {
13    try { return JSON.parse(localStorage.getItem(key)) || {}; } catch (_) { return {}; }
14  }
15  function writeState(state) {
16    try { localStorage.setItem(key, JSON.stringify(state)); } catch (_) {}
17  }
18  function canonicalUrl() {
19    var canonical = document.querySelector('link[rel="canonical"]');
20    return canonical ? canonical.href : location.origin + location.pathname;
21  }
22  function track(name, detail) {
23    if (allowedEvents.indexOf(name) === -1 || blocked.test(location.pathname)) return;
24    var body = JSON.stringify({event: name, path: location.pathname, detail: detail || {}});
25    try {
26      if (navigator.sendBeacon) {
27        navigator.sendBeacon('/engagement/event', new Blob([body], {type: 'application/json'}));
28      } else {
29        fetch('/engagement/event', {method: 'POST', headers: {'Content-Type': 'application/json'}, body: body, keepalive: true, credentials: 'same-origin'});
30      }
31    } catch (_) {}
32  }
33  function node(tag, className, text) {
34    var element = document.createElement(tag);
35    if (className) element.className = className;
36    if (text) element.textContent = text;
37    return element;
38  }
39  function addStyles() {
40    var style = node('style');
41    style.textContent = '.you-growth-actions{box-sizing:border-box;max-width:960px;margin:32px auto;padding:16px 18px;border:1px solid #d8dee8;border-radius:14px;background:#fff;color:#182230;box-shadow:0 8px 30px rgba(15,23,42,.07);font:600 14px/1.35 system-ui,-apple-system,sans-serif}.you-growth-actions__row{display:flex;flex-wrap:wrap;gap:10px;align-items:center}.you-growth-actions__label{margin-right:auto;font-weight:750}.you-growth-button,.you-growth-actions a{appearance:none;border:1px solid #b9c4d2;border-radius:999px;background:#fff;color:#182230;padding:9px 14px;font:700 13px/1 system-ui,-apple-system,sans-serif;text-decoration:none;cursor:pointer}.you-growth-button:hover,.you-growth-button:focus,.you-growth-actions a:hover,.you-growth-actions a:focus{border-color:#334155;background:#f1f5f9}.you-growth-nudge{margin:0 0 12px;padding:12px 14px;border-radius:10px;background:#f1f5f9;font-weight:500}.you-growth-resume{position:fixed;z-index:2147483000;left:16px;bottom:16px;box-sizing:border-box;width:min(380px,calc(100vw - 32px));padding:16px 44px 16px 16px;border:1px solid #d8dee8;border-radius:14px;background:#fff;color:#182230;box-shadow:0 16px 42px rgba(15,23,42,.2);font:14px/1.4 system-ui,-apple-system,sans-serif}.you-growth-resume strong{display:block;margin-bottom:7px}.you-growth-resume a{display:block;margin-top:5px;color:#0f766e;font-weight:750}.you-growth-resume button{position:absolute;top:8px;right:8px;border:0;background:transparent;font-size:20px;cursor:pointer}@media(max-width:520px){.you-growth-actions{margin:24px 12px}.you-growth-actions__label{width:100%}}';
42    document.head.appendChild(style);
43  }
44  function usefulPage() {
45    if (blocked.test(location.pathname) || legal.test(location.pathname)) return false;
46    if (location.pathname === '/') {
47      return !!document.querySelector('[data-result], .result-panel, .checkup-result, .mini-result, .calc-result, .result-box');
48    }
49    return !!document.querySelector('main article, article, [itemtype*="Article"]') || /(blog|learn|guides?|articles?|tools?|pickup-guide|personality|insurance|security|communication|relationships?|credit|debt)/i.test(location.pathname);
50  }
51  function rememberPage(state) {
52    if (!usefulPage()) return;
53    var item = {path: location.pathname, title: document.title.replace(/\s*[|—-]\s*[^|—-]+$/, '').trim(), seen: Date.now()};
54    state.recent = (state.recent || []).filter(function (entry) { return entry.path !== item.path; });
55    state.recent.unshift(item);
56    state.recent = state.recent.slice(0, 12);
57  }
58  function addActions(state) {
59    if (!usefulPage()) return null;
60    var main = document.querySelector('main');
61    if (!main || document.querySelector('.you-growth-actions')) return null;
62    var box = node('aside', 'you-growth-actions');
63    box.setAttribute('aria-label', 'Save and share this page');
64    var row = node('div', 'you-growth-actions__row');
65    row.appendChild(node('span', 'you-growth-actions__label', 'Keep this useful page handy'));
66    var saved = (state.saved || []).some(function (entry) { return entry.path === location.pathname; });
67    var saveButton = node('button', 'you-growth-button', saved ? 'Saved' : 'Save');
68    saveButton.type = 'button';
69    saveButton.addEventListener('click', function () {
70      state.saved = state.saved || [];
71      var index = state.saved.findIndex(function (entry) { return entry.path === location.pathname; });
72      if (index >= 0) {
73        state.saved.splice(index, 1); saveButton.textContent = 'Save'; track('unsave');
74      } else {
75        state.saved.unshift({path: location.pathname, title: document.title, saved: Date.now()});
76        state.saved = state.saved.slice(0, 25); saveButton.textContent = 'Saved'; track('save');
77      }
78      writeState(state);
79    });
80    row.appendChild(saveButton);
81    if (navigator.share) {
82      var shareButton = node('button', 'you-growth-button', 'Share');
83      shareButton.type = 'button';
84      shareButton.addEventListener('click', function () {
85        navigator.share({title: document.title, text: document.querySelector('meta[name="description"]')?.content || '', url: canonicalUrl()}).then(function () { track('share', {method: 'native'}); }).catch(function () {});
86      });
87      row.appendChild(shareButton);
88    }
89    var copyButton = node('button', 'you-growth-button', 'Copy link');
90    copyButton.type = 'button';
91    copyButton.addEventListener('click', function () {
92      navigator.clipboard.writeText(canonicalUrl()).then(function () {
93        copyButton.textContent = 'Link copied'; track('copy_link');
94        setTimeout(function () { copyButton.textContent = 'Copy link'; }, 1800);
95      }).catch(function () { window.prompt('Copy this link', canonicalUrl()); });
96    });
97    row.appendChild(copyButton);
98    box.appendChild(row);
99    main.appendChild(box);
100    return box;
101  }
102  function showResume(state) {
103    if (location.pathname !== '/') return;
104    if (Date.now() - (state.resumeDismissed || 0) < 7 * 86400000) return;
105    var isSaved = state.saved && state.saved.length;
106    var items = (isSaved ? state.saved : (state.recent || [])).filter(function (entry) { return entry.path !== '/'; }).slice(0, 3);
107    if (!items.length) return;
108    setTimeout(function () {
109      var box = node('aside', 'you-growth-resume');
110      box.appendChild(node('strong', '', isSaved ? 'Your saved pages' : 'Continue where you left off'));
111      items.forEach(function (item) {
112        var link = node('a', '', item.title || 'Open your recent page'); link.href = item.path;
113        link.addEventListener('click', function () { track('resume', {source: isSaved ? 'saved' : 'recent'}); }); box.appendChild(link);
114      });
115      var close = node('button', '', '×'); close.type = 'button'; close.setAttribute('aria-label', 'Dismiss');
116      close.addEventListener('click', function () { state.resumeDismissed = Date.now(); writeState(state); box.remove(); track('dismiss_resume'); });
117      box.appendChild(close); document.body.appendChild(box);
118    }, 1400);
119  }
120  function addCompletionNudge(actions) {
121    if (!actions || actions.querySelector('.you-growth-nudge')) return;
122    var nudge = node('div', 'you-growth-nudge', 'Result ready. Save this page so you can return, or share it with someone who would find it useful.');
123    if (host === 'good.you' || host === 'like.you') {
124      var link = node('a', '', 'Get occasional useful updates');
125      link.href = host === 'good.you' ? '/newsletter' : '#newsletter-title';
126      link.style.marginLeft = '10px'; link.addEventListener('click', function () { track('subscribe'); }); nudge.appendChild(link);
127    }
128    actions.insertBefore(nudge, actions.firstChild); track('tool_complete');
129  }
130  function observeResults(actions) {
131    if (!actions) return;
132    var selector = '[data-result], [data-test-result], .result-panel, .tool-result, .type-result, .checkup-result, .mini-result, .calc-result, .result-box, #result, #results';
133    if (!toolPath && !document.querySelector(selector)) return;
134    function visibleResult() {
135      return Array.prototype.some.call(document.querySelectorAll(selector), function (element) {
136        return !element.hidden && !element.classList.contains('is-hidden') && getComputedStyle(element).display !== 'none' && element.textContent.trim().length > 20;
137      });
138    }
139    if (visibleResult()) addCompletionNudge(actions);
140    var observer = new MutationObserver(function () { if (visibleResult()) { addCompletionNudge(actions); observer.disconnect(); } });
141    observer.observe(document.body, {subtree: true, childList: true, attributes: true, attributeFilter: ['hidden', 'class', 'style']});
142    document.addEventListener('submit', function () { setTimeout(function () { addCompletionNudge(actions); }, 500); }, {once: true});
143  }
144  function init() {
145    if (blocked.test(location.pathname)) return;
146    addStyles();
147    var state = readState();
148    var now = Date.now();
149    var previous = state.lastVisit || 0;
150    if (previous && now - previous > 20 * 3600000) {
151      var days = Math.round((now - previous) / 86400000);
152      track('return_visit', {bucket: days >= 30 ? '30d_plus' : days >= 7 ? '7_29d' : '1_6d'});
153    }
154    state.lastVisit = now; rememberPage(state); writeState(state); track('page_view');
155    var actions = addActions(state); showResume(state); observeResults(actions);
156    document.addEventListener('you:completed', function () { addCompletionNudge(actions); });
157    document.querySelectorAll('form[action*="newsletter"], [data-newsletter]').forEach(function (form) { form.addEventListener('submit', function () { track('subscribe'); }); });
158  }
159  window.youGrowth = {track: track, complete: function () { document.dispatchEvent(new CustomEvent('you:completed')); }};
160  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init); else init();
161}());

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.