PageSourceSearch

https://cdn.netzpolitik.org/wp-content/themes/vollehaerte/campaign…?v=v39.1790675973&ver=7.1.2

js netzpolitik.org collected 2026-10-01 07:20:53 UTC 6,771 bytes, 181 lines download raw bytes

1/* 
2Donation Goal Countdown - Modified from flip clock
3Creates a dramatic countdown from donation goal to remaining amount
4*/
5
6function flip(container, number, duration = 600) {
7    const currentNumber = container.dataset.number;
8    const baseTop = container.querySelector(".base .top");
9    const baseBottom = container.querySelector(".base .bottom");
10    const flaps = container.querySelectorAll(".flap");
11    const front = container.querySelector(".front");
12    const back = container.querySelector(".back");
13
14    // Set the animation duration dynamically
15    container.style.setProperty("--flip-duration", `${duration}ms`);
16
17    // Update the container's data attribute to track current number
18    container.dataset.number = number;
19
20    // Set up the flip animation: front shows current, back shows new number
21    front.dataset.content = currentNumber;
22    back.dataset.content = number;
23
24    // Update the top base to show the new number immediately
25    baseTop.innerHTML = number;
26
27    // Trigger the flip animation by adding the show class
28    flaps.forEach((flap) => {
29        flap.classList.add("show");
30    });
31
32    // After animation completes, clean up and set final state
33    setTimeout(() => {
34        flaps.forEach((flap) => {
35            flap.classList.remove("show");
36        });
37        baseBottom.innerHTML = number;
38    }, duration);
39}
40
41const set = (container, number) => {
42    // Set number without animation - used for initial setup
43    const base = container.querySelectorAll(".base div");
44
45    container.dataset.number = number;
46    base.forEach((div) => {
47        div.innerHTML = number;
48    });
49};
50
51// containerType = money, people, days
52const updateDisplay = (amount, shouldFlip, flipDuration = 600, containerType) => {
53    
54    // Determine padding based on container type
55    let paddingLength;
56    if (containerType === "days") {
57        paddingLength = 2;
58    } else if (containerType === "people") {
59        paddingLength = 3;
60    } else {
61        paddingLength = 4; // default for money and other types
62    }
63
64    //console.log("updateDisplay");
65    
66    // Convert amount to string with appropriate padding
67    const amountString = String(amount).padStart(paddingLength, "0");
68
69    // Get all digit container at once
70    const container = document.querySelectorAll(`.${containerType}-1, .${containerType}-2, .${containerType}-3, .${containerType}-4`);
71
72    //console.log("container", container);
73
74    // Update each digit position
75    container.forEach((container) => {
76        if (container instanceof HTMLElement && container) {
77            // Determine which digit this container should display based on its class
78            let digitIndex;
79            if (container.classList.contains(`${containerType}-1`)) digitIndex = 0;
80            else if (container.classList.contains(`${containerType}-2`)) digitIndex = 1;
81            else if (container.classList.contains(`${containerType}-3`)) digitIndex = 2;
82            else if (container.classList.contains(`${containerType}-4`)) digitIndex = 3;
83
84            const digit = amountString[digitIndex];
85            const currentDigit = container.dataset.number;
86
87            //console.log("updateDisplay foreach");
88
89            // Only update if the digit has actually changed and the digit exists
90            if (currentDigit !== digit && digit !== undefined) {
91                if (shouldFlip) {
92                    //console.log("flipduration", flipDuration);
93                    flip(container, digit, flipDuration);
94                } else {
95                    //console.log("set to", digit);
96                    set(container, digit);
97                }
98            }
99        }
100    });
101};
102
103// type = money, people, days
104const animateCountdown = (startAmount, endAmount, duration = 2000, type = "money") => {
105    const totalSteps = startAmount - endAmount;
106    const baseStepDuration = duration / totalSteps;
107    let currentAmount = startAmount;
108
109    //console.log("animationCountdown");
110
111    // Set initial display without animation
112    updateDisplay(currentAmount, false, 1, type);
113
114    // Define how many steps from the end should start slowing down
115    const easeOutRange = Math.min(100, Math.floor(totalSteps * 0.3));
116    const easeOutFactor = Math.pow(600, 1 / easeOutRange);
117
118    const scheduleNextStep = () => {
119        if (currentAmount <= endAmount) {
120            return; // We're done
121        }
122
123        currentAmount--;
124        const stepsRemaining = currentAmount - endAmount;
125
126        let targetStepDuration;
127        if (stepsRemaining <= easeOutRange) {
128            targetStepDuration = Math.max(baseStepDuration, Math.pow(easeOutFactor, easeOutRange - stepsRemaining));
129        } else {
130            targetStepDuration = baseStepDuration;
131        }
132
133        updateDisplay(currentAmount, true, targetStepDuration, containerType = type);
134
135        // Schedule the next step with the guaranteed-safe duration
136        setTimeout(() => {
137            scheduleNextStep();
138        }, targetStepDuration + 10);
139    };
140
141    // Start the countdown chain
142    setTimeout(() => {
143        scheduleNextStep();
144    }, baseStepDuration);
145};
146
147function get_twingle_campaign_data(field) {
148    const root = document.querySelector('.'+field);
149    return root.textContent;
150}
151
152
153
154
155
156// Initialize the donation countdown when page loads
157document.addEventListener("DOMContentLoaded", function () {
158    // Start the dramatic countdown from goal to remaining amount
159    // This creates the effect of counting down from 3000 to whatever is still needed
160    // Configuration for your donation campaign
161    const DONATION_GOAL = 3000;
162    const CURRENT_DONATION_AMOUNT = get_twingle_campaign_data("data-current-donation-amount"); // This would come from your backend/API
163    const REMAINING_AMOUNT = DONATION_GOAL - CURRENT_DONATION_AMOUNT;
164    const TOTAL_DAYS = 30;
165    const start = new Date(get_twingle_campaign_data("data-first-day")); // Erster Tag
166    const today = new Date();
167    const difference = today - start;
168    const REMAINING_DAYS = Math.ceil(30 - difference / (1000 * 60 * 60 * 24));
169    // console.log("start:"+start);
170    // console.log("today:"+today);
171    // console.log("difference:"+difference);
172    // console.log("REMAINING_DAYS:"+REMAINING_DAYS);
173    const TOTAL_DONATORS = 300;
174    const CURRENT_DONATOR_COUNT = get_twingle_campaign_data("data-current-donator-count");
175    const REMAINING_DONATOR_COUNT = TOTAL_DONATORS - CURRENT_DONATOR_COUNT;
176    const ANIMATION_DURATION = 3000;
177    // Todo trigger when visible …
178    animateCountdown(DONATION_GOAL, REMAINING_AMOUNT, ANIMATION_DURATION, "money");
179    animateCountdown(TOTAL_DAYS, REMAINING_DAYS, ANIMATION_DURATION, "days");
180    animateCountdown(TOTAL_DONATORS, REMAINING_DONATOR_COUNT, ANIMATION_DURATION, "people");
181});

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.