The official website for the open-source compatibility layer fpPS4
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 402 lines 12 kB view raw
1// this is my attempt on rewriting this... thing 2let avifSupport = false; 3let imageLoading = true; 4let oldestFilter = false; 5let datesButton = false; 6let statusFilter = []; 7let currentPage = 1; 8 9let issuesPerPage = 20; 10const codeRegex = /[a-zA-Z]{4}[0-9]{5}/; 11 12let Timer; 13let totalPages; 14let totalIssues; 15let fancyJsonData; // :3 16 17// Check Avif Support 18console.log(`Hey there! I've implemented an avif support check to spare browsers like Edge from having a stroke :D!`); 19const avif = new Image(); 20avif.src = "data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A="; 21avif.onload = function() { 22 console.log('AVIF IS SUPPORTED :D'); 23 avifSupport = true; 24}; 25avif.onerror = function() { 26 console.log('AVIF IS NOT SUPPORTED D:'); 27 avifSupport = false; 28 // window.alert("Hey! Your browser doesn't support avif, avif is an image format that has low file sizes while having high quality images. You won't get any game images"); 29 const iButton = document.getElementById('imageButton'); 30 console.log(iButton); 31 iButton.classList.add('selected'); 32 iButton.style.cursor = 'default'; 33 iButton.removeAttribute("onclick"); 34}; 35 36 37 38// game cards and stats handler and other on-load stuff 39document.addEventListener('DOMContentLoaded', async function () { 40 await init() 41 42 // + check cookies 43 const imageLoadingCookie = checkCookies("imageLoadingSetting", "true"); 44 const datesSetting = checkCookies("datesSetting", "false"); 45 46 if (imageLoadingCookie === "false") { 47 imageLoading = false; 48 document.getElementById("imageButton").classList.add('selected'); 49 } 50 51 if (datesSetting === "true") { 52 datesButton = true; 53 document.getElementById('datesButton').classList.toggle('selected'); 54 } 55 56 // Adjust screen size for mobile and 4k monitors for some reason 57 // window.addEventListener('load', adjustScreenSize); 58 window.addEventListener('resize', adjustScreenSize); 59 60 61 // + fetch issues and set the tag bars 62 fetch('https://api.fpps4.net/database.json') 63 .then(response => response.json()) 64 .then(jsonData => { 65 fancyJsonData = jsonData; 66 67 totalIssues = jsonData.length; 68 totalPages = Math.ceil(totalIssues / issuesPerPage); 69 70 console.log("\nCOMPATIBILITY STATS"); 71 72 let availableStatus = ['Nothing', 'Boots', 'Menus', 'Ingame', 'Playable']; 73 let totalPercentage = 0; 74 75 let statusPercentages = []; 76 let statusCount = []; 77 78 availableStatus.forEach(status => { 79 statusCount[status] = 0; // init tag count 80 81 jsonData.forEach(issue => { 82 if (issue.status === status) { 83 statusCount[status]++; 84 } 85 }); 86 87 let rawPercentage = parseFloat((statusCount[status] / totalIssues * 100).toFixed(2)); 88 89 statusPercentages[status] = rawPercentage; 90 totalPercentage += rawPercentage; 91 }); 92 93 // stats & tag filter 94 availableStatus.forEach(status => { 95 let percent = statusPercentages[status]; 96 let count = statusCount[status]; 97 let element = document.getElementById(status + 'Bar'); 98 let textElement = document.getElementById(status + 'Info') 99 let parentElement = element.parentElement; 100 console.log(`${status} = ${percent}% [${count}]`); 101 element.style.width = percent + '%'; 102 textElement.textContent = percent + '% - ' + count; 103 104 parentElement.addEventListener('click', function () { 105 parentElement.classList.toggle('selected'); 106 statusFilter.includes(status) ? statusFilter.splice(statusFilter.indexOf(status), 1) : statusFilter.push(status); 107 currentPage = 1; 108 updateSearchResults(); 109 }); 110 }); 111 console.log("\n"); 112 113 gameCardHandler(jsonData.slice(0, issuesPerPage)); //first 20 114 pageButtonHandler(); 115 116 }) 117 .catch(console.error); 118}); 119 120 121// Searching 122document.querySelector('#search').addEventListener('input', function() { 123 currentPage = 1; 124 updateSearchResults(); 125}); 126 127function updateSearchResults() { 128 clearTimeout(Timer); 129 const gameWrapper = document.querySelector('#gameWrapper'); 130 const searchQuery = document.querySelector('#search').value.toLowerCase(); 131 132 gameWrapper.querySelectorAll('.gameContainer').forEach(container => { 133 const skeletonDiv = document.createElement('div'); 134 skeletonDiv.classList.add('gameContainer', 'skeletonAnimation'); 135 gameWrapper.replaceChild(skeletonDiv, container); 136 }); 137 138 Timer = setTimeout(() => { 139 let jsonData = []; 140 let isCodeSearch = codeRegex.test(searchQuery); 141 142 fancyJsonData.forEach(issue => { 143 // id based searching 144 if (isCodeSearch && (issue.code.toLowerCase() !== searchQuery)) { 145 return; 146 147 // title based searching 148 } else if (!isCodeSearch && !issue.title.toLowerCase().includes(searchQuery)) { 149 return; 150 } 151 152 // filter tags 153 if (statusFilter.length > 0) { 154 let isGood = false; 155 156 for (const status of statusFilter) { 157 if (status === issue.status) { 158 isGood = true; 159 break; 160 } 161 } 162 163 if (isGood === false) { 164 return; 165 } 166 } 167 168 jsonData.push(issue); 169 }); 170 171 172 let startSlice = (currentPage - 1) * issuesPerPage; // makes it start on 0 173 let endSlice = startSlice + issuesPerPage; 174 175 totalPages = Math.ceil(jsonData.length / issuesPerPage); 176 totalIssues = jsonData.length; 177 178 let tempJsonData; 179 180 if (oldestFilter === true) { 181 tempJsonData = jsonData.reverse(); 182 } else { 183 tempJsonData = jsonData; 184 } 185 186 gameCardHandler(tempJsonData.slice(startSlice, endSlice)); 187 pageButtonHandler(); 188 189 }, 300); 190} 191 192// Game Card handler 193function gameCardHandler(jsonData) { 194 const gameWrapper = document.getElementById("gameWrapper"); 195 gameWrapper.innerHTML = ""; 196 197 jsonData.forEach(issue => { 198 // game image URL 199 let imageSource = ""; 200 let imageText = "GAME"; 201 let imageTextSize = 1.25; 202 203 switch(true) { 204 case issue.image && imageLoading && avifSupport && issue.issue_type === "Homebrew": 205 imageSource = "https://api.fpps4.net/images/homebrew/" + issue.title + ".avif"; 206 break; 207 case issue.image && imageLoading && avifSupport: 208 imageSource = "https://api.fpps4.net/images/game/" + issue.code +".avif"; 209 break; 210 case issue.issue_type === "SystemFwUnknown" || issue.issue_type === "SystemFw505": 211 imageText = "SYSTEM"; 212 imageTextSize = 1.13; 213 break 214 } 215 216 if (issue.issue_type === "Homebrew") { // needs to be applied to all homebrews 217 imageText = "HOME<br>BREW"; 218 imageTextSize = 1.25; 219 if (issue.code === "") { 220 issue.code = "HOMEBREW"; 221 } 222 } 223 224 let imageTextEnabled = issue.image && imageLoading && avifSupport ? "none" : "flex"; 225 let updated = new Date(issue.updated).toLocaleDateString() 226 227 228 // game cards 229 const gameElementHTML = ` 230 <div class="gameContainer"> 231 <a class="gameImageLink" target="_blank" href="https://github.com/red-prig/fpps4-game-compatibility/issues/${issue.id}"> 232 <p class="gameImageText" style="font-size: ${imageTextSize}rem; display: ${imageTextEnabled};">${imageText}</p> 233 ${imageSource ? `<img class="gameImage" loading="lazy" alt="${issue.title} - ${issue.code} game image" src="${imageSource}">` : "" } 234 </a> 235 <div class="gameSeparator ${issue.status}"></div> 236 <div class="gameDetails"> 237 <p class="gameName">${issue.title}</p> 238 <p class="gameCusa" data-date="${updated}" data-cusa="${issue.code}">${issue.code}</p> 239 <p class="gameStatus ${issue.status}">${issue.status}</p> 240 </div> 241 </div>`; 242 243 const tempContainer = document.createElement('div'); 244 tempContainer.innerHTML = gameElementHTML; 245 const gameContainer = tempContainer.querySelector('.gameContainer'); 246 gameWrapper.appendChild(gameContainer); 247 }); 248 249 const statContainer = document.createElement('h4'); 250 statContainer.innerHTML = `<h4 class="totalTimeText">${totalIssues} results </h4>`; 251 gameWrapper.appendChild(statContainer); 252 253 if (datesButton) { 254 dateButtonHandler(); 255 } 256 257 // Image Effect 258 document.querySelectorAll('.gameImage, .gameImageText').forEach(image => { 259 image.addEventListener('mousemove', e => { 260 const r = image.getBoundingClientRect(); 261 const x = e.clientX - r.left; 262 const y = e.clientY - r.top; 263 image.style.transformOrigin = `${x}px ${y}px`; 264 image.style.transform = 'scale(1.08)'; 265 }); 266 image.addEventListener('mouseleave', () => { 267 image.style.transform = 'scale(1)'; 268 }); 269 }); 270 271 // functions in required.js 272 updateFooter(); 273} 274 275 276// NoImage button 277function imageButton(button) { 278 imageLoading = !imageLoading; 279 setCookie("imageLoadingSetting", imageLoading); 280 button.classList.toggle('selected'); 281 imageButtonHandler(); 282} 283 284 285// Date Button 286function dateButton(button) { 287 datesButton = !datesButton; 288 setCookie("datesSetting", datesButton); 289 button.classList.toggle('selected'); 290 dateButtonHandler(); 291} 292 293 294// Oldes/Newest Button 295function sortButton(button) { 296 oldestFilter = !oldestFilter; 297 button.classList.toggle('selected'); 298 updateSearchResults(); 299} 300 301 302function imageButtonHandler() { 303 if (imageLoading === false) { 304 document.querySelectorAll(".gameImageText").forEach(imageText => { 305 imageText.style.display = "flex"; 306 }); 307 } else { 308 updateSearchResults(); 309 } 310} 311 312 313function dateButtonHandler() { 314 document.querySelectorAll(".gameCusa").forEach(element => { 315 if (datesButton === true) { 316 element.textContent = element.dataset.date; 317 } else { 318 element.textContent = element.dataset.cusa; 319 } 320 }); 321} 322 323function pageButtonHandler(type) { 324 // maybe make it handle everything in once? 325 const maxNumber = totalPages; 326 const searchBar = document.getElementById("search3"); 327 const minButton = document.getElementById("pageBarMin"); 328 const maxButton = document.getElementById("pageBarMax"); 329 330 maxButton.textContent = maxNumber; 331 332 if (currentPage != maxNumber) { 333 if (type === "forward") { 334 currentPage += 1; 335 updateSearchResults(); 336 } else if (type === "max") { 337 currentPage = maxNumber; 338 updateSearchResults(); 339 } 340 } 341 342 if (currentPage != 1) { 343 if (type === "back") { 344 currentPage -= 1; 345 updateSearchResults(); 346 } else if (type === "min") { 347 currentPage = 1; 348 updateSearchResults(); 349 } 350 } 351 352 if (type === "input") { 353 clearTimeout(Timer); 354 355 Timer = setTimeout(() => { 356 inputValue = parseInt(searchBar.value); 357 console.log(inputValue); 358 359 if (inputValue > maxNumber) { 360 inputValue = maxNumber; 361 } else if (inputValue < 1) { 362 inputValue = 1; 363 } 364 365 currentPage = inputValue; 366 searchBar.placeholder = currentPage; 367 searchBar.value = ""; 368 updateSearchResults(); 369 }, 400); 370 } 371 372 373 374 if (currentPage !== 1 && currentPage !== maxNumber) { 375 searchBar.placeholder = currentPage; 376 searchBar.classList.add("selected"); 377 maxButton.classList.remove("selected"); 378 minButton.classList.remove("selected"); 379 } else if (currentPage === 1) { 380 minButton.classList.add("selected"); 381 maxButton.classList.remove("selected"); 382 searchBar.classList.remove("selected"); 383 searchBar.placeholder = "..."; 384 } else if (currentPage === maxNumber) { 385 minButton.classList.remove("selected"); 386 maxButton.classList.add("selected"); 387 searchBar.classList.remove("selected"); 388 searchBar.placeholder = "..."; 389 } 390} 391 392search3.addEventListener("click", function() { 393 if (search3.placeholder == '...') { 394 search3.placeholder = ''; 395 } 396}); 397 398search3.addEventListener("blur", function() { 399 if (search3.placeholder == '') { 400 search3.placeholder = '...'; 401 } 402});