PageSourceSearch

https://cybermobbing.web.app/js/firebase-counter.js?v=1784223914

js cybermobbing.web.app collected 2026-10-03 12:20:49 UTC 5,259 bytes, 163 lines download raw bytes

1/**
2 * @file firebase-counter.js
3 * @description Firebase Realtime Database view counter with daily usage limit.
4 *   Tracks total all-time views and per-day views. If the daily count exceeds
5 *   DAILY_LIMIT, the start screen is replaced with a "limit reached" page that
6 *   shows a countdown to midnight. View counts are displayed live via Firebase
7 *   .on('value') listeners.
8 * @requires config.js - firebaseConfig object and DAILY_LIMIT constant
9 */
10
11// ========== FIREBASE VIEW COUNTER + DAILY LIMIT ==========
12
13// Initialize Firebase app with credentials from config.js
14firebase.initializeApp(firebaseConfig);
15
16var db = firebase.database();
17
18/** @type {firebase.database.Reference} Reference to /views (total all-time count) */
19var viewsRef = db.ref('views');
20
21/** @type {string} Today's date as YYYY-MM-DD, used as the daily counter key */
22var today = new Date().toISOString().slice(0, 10);
23
24/** @type {firebase.database.Reference} Reference to /daily/<today> (today's count) */
25var dailyRef = db.ref('daily/' + today);
26
27// On page load, check if today's limit has been reached
28dailyRef.once('value', function (snapshot) {
29  var todayCount = snapshot.val() || 0;
30
31  if (todayCount >= DAILY_LIMIT) {
32    document.getElementById('start').classList.add('hidden');
33    document.getElementById('limitPage').classList.add('show');
34    startLimitTimer();
35  }
36});
37
38/**
39 * Per-browser deduplication: a browser increments the counter at most once
40 * per UTC day. Prevents inflated counts from refresh loops (e.g. students
41 * replaying the simulation within one lesson) and raises the bar for
42 * casual abuse -- a scripted attacker still bypasses this by clearing
43 * localStorage, but organic double-counting is eliminated entirely.
44 * Storage failures (private mode, disabled cookies) fall through silently
45 * so the counter keeps working.
46 */
47var COUNT_STORAGE_KEY = 'cms_last_count';
48
49function hasCountedToday() {
50  try {
51    return localStorage.getItem(COUNT_STORAGE_KEY) === today;
52  } catch (e) {
53    return false;
54  }
55}
56
57function markCountedToday() {
58  try {
59    localStorage.setItem(COUNT_STORAGE_KEY, today);
60  } catch (e) {
61    /* localStorage unavailable -- accept duplicate count */
62  }
63}
64
65/**
66 * Atomically increments both the total and daily view counters.
67 * Called once when the simulation starts (from go() in main.js).
68 * Uses Firebase transactions to avoid race conditions with concurrent users.
69 * Skips the increment entirely if this browser has already counted today.
70 */
71function incrementCounters() {
72  if (hasCountedToday()) return;
73  markCountedToday();
74  viewsRef
75    .transaction(function (current) {
76      return (current || 0) + 1;
77    })
78    .catch(function (err) {
79      if (typeof console !== 'undefined') console.warn('Counter increment failed:', err);
80    });
81  dailyRef
82    .transaction(function (current) {
83      return (current || 0) + 1;
84    })
85    .catch(function (err) {
86      if (typeof console !== 'undefined') console.warn('Daily counter increment failed:', err);
87    });
88}
89
90// Live-update all counter display elements whenever the total changes.
91// If the Firebase connection is blocked (ad blockers, school firewalls,
92// Firefox ETP), the callback never fires; hide the counter boxes after
93// 5s so users do not see the "--" placeholder stuck on screen.
94var viewCountLoaded = false;
95
96function hideViewCounters() {
97  var boxes = document.querySelectorAll('.start-views, .fin-views');
98  for (var i = 0; i < boxes.length; i++) boxes[i].style.display = 'none';
99}
100
101viewsRef.on(
102  'value',
103  function (snapshot) {
104    viewCountLoaded = true;
105    var count = snapshot.val() || 0;
106    var formatted = count.toLocaleString('de-DE');
107    var el = document.getElementById('viewCount');
108    var el2 = document.getElementById('viewCountStart');
109    if (el) el.textContent = formatted;
110    if (el2) el2.textContent = formatted;
111  },
112  function () {
113    hideViewCounters();
114  }
115);
116
117setTimeout(function () {
118  if (!viewCountLoaded) hideViewCounters();
119}, 5000);
120
121/**
122 * Starts a live countdown timer showing the time remaining until midnight,
123 * when the daily limit resets. Automatically reloads the page at midnight.
124 */
125function startLimitTimer() {
126  var el = document.getElementById('limitTimer');
127  if (!el) return;
128
129  /**
130   * Updates the HH:MM:SS countdown display.
131   * The daily counter key (`today`) is derived from UTC, so the reset
132   * must also be computed against UTC midnight — otherwise a user in
133   * CET/CEST sees a 24h countdown at local midnight while the UTC key
134   * actually rolls over 1-2h later.
135   */
136  function update() {
137    var now = new Date();
138    var midnight = new Date(
139      Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1, 0, 0, 0)
140    );
141    var diff = midnight - now;
142
143    var h = Math.floor(diff / 3600000);
144    var m = Math.floor((diff % 3600000) / 60000);
145    var s = Math.floor((diff % 60000) / 1000);
146
147    el.textContent =
148      String(h).padStart(2, '0') +
149      ':' +
150      String(m).padStart(2, '0') +
151      ':' +
152      String(s).padStart(2, '0');
153
154    // When midnight arrives, reload to clear the limit
155    if (diff <= 0) {
156      clearInterval(timerInt);
157      window.location.reload();
158    }
159  }
160
161  update();
162  var timerInt = setInterval(update, 1000);
163}

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.