PageSourceSearch

https://khum.com/script.js

js khum.com collected 2026-10-02 11:44:39 UTC 16,418 bytes, 399 lines download raw bytes

1//VARIABLES
2
3const imageDepotBase = "https://lccibackend.com/merlin/imageDepot/";
4const url = new URL(location);
5let grabbedPages = [];
6let activePage = "";
7
8let pages = {
9    "home":{"url":"pages/home.html", "script":"assets/scripts/home.js"},
10    "djs":{"url":"pages/djs.html", "script":"assets/scripts/djs.js"},
11    "schedule":{"url":"pages/schedule.html", "script":"assets/scripts/schedule.js"},
12    "features":{"url":"pages/features.html", "script":"assets/scripts/features.js"},
13    "playlist":{"url":"pages/playlist.html", "script":"assets/scripts/playlist.js"},
14    "contact":{"url":"pages/contact.html"},
15    "eeo":{"url":"pages/eeo.html"},
16    "fcc":{"url":"pages/fcc.html"},
17    "contestrules":{"url":"pages/contestrules.html"},
18};
19
20// const storedVotes = localStorage.getItem("storedVotes");
21// let sentVotes = [];
22// if(storedVotes != null){ sentVotes = JSON.parse(storedVotes); }
23
24let sentVotes = (localStorage.getItem("storedVotes")) ? JSON.parse(localStorage.getItem("storedVotes")) : [];
25
26//DOM HOOKS
27
28const dialogs = {
29    "lccFamily":{
30        "modal":document.getElementById("lccfamily-dialog"),
31        "closeButton":document.getElementById("lccfamily-dialog-close-button"),
32    }
33};
34
35const nav = {
36    "home":document.getElementById("home-nav-button"),
37    "djs":document.getElementById("djs-nav-button"),
38    "schedule":document.getElementById("schedule-nav-button"),
39    "features":document.getElementById("features-nav-button"),
40    "playlist":document.getElementById("playlist-nav-button"),
41    "contact":document.getElementById("contact-nav-button"),
42    "goodTimeGuide":document.getElementById("goodtimeguide-nav-button"),
43    "swag":document.getElementById("swag-nav-button"),
44    "facebook":document.getElementById("facebook-nav-button"),
45    "instagram":document.getElementById("instagram-nav-button"),
46    "twitter":document.getElementById("twitter-nav-button")
47};
48
49const nowPlaying = {
50    "section":document.getElementById("nowplaying-section"),
51    "audio":document.getElementById("nowplaying-audio"),
52    "placeholder":document.getElementById("nowplaying-placeholder"),
53    "controller":document.getElementById("nowplaying-controller-div"),
54    "tuneInButton":document.getElementById("tunein-button"),
55    "albumCover":document.getElementById("nowplaying-img"),
56    "info":document.getElementById("nowplaying-info"),
57    "title":document.getElementById("nowplaying-title"),
58    "artist":document.getElementById("nowplaying-artist"),
59    "volumeslider":document.getElementById("nowplaying-volume-slider"),
60    "volumeTrack":document.getElementById("nowplaying-volume-track"),
61    "upVoteButton":document.getElementById("nowplaying-upvote-button"),
62    "upVoteSpan":document.getElementById("nowplaying-upvote-span"),
63    "downVoteButton":document.getElementById("nowplaying-downvote-button"),
64    "downVoteSpan":document.getElementById("nowplaying-downvote-span"),
65    "currentSong":{},
66    "isSetUp":false,
67};
68
69const recentlyPlayed = {
70    "placeholder":document.getElementById("recentlyplayed-placeholder"),
71    "info":document.getElementById("recentlyplayed-info"),
72    "entry1":document.getElementById("recentlyplayed1-entry"),
73    "title1":document.getElementById("recentlyplayed1-title"),
74    "artist1":document.getElementById("recentlyplayed1-artist"),
75    "time1":document.getElementById("recentlyplayed1-time"),
76    "entry2":document.getElementById("recentlyplayed2-entry"),
77    "title2":document.getElementById("recentlyplayed2-title"),
78    "artist2":document.getElementById("recentlyplayed2-artist"),
79    "time2":document.getElementById("recentlyplayed2-time"),
80    "entry3":document.getElementById("recentlyplayed3-entry"),
81    "title3":document.getElementById("recentlyplayed3-title"),
82    "artist3":document.getElementById("recentlyplayed3-artist"),
83    "time3":document.getElementById("recentlyplayed3-time"),
84    "recentSongs":[],
85    "isSetUp":false,
86};
87
88const footer = {
89    "pawscauseButton":document.getElementById("pawscause-footer-button"),
90    "lccfamilyButton":document.getElementById("lccfamily-footer-button"),
91    "eeoButton":document.getElementById("eeo-footer-button"),
92    "fccButton":document.getElementById("fcc-footer-button"),
93    "contestrulesButton":document.getElementById("contestrules-footer-button"),
94    "publicfileButton":document.getElementById("publicfile-footer-button"),
95    "copyrightYear":document.getElementById("copyright-year"),
96};
97
98const mobile = {
99    "menu":document.getElementById("mobile-menu"),
100    "toggle":document.getElementById("mobile-menu-toggle"),
101    "nowPlayingWrapper":document.getElementById("mobile-now-playing-wrapper"),
102};
103
104//DOM FUNCTIONS
105
106function simpleDate(_input){
107    let dateObject = new Date(Date.parse(_input));
108    let output = "";
109    let ampm = "PM";
110    if(dateObject.getHours() > 12 ){
111        output = output + (dateObject.getHours() - 12);
112        if(dateObject.getHours() == 12){
113            output = output + (dateObject.getHours());
114            ampm = "AM";
115        }
116    } else {
117        output = output + (dateObject.getHours());
118        ampm = "AM";
119    }
120    if(dateObject.getMinutes() < 10){
121        output = output + ":0";
122    } else {
123        output = output + ":";
124    }
125    output = output + dateObject.getMinutes() + ampm;
126    return output;
127}
128
129function displayRecentlyPlayed(){
130    if(!recentlyPlayed.isSetUp){ return; }
131    recentlyPlayed.title1.textContent = recentlyPlayed.recentSongs[0].title;
132    recentlyPlayed.artist1.textContent = "By: " + recentlyPlayed.recentSongs[0].artist;
133    recentlyPlayed.time1.textContent = simpleDate(recentlyPlayed.recentSongs[0].time);
134    recentlyPlayed.title2.textContent = recentlyPlayed.recentSongs[1].title;
135    recentlyPlayed.artist2.textContent = "By: " + recentlyPlayed.recentSongs[1].artist;
136    recentlyPlayed.time2.textContent = simpleDate(recentlyPlayed.recentSongs[1].time);
137    recentlyPlayed.title3.textContent = recentlyPlayed.recentSongs[2].title;
138    recentlyPlayed.artist3.textContent = "By: " + recentlyPlayed.recentSongs[2].artist;
139    recentlyPlayed.time3.textContent = simpleDate(recentlyPlayed.recentSongs[2].time);
140}
141
142function displayCurrentSong(){
143    if(!nowPlaying.isSetUp){ return; }
144    if(nowPlaying.currentSong.cover_art_url == ""){
145        nowPlaying.albumCover.src = "assets/images/khum-missing.jpg";
146    } else {
147        nowPlaying.albumCover.src = nowPlaying.currentSong.cover_art_url;
148    }
149    nowPlaying.title.textContent = nowPlaying.currentSong.title;
150    nowPlaying.artist.textContent = "By: " + nowPlaying.currentSong.artist;
151    nowPlaying.upVoteSpan.textContent = nowPlaying.currentSong.up;
152    nowPlaying.downVoteSpan.textContent = nowPlaying.currentSong.down;
153}
154
155function assignPage(_pageLabel){
156    url.searchParams.set("page", _pageLabel);
157    history.replaceState({},"",url);
158}
159
160//ASYNC FUNCTIONS
161
162async function getRecentlyPlayed(){
163    let payload = new FormData;
164    const url="/common/scripts/fetch/fetch_recently_played.php?station=KHUM";
165    const response = await fetch(url, {
166        method: "POST",
167        body: payload,
168    }).then(_response => {
169        return _response.json();
170    }).then(_message => {
171        if(_message.success){
172            nowPlaying.currentSong = JSON.parse(_message.currentSong);
173            recentlyPlayed.recentSongs = JSON.parse(_message.recentSongs);
174            nowPlaying.isSetUp = true;
175            recentlyPlayed.isSetUp = true;
176            displayRecentlyPlayed();
177            displayCurrentSong();
178            recentlyPlayed.placeholder.classList.add("hidden");
179            recentlyPlayed.info.hidden = false;
180            nowPlaying.placeholder.classList.add("hidden");
181            nowPlaying.info.hidden = false;
182        } else {
183            alert(_message.message);
184        }
185    });
186}
187
188async function loadPage(_pageKey){
189    let _url = pages[_pageKey].url;
190    let _label = _pageKey;
191    let _scriptUrl = (pages[_pageKey].script) ? pages[_pageKey].script : "";
192
193    mobile.menu.classList.remove("open");
194    mobile.toggle.textContent = "=";
195
196    if(activePage == _label){ return; } //If we're already seeing that page, do nothing
197
198    assignPage(_label);
199
200    document.querySelectorAll("[data-pagekey]").forEach(_pageButton=>{ _pageButton.disabled = false; });
201    document.querySelectorAll("[data-pagekey="+_label+"]").forEach(_button=>{ _button.disabled = true; });
202
203    if(grabbedPages.includes(_label)){ //If we've already grabbed that page, just show it
204        document.querySelectorAll(".page").forEach(_page=>{ _page.classList.add("hidden"); });
205        document.getElementById(_label).classList.remove("hidden");
206        activePage = _label;
207        return;
208    }
209
210    //otherwise, fetch it and show it.
211
212    fetch(_url).then(function(response) {
213        return response.text();
214    }).then(function(_html){
215        const newPage = document.createElement("div");
216        newPage.classList.add("page");
217        newPage.id = _label;
218        newPage.innerHTML = _html;
219        if(_scriptUrl != ""){
220            let runtimeScript = document.createElement("script");
221            runtimeScript.defer = true;
222            runtimeScript.src = _scriptUrl;
223            newPage.appendChild(runtimeScript);
224        }
225        let pages = document.querySelectorAll(".page");
226        pages.forEach(_page=>{
227            _page.classList.add("hidden");
228        });
229        grabbedPages.push(_label);
230        activePage = _label;
231        document.querySelector("main").appendChild(newPage);
232    })
233}
234
235async function sendVote(_songId, _isUpVote){
236    //send vote off
237    let payload = new FormData;
238
239    payload.set("song_id", _songId);
240    payload.set("station", 2);
241    if(_isUpVote){
242        payload.set("up", 1);
243        payload.set("down", 0);
244    } else {
245        payload.set("up", 0);
246        payload.set("down", 1);
247    }
248
249    const url="https://lccibackend.com/vote_receiver.php";
250    const myHeaders = new Headers();
251    myHeaders.append("Access-Control-Allow-Origin","https://khum.com");
252    myHeaders.append("Access-Control-Allow-Credentials", "true");
253
254    const response = await fetch(url, {
255        method: "POST",
256        // headers: myHeaders,
257        body: payload
258    }).then(_response => {
259        return _response.json();
260    }).then(_message => {
261        if(!_message.success){
262            alert(_message.message);
263        }
264    });
265}
266
267//EVENT LISTENERS
268
269document.addEventListener("voteSent",_event=>{
270    if(!sentVotes.includes(_event.detail.songId)){
271        sendVote(_event.detail.songId, _event.detail.isUpVote);
272        sentVotes.push(_event.detail.songId);
273        localStorage.setItem("storedVotes", JSON.stringify(sentVotes));
274    }
275    if(_event.detail.isUpVote){
276        nowPlaying.currentSong.up ++;
277    } else {
278        nowPlaying.currentSong.down ++;
279    }
280    nowPlaying.upVoteSpan.textContent = nowPlaying.currentSong.up;
281    nowPlaying.downVoteSpan.textContent = nowPlaying.currentSong.down;
282});
283
284nowPlaying.tuneInButton.addEventListener("click",_event=>{
285    nowPlaying.audio.play();
286    nowPlaying.tuneInButton.classList.add("hidden");
287    nowPlaying.albumCover.hidden = false;
288    nowPlaying.controller.classList.remove("hidden");
289});
290
291recentlyPlayed.entry1.addEventListener("click",_event=>{ loadPage("playlist"); });
292recentlyPlayed.entry2.addEventListener("click",_event=>{ loadPage("playlist"); });
293recentlyPlayed.entry3.addEventListener("click",_event=>{ loadPage("playlist"); });
294
295nav.goodTimeGuide.addEventListener("click",_event=>{ window.open("https://lostcoastoutpost.com/lowdown/"); });
296nav.swag.addEventListener("click",_event=>{ window.open("https://lccswag.myspreadshop.com/KHUM"); });
297nav.facebook.addEventListener("click",_event=>{ window.open("https://www.facebook.com/KHUMradio/"); });
298nav.instagram.addEventListener("click",_event=>{ window.open("https://www.instagram.com/khumradio/"); });
299nav.twitter.addEventListener("click",_event=>{ window.open("https://x.com/KHUMradio"); });
300
301footer.lccfamilyButton.addEventListener("click",_event=>{ dialogs.lccFamily.modal.showModal(); });
302footer.pawscauseButton.addEventListener("click",_event=>{ window.open("http://humboldtpawscause.com/"); });
303footer.publicfileButton.addEventListener("click",_event=>{ window.open("https://publicfiles.fcc.gov/fm-profile/khum"); });
304
305dialogs.lccFamily.closeButton.addEventListener("click",_event=>{ dialogs.lccFamily.modal.close(); });
306
307//NOW PLAYING SET-UP
308
309const eSource =  new EventSource("https://lccibackend.com/lcc_sse.php?station=2");
310eSource.addEventListener("message",_event=>{
311    document.dispatchEvent(new CustomEvent("newSong", { detail:JSON.parse(_event.data) }));
312});
313
314document.addEventListener("newSong", _event=>{
315    if(_event.detail.title != nowPlaying.currentSong.title){
316        if(!_event.detail.up){ _event.detail.up = 0; }
317        if(!_event.detail.down){ _event.detail.down = 0; }
318        _event.detail.time = _event.detail.play_date;
319
320        recentlyPlayed.recentSongs.unshift(nowPlaying.currentSong);
321        recentlyPlayed.recentSongs.pop();
322        nowPlaying.currentSong = _event.detail;
323
324        displayCurrentSong();
325        displayRecentlyPlayed();
326        nowPlaying.upVoteButton.disabled = false;
327        nowPlaying.downVoteButton.disabled = false;
328    }
329});
330
331let sliding = false;
332let volume = 50;
333nowPlaying.audio.volume = 0.5;
334
335nowPlaying.volumeslider.addEventListener("touchstart",_event=>{ sliding = true; });
336document.addEventListener("touchmove",_event=>{
337    if(sliding){
338        volume = (_event.touches[0].clientX - nowPlaying.volumeTrack.getBoundingClientRect().x) / nowPlaying.volumeTrack.getBoundingClientRect().width * 100;
339        if(volume > 100) { volume = 100; }
340        if(volume < 0) { volume = 0; }
341        nowPlaying.volumeslider.style.left = ((volume / 100) * nowPlaying.volumeTrack.getBoundingClientRect().width) + "px";
342        nowPlaying.audio.volume = volume/100;
343    }
344});
345document.addEventListener("touchend",_event=>{ sliding = false; });
346nowPlaying.volumeslider.addEventListener("mousedown",_event=>{ sliding = true; });
347document.addEventListener("mousemove",_event=>{
348    if(sliding){
349        volume = (_event.clientX - nowPlaying.volumeTrack.getBoundingClientRect().x) / nowPlaying.volumeTrack.getBoundingClientRect().width * 100;
350        if(volume > 100) { volume = 100; }
351        if(volume < 0) { volume = 0; }
352        nowPlaying.volumeslider.style.left = ((volume / 100) * nowPlaying.volumeTrack.getBoundingClientRect().width) + "px";
353        nowPlaying.audio.volume = volume/100;
354        if(_event.buttons == 0){ sliding = false; }
355    }
356});
357
358nowPlaying.upVoteButton.addEventListener("click",_event=>{
359    document.dispatchEvent(new CustomEvent("voteSent", { detail:{ songId:nowPlaying.currentSong.song_id, isUpVote:true } }));
360    nowPlaying.upVoteButton.disabled = true;
361    nowPlaying.downVoteButton.disabled = true;
362});
363
364nowPlaying.downVoteButton.addEventListener("click",_event=>{
365    document.dispatchEvent(new CustomEvent("voteSent", { detail:{ songId:nowPlaying.currentSong.song_id, isUpVote:false } }));
366    nowPlaying.upVoteButton.disabled = true;
367    nowPlaying.downVoteButton.disabled = true;
368});
369
370mobile.toggle.addEventListener("click",_event=>{
371    if(mobile.menu.classList.contains("open")){
372        mobile.menu.classList.remove("open");
373        mobile.toggle.textContent = "=";
374    } else {
375        mobile.menu.classList.add("open");
376        mobile.toggle.textContent = "X";
377    }
378});
379
380//SET-UP
381
382document.querySelectorAll('[data-pagekey]').forEach(_pageButton=>{
383    _pageButton.addEventListener("click",_event=>{
384        loadPage(_pageButton.dataset.pagekey);
385    });
386});
387
388footer.copyrightYear.textContent = (new Date).getFullYear();
389
390let startingPage = (window.location.search) ? window.location.search.substring(1).split("=")[1] : "home";
391
392getRecentlyPlayed();
393
394document.addEventListener('DOMContentLoaded', function(){
395    loadPage(startingPage);
396    if(screen.orientation.type == "portrait-primary" || screen.orientation.type == "portrait-secondary"){
397        mobile.nowPlayingWrapper.appendChild(nowPlaying.section);
398    }
399});

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.