PageSourceSearch

https://theplus.so/public/js/a.js?v=1786925950

js theplus.so collected 2026-10-09 22:25:44 UTC 9,716 bytes, 243 lines download raw bytes

1/*
2 * thePlus.so analytics beacon
3 *
4 * Cerez kullanmiyor, localStorage'a kimlik yazmiyor. Oturum kimligi yalnizca
5 * sekme omru boyunca sessionStorage'da duruyor ve sunucuda tuzlanip
6 * hashleniyor, yani geri cozulemiyor.
7 *
8 * Gonderdigi: yol, yonlendiren, ekran genisligi, sayfada gecen sure,
9 * tiklanan dugmeler, fiyat sayfasinda inilen derinlik.
10 * Gondermedigi: ham IP, cerez, parmak izi, kisisel hicbir sey.
11 */
12(function () {
13  'use strict';
14
15  var EP = '/admin/api/hit';
16
17  // Do Not Track ve Global Privacy Control saygi goruyor
18  if (navigator.doNotTrack === '1' || window.doNotTrack === '1' ||
19      navigator.globalPrivacyControl === true) { return; }
20
21  // onizleme ve mola sayfalarini sayma
22  if (location.search.indexOf('datecontrol=') !== -1) { return; }
23  if (location.pathname.indexOf('/pause') === 0) { return; }
24
25  var SK = 'tp_sid';
26  var sid, fresh = false;
27  try {
28    sid = sessionStorage.getItem(SK);
29    if (!sid) {
30      sid = Math.random().toString(36).slice(2) + Date.now().toString(36);
31      sessionStorage.setItem(SK, sid);
32      fresh = true;
33    }
34  } catch (e) { sid = 'nostore'; }
35
36  function send(body, keep) {
37    try {
38      var s = JSON.stringify(body);
39      if (keep && navigator.sendBeacon) {
40        navigator.sendBeacon(EP, new Blob([s], { type: 'application/json' }));
41      } else {
42        fetch(EP, { method: 'POST', body: s, keepalive: !!keep,
43                    headers: { 'Content-Type': 'application/json' } }).catch(function () {});
44      }
45    } catch (e) {}
46  }
47
48  var t0 = Date.now();
49  var path = location.pathname + location.search;
50
51  send({
52    p: path,
53    r: document.referrer || '',
54    n: fresh ? 1 : 0,
55    sid: sid,
56    w: window.screen ? window.screen.width : null
57  });
58
59  // Sayfa disi olay bildirmek icin: tpEvent('signup') ya da tpEvent('purchase', 149)
60  window.tpEvent = function (name, value, meta) {
61    send({ e: String(name).slice(0, 60), v: value, m: meta, p: path, sid: sid }, true);
62  };
63
64  // Ayrilirken sayfada gecen sureyi bildir. visibilitychange, pagehide'dan
65  // daha guvenilir: mobil tarayicilar pagehide'i her zaman tetiklemiyor.
66  var sent = false;
67  function bye() {
68    if (sent) { return; }
69    sent = true;
70    send({ p: path, sid: sid, u: 1, d: Math.round((Date.now() - t0) / 1000) }, true);
71  }
72  document.addEventListener('visibilitychange', function () {
73    if (document.visibilityState === 'hidden') { bye(); }
74  });
75  window.addEventListener('pagehide', bye);
76
77  /* ── hatalar ─────────────────────────────────────────────────────────────
78   * Ziyaretcinin tarayicisinda patlayan JavaScript hatalari. Asil soru
79   * "kac hata var" degil, "kac kisi bundan etkilendi"; panel oran olarak
80   * gosteriyor. Ayni hata ayni sayfada bir kez bildirilir, yoksa dongu
81   * icindeki bir hata butun sayilari bozar. */
82  var errSeen = {};
83  function reportErr(msg, src, line) {
84    var key = String(msg || '').slice(0, 180);
85    if (!key || errSeen[key]) { return; }
86    errSeen[key] = 1;
87    send({ x: key, xs: String(src || '').slice(0, 180), xl: line || 0, p: path, sid: sid }, true);
88  }
89  window.addEventListener('error', function (e) {
90    if (!e) { return; }
91    if (e.message) { reportErr(e.message, e.filename, e.lineno); }
92    else if (e.target && e.target.src) {
93      // resim, betik gibi bir kaynak yuklenemedi
94      reportErr('Failed to load ' + (e.target.tagName || '').toLowerCase(), e.target.src, 0);
95    }
96  }, true);
97  window.addEventListener('unhandledrejection', function (e) {
98    var r = e && e.reason;
99    reportErr('Unhandled promise: ' + (r && r.message ? r.message : String(r)), '', 0);
100  });
101
102  /* ── tiklamalar ──────────────────────────────────────────────────────────
103   * Sitede zaten yuzden fazla data-track isareti var ama bugune kadar hicbir
104   * sey onlari dinlemiyordu. Artik dinliyoruz.
105   *
106   * Fiyat sayfasinda ayrica isaretsiz dugme ve baglantilari da yaziyoruz,
107   * cunku orada neyin tiklandigini bilmek dogrudan para demek. Etiket olarak
108   * gorunen yazi kullaniliyor, kirkbir karakterde kesiliyor. */
109  var onPricing = /^\/(pricing|upgrade)\/?$/.test(location.pathname);
110
111  function label(el) {
112    var t = (el.textContent || '').replace(/\s+/g, ' ').trim();
113    if (!t) { t = el.getAttribute('aria-label') || el.getAttribute('title') || ''; }
114    if (!t) { t = (el.getAttribute('href') || '').slice(0, 40); }
115    return t.slice(0, 40);
116  }
117
118  document.addEventListener('click', function (ev) {
119    var el = ev.target && ev.target.closest ? ev.target.closest('[data-track],a,button') : null;
120    if (!el) { return; }
121
122    var tracked = el.getAttribute('data-track');
123    if (tracked) {
124      window.tpEvent('click:' + tracked.slice(0, 50));
125      return;
126    }
127    if (!onPricing) { return; }
128
129    // Fiyat sayfasi: neye basildigini bilmek istiyoruz
130    var href = el.getAttribute('href') || '';
131    var kind = /lemonsqueezy|checkout|buy/i.test(href) ? 'checkout'
132             : /membership|signup|register/i.test(href) ? 'signup'
133             : el.tagName === 'BUTTON' ? 'button' : 'link';
134    window.tpEvent('pricing:' + kind + ':' + label(el));
135  }, true);
136
137  /* ── fiyat sayfasinda inilen derinlik ────────────────────────────────────
138   * Hangi bolume kadar bakiliyor sorusunun cevabi. Her esik bir 
138kez bildirilir. */
139  if (onPricing) {
140    var marks = [25, 50, 75, 100], hit = {};
141    var depth = function () {
142      var h = document.documentElement;
143      var total = Math.max(h.scrollHeight - h.clientHeight, 1);
144      var p = Math.min(100, Math.round((window.scrollY || h.scrollTop) / total * 100));
145      for (var i = 0; i < marks.length; i++) {
146        if (p >= marks[i] && !hit[marks[i]]) {
147          hit[marks[i]] = 1;
148          window.tpEvent('pricing:depth:' + marks[i]);
149        }
150      }
151    };
152    var tick = null;
153    window.addEventListener('scroll', function () {
154      if (tick) { return; }
155      tick = setTimeout(function () { tick = null; depth(); }, 400);
156    }, { passive: true });
157    depth();
158  }
159
160  /* ---------------------------------------------------------------
161   * Fiyat sayfasi derin olcumu
162   *
163   * Yalnizca /pricing ve /upgrade uzerinde calisir. Cerez kullanmaz,
164   * ham koordinat gondermez: imlec ve tiklamalar 12 sutun x %5'lik
165   * satirlara yuvarlanir, yani veri dogasi geregi topludur.
166   * ------------------------------------------------------------- */
167  if (onPricing) {
168    var KOL = 12;          // yatay hucre sayisi
169    var SATIR_YUZDE = 5;   // her satir sayfanin %5'i
170    var tpHeat = {};       // "tip|kol|satir" -> sayac
171
172    function hucre(tip, x, yMutlak) {
173      var w = Math.max(document.documentElement.clientWidth, 1);
174      var h = Math.max(document.documentElement.scrollHeight, 1);
175      var kol = Math.min(KOL - 1, Math.max(0, Math.floor(x / w * KOL)));
176      var satir = Math.min(Math.floor(100 / SATIR_YUZDE) - 1,
177                           Math.max(0, Math.floor(yMutlak / h * (100 / SATIR_YUZDE))));
178      var k = tip + '|' + kol + '|' + satir;
179      tpHeat[k] = (tpHeat[k] || 0) + 1;
180    }
181
182    /* imleci saniyede iki kez ornekle, surekli dinleyip islem yapma */
183    var sonX = null, sonY = null;
184    document.addEventListener('mousemove', function (ev) {
185      sonX = ev.clientX; sonY = ev.clientY + (window.scrollY || 0);
186    }, { passive: true });
187    setInterval(function () {
188      if (sonX === null) { return; }
189      hucre('m', sonX, sonY);
190      sonX = null;
191    }, 500);
192
193    document.addEventListener('click', function (ev) {
194      hucre('c', ev.clientX, ev.clientY + (window.scrollY || 0));
195    }, { passive: true });
196
197    /* bolum gorunurlugu ve kart dikkati */
198    var gorulen = {}, dikkat = {}, girdi = {};
199    if (window.IntersectionObserver) {
200      var io = new IntersectionObserver(function (girisler) {
201        for (var i = 0; i < girisler.length; i++) {
202          var g = girisler[i];
203          var ad = g.target.getAttribute('data-sec')
204                || g.target.getAttribute('data-card') || '';
205          if (!ad) { continue; }
206          if (g.isIntersecting) {
207            if (!gorulen[ad]) { gorulen[ad] = 1; window.tpEvent('pricing:seen:' + ad); }
208            girdi[ad] = Date.now();
209          } else if (girdi[ad]) {
210            dikkat[ad] = (dikkat[ad] || 0) + (Date.now() - girdi[ad]);
211            girdi[ad] = 0;
212          }
213        }
214      }, { threshold: 0.5 });
215      var hedefler = document.querySelectorAll('[data-sec],[data-card]');
216      for (var j = 0; j < hedefler.length; j++) { io.observe(hedefler[j]); }
217    }
218
219    /* sayfadan cikarken toplu gonder */
220    function tpHeatGonder() {
221      var liste = [];
222      for (var k in tpHeat) {
223        if (!Object.prototype.hasOwnProperty.call(tpHeat, k)) { continue; }
224        var p = k.split('|');
225        liste.push([p[0], +p[1], +p[2], tpHeat[k]]);
226      }
227      /* acik kalan kartlarin suresini kapat */
228      for (var ad in girdi) {
229        if (girdi[ad]) { dikkat[ad] = (dikkat[ad] || 0) + (Date.now() - girdi[ad]); girdi[ad] = 0; }
230      }
231      var sure = [];
232      for (var a in dikkat) {
233        if (dikkat[a] > 800) { sure.push([a, Math.round(dikkat[a] / 1000)]); }
234      }
235      if (!liste.length && !sure.length) { return; }
236      send({ p: path, h: liste, a: sure }, true);
237    }
238    window.addEventListener('pagehide', tpHeatGonder);
239    document.addEventListener('visibilitychange', function () {
240      if (document.visibilityState === 'hidden') { tpHeatGonder(); }
241    });
242  }
243})();

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.