PageSourceSearch

https://contango.xyz/data.js

js contango.xyz collected 2026-09-28 13:52:55 UTC 7,060 bytes, 187 lines download raw bytes

1async function getMediumPosts() {
2    const rssUrl = 'https://medium.com/feed/contango-xyz';
3    const proxyUrl = `https://api.allorigins.win/get?url=${encodeURIComponent(rssUrl)}`;
4
5    const numberOfPosts = 8;
6
7    try {
8        const response = await fetch(proxyUrl);
9        const data = await response.json();
10
11        // Parse the XML content
12        const parser = new DOMParser();
13        const xmlDoc = parser.parseFromString(data.contents, "text/xml");
14        const items = xmlDoc.querySelectorAll("item");
15        const blog = document.getElementById('blog');
16        const posts = blog.querySelector(".posts");
17        const pagination = document.createElement("div");
18        pagination.classList.add("pagination");
19        let postsHtml = '';
20        let paginationHtml = '';
21
22        items.forEach((item, index) => {
23            if (index >= numberOfPosts) return;
24            const title = item.querySelector("title").textContent;
25            const link = item.querySelector("link").textContent;
26            const pubDate = new Date(item.querySelector("pubDate").textContent);
27            const content = item.lastElementChild.textContent;
28            const imgMatch = content.match(/<img[^>]+src="([^">]+)"/);  // Regex to find the first image
29            const imgSrc = imgMatch ? imgMatch[1] : "assets/videos/landing-bg.png";  // Use placeholder if no image
30
31            postsHtml += `
32                    <a class="post card-style gradient-border" href="${link}" target="_blank">
33                        <div class="post-link">
34                            <h2>${title}</h2>
35                            <span>Read post</span>
36                        </div>
37                        <p>Published on: ${pubDate.toLocaleDateString()}</p>
38                        <div class="arrow"></div>
39                        <img src="${imgSrc}" alt="${title}">
40                        <input class="radio-items" type="radio" name="posts" id="post-${index}">
41                    </a>
42            `;
43
44            paginationHtml += `<label for="post-${index}"></label>`
45        });
46
47        blog.insertAdjacentHTML("afterbegin", '<h3>Learn more</h3>');
48        posts.innerHTML = postsHtml;
49        pagination.innerHTML = paginationHtml;
50        blog.appendChild(pagination);
51
52        const radioItems = posts.querySelectorAll(".radio-items");
53        radioItems[0].checked = true;
54        blog.querySelector(".posts-container").addEventListener("scroll", (e) => {
55            radioItems.forEach(radio => {
56                if (radio.getBoundingClientRect().left <= blog.getBoundingClientRect().width / 2) {
57                    radio.checked = true;
58                }
59
60            });
61        });
62
63    } catch (error) {
64        console.error('Error fetching Medium posts:', error);
65    }
66}
67
68async function graphQLQuery(query) {
69    return fetch("https://api.contango.xyz/graphql", {
70        method: 'POST',
71        headers: {
72            'Content-Type': 'application/json',
73        },
74        body: JSON.stringify({ query })
75    });
76}
77
78async function getAPIData() {
79
80    const totalOpenInterest = document.querySelector("#total-open-interest");
81    const volume = document.querySelector("#volume");
82    const uniqueUsers = document.querySelector("#unique-users");
83    const maxAnimationDuration = 20000;
84
85    const openInterestStartValue = Number(localStorage.getItem("openInterest") || 280000000);
86    const volStartValue = Number(localStorage.getItem("volume") || 3100000000);
87    const uniqueIdsStartValue = Number(localStorage.getItem("uniqueIds") || 16000);
88        
89    animateValue(totalOpenInterest, openInterestStartValue - 100000, openInterestStartValue, maxAnimationDuration, "$");
90    animateValue(volume, volStartValue - 100000, volStartValue + 100000, maxAnimationDuration, "$");
91    animateValue(uniqueUsers, uniqueIdsStartValue - 200, uniqueIdsStartValue + 200, maxAnimationDuration, "+");
92
93    // Define the GraphQL queries
94    const positionHistoryQuery = `
95        query {
96            positionHistoryItems{
97                aggregates{
98                    distinctCount{
99                        owner
100                    }
101                }
102            }
103        }`;
104
105    const openInterestQuery = `
106        query {
107            openInterests{
108                aggregates{
109                    sum{
110                        oiUsd
111                    }
112                }
113            }
114        }`;
115
116    const volumeQuery = `
117        query {
118            volumes{
119                aggregates{
120                    sum{
121                        volUsd
122                    }
123                }
124            }
125        }`;
126
127    // Send concurrent requests
128    Promise.all([
129        graphQLQuery(positionHistoryQuery),
130        graphQLQuery(openInterestQuery),
131        graphQLQuery(volumeQuery)
132    ])
133    .then(responses => Promise.all(responses.map(r => r.ok ? r.json() : null)))
134    .then(([positionData, openInterestData, volumeData]) => {
135        const uniqueIds = positionData?.data.positionHistoryItems.aggregates.distinctCount.owner;
136        if (uniqueIds) {
137            localStorage.setItem("uniqueIds", uniqueIds);
138            if (uniqueUsers.classList.contains("animate")) {
139                uniqueUsers.classList.remove("animate");
140                uniqueUsers.innerHTML = "+" + (Math.floor(uniqueIds / 100) * 100).toLocaleStr
140ing();
141            }
142        }
143
144        const openInterest = openInterestData?.data.openInterests.aggregates.sum.oiUsd;
145        if (openInterest) {
146            localStorage.setItem("openInterest", openInterest);
147            
148            if (totalOpenInterest.classList.contains("animate")) {
149                totalOpenInterest.classList.remove("animate");
150                totalOpenInterest.innerHTML = "$" + Math.floor(openInterest).toLocaleString();
151            }
152        }
153
154        const vol = volumeData?.data.volumes.aggregates.sum.volUsd;
155        if (vol) {
156            localStorage.setItem("volume", vol);
157            if (volume.classList.contains("animate")) {
158                volume.classList.remove("animate");
159                volume.innerHTML = "$" + Math.floor(vol).toLocaleString();
160            }
161        }
162    })
163    .catch(error => {
164        console.error("Error:", error);
165        totalOpenInterest.innerHTML = "$" + totalOpenInterest.textContent.toLocaleString();
166        volume.innerHTML = "$" + volume.textContent.toLocaleString();
167        uniqueUsers.innerHTML = "+" + uniqueUsers.textContent.toLocaleString();
168    });
169}
170
171function animateValue(obj, start, end, duration, option = "") {
172    let startTimestamp = null;
173    const step = (timestamp) => {
174        if (!obj.classList.contains("animate")) return;
175        if (!startTimestamp) startTimestamp = timestamp;
176        const progress = Math.min((timestamp - startTimestamp) / duration, 1);
177        if (progress < 1) {
178            window.requestAnimationFrame(step);
179        }
180        obj.innerHTML = option + Math.floor(progress * (end - start) + start).toLocaleString();
181    };
182    window.requestAnimationFrame(step);
183}
184
185getMediumPosts();
186getAPIData();
187

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.