From 2036d26cab4bbc4ee5492823557169a1c6d2f2fe Mon Sep 17 00:00:00 2001 From: "Mr. Snowy" <61592704+MrSn0wy@users.noreply.github.com> Date: Thu, 3 Aug 2023 22:19:55 +0200 Subject: [PATCH] compatibility list release The compatibility list is released! --- .gitignore | 1 + public_html/compatibility/app.js | 393 +++++++++++++++++++++++++- public_html/compatibility/index.html | 73 +++-- public_html/compatibility/styles.css | 249 +++++++++++++--- public_html/images/NA.avif | Bin 0 -> 998 bytes public_html/images/NA.jpg | Bin 0 -> 1839 bytes public_html/scripts/issue_fetcher.php | 4 +- public_html/scripts/search.php | 182 ++++++++++++ 8 files changed, 833 insertions(+), 69 deletions(-) create mode 100644 public_html/images/NA.avif create mode 100644 public_html/images/NA.jpg create mode 100644 public_html/scripts/search.php diff --git a/.gitignore b/.gitignore index 300b55e..de9a4ee 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ public_html/cgi-bin public_html/.htaccess public_html/ads.txt public_html/images/Thumbs.db +public_html/images/CUSA \ No newline at end of file diff --git a/public_html/compatibility/app.js b/public_html/compatibility/app.js index cd0afb4..6a779f9 100644 --- a/public_html/compatibility/app.js +++ b/public_html/compatibility/app.js @@ -1,15 +1,398 @@ -function adjustSizes() { +let imageLoading = true; +let avifSupport = false; +let sTimer; +let iTimer; +let pTimer; +let skeletonTimeout; +let tagFilter = []; +let oldestFilter = false; +let datesButton = false; +let lightTheme = false; +let pageNumber = 1; +let inputValue = 1; +let updatePageValue = false; +let noImagesLocked = false; + +function setCookie(name, value) { + var date = new Date(); + date.setMonth(date.getMonth() + 1); + var expires = "; expires=" + date.toUTCString(); + document.cookie = name + "=" + value + expires + "; path=/"; +} + +// Checks cookies +function checkCookies(name, defaultValue) { + return document.cookie.split("; ").find(c => c.startsWith(name + "="))?.split("=")[1] ?? defaultValue; // if cookie doesnt exist use the default value +} + +const imageLoadingCookie = checkCookies("imageLoading", "true"); +if (imageLoadingCookie === "false") { + imageLoading = false; + document.getElementById("imageButton").classList.add('selected'); +} + +const dateCookie = checkCookies("datesButton", "false"); +if (dateCookie === "true") { + datesButton = true; + + document.getElementById("datesButton").classList.add('selected'); +} + +const lightThemeCookie = checkCookies("lightTheme", "false"); +if (lightThemeCookie === "true") { + lightTheme = true; + document.body.classList.toggle("lightMode"); +} +//- Checks cookies + +function LightModeIconChange() { + if (lightTheme === true) { + document.getElementById("lightModeIcon").style.display = "flex"; + document.getElementById("darkModeIcon").style.display = "none"; + } else { + document.getElementById("darkModeIcon").style.opacity = "1"; + } +} + +function adjustFontSize() { var screenWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; + var referenceWidth = 2000; var referenceFontSize = 16; - var phoneReferenceWidth = 560; + var phoneReferenceWidth = 550; - if (screenWidth >= phoneReferenceWidth) { + if (screenWidth >= referenceWidth) { + // if its larger then 2000px + var fontSize = (screenWidth / referenceWidth) * referenceFontSize; + document.documentElement.style.fontSize = fontSize + 'px'; + } else if (screenWidth >= phoneReferenceWidth) { + // if its higer then 550 but under 2000 then set it as default document.documentElement.style.fontSize = referenceFontSize + 'px'; } else { + // if the width is lower then 550 var fontSize = (screenWidth / phoneReferenceWidth) * referenceFontSize; document.documentElement.style.fontSize = fontSize + 'px'; } } -window.addEventListener('DOMContentLoaded', adjustSizes); -window.addEventListener('resize', adjustSizes); \ No newline at end of file +// resize on load and when rezsizing the window +window.addEventListener('load', adjustFontSize); +window.addEventListener('resize', adjustFontSize); + +// Check Avif Support +console.log(`Hey there! I've implemented an avif support check to spare browsers like Edge from having a stroke :D!`); +const avif = new Image(); +avif.src = "data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A="; +avif.onload = function() { + console.log('AVIF IS SUPPORTED :D'); + avifSupport = true; +}; +avif.onerror = function() { + console.log('AVIF IS NOT SUPPORTED D:'); + avifSupport = false; + noImagesLocked = true; + imageButton(); +}; + +// Page Load +document.addEventListener('DOMContentLoaded', function() { + fetch('https://fpps4.net/scripts/search.php?q=&stats') + .then(response => response.text()) + .then(data => { + document.querySelector('#gameWrapper').innerHTML = data; + applyMods(); + gameStats(); + filter(); + }) + .catch(console.error); +}); + +// Game Stats +function gameStats() { + var labelData = []; + var tempPercentage = 0; + var tempTotal = 0; + var labels = ['N/A', 'Nothing', 'Boots', 'Menus', 'Ingame', 'Playable']; + console.log("\nCOMPATIBILITY STATS"); + + labels.forEach(label => { + var div = document.getElementById(label).getAttribute('data').split('+'); + var percentage = div[0]; + var total = div[1]; + labelData[label] = [percentage, total]; + console.log(label + ' = ' + percentage + '% ['+ total +']'); + }); + console.log("\n"); + + for (var label in labelData) { + var data = labelData[label]; + var percentage = data[0]; + var total = data[1]; + if (label === 'N/A' || label === 'Nothing') { //combine N/A & Nothing + tempPercentage = (parseFloat(percentage) + parseFloat(tempPercentage)); + tempTotal = (parseFloat(total) + parseFloat(tempTotal)); + percentage = tempPercentage.toFixed(2); + total = tempTotal.toFixed(0); + element = document.getElementById('NothingBar'); + text = document.getElementById('NothingInfo') + } else { + element = document.getElementById(label + 'Bar'); + text = document.getElementById(label + 'Info') + } + element.style.width = percentage + '%'; + text.textContent = percentage + '% - ' + total; + } +}; + +// Header Load +fetch('https://fpps4.net/parts/navbar.html') +.then(response => response.text()) +.then(data => { + document.querySelector('#header').innerHTML = data; + LightModeIconChange(); +}) +.catch(console.error); + +// Searching +document.querySelector('#search').addEventListener('input', function() { + updatePageValue = true; + pageNumber = 1; + UpdateSearchResults() +}); + +function UpdateSearchResults() { + clearTimeout(sTimer); + + const gameWrapper = document.querySelector('#gameWrapper'); + gameWrapper.querySelectorAll('.gameContainer').forEach(container => { + const skeletonDiv = document.createElement('div'); + skeletonDiv.classList.add('gameContainer', 'skeletonAnimation'); + gameWrapper.replaceChild(skeletonDiv, container); + }); + + sTimer = setTimeout(() => { + const searchQuery = document.querySelector('#search').value; + fetch('https://fpps4.net/scripts/search.php?q=' + searchQuery + '&tag=' + tagFilter + '&page=' + pageNumber + '&oldest=' + oldestFilter) + .then(response => response.text()) + .then(data => { + document.querySelector('#gameWrapper').innerHTML = data; + applyMods(); + }) + .catch(console.error); + }, 300); +} + +// Filters +function filter() { + var ids = ['Nothing', 'Boots', 'Menus', 'Ingame', 'Playable']; + + ids.forEach(id => { + element = document.getElementById(id + 'Bar'); + parent = element.parentElement; + parent.addEventListener('click', function() { + this.classList.toggle('selected'); + + tagFilter.includes(id) ? tagFilter.splice(tagFilter.indexOf(id), 1) : tagFilter.push(id); + console.log('Tag Filter has been updated to: ' + tagFilter); + updatePageValue = true; + pageNumber = 1; + UpdateSearchResults(); + }); + }); +}; + +// Image Handler +function imageHandler() { + document.querySelectorAll('.gameImage').forEach(gameImage => { + gameImage.setAttribute("loading", "lazy"); + const data = gameImage.dataset.cusa; + gameImage.src = data.includes('CUSA') && imageLoading ? (avifSupport ? "../images/CUSA/" + data + ".avif" : "../images/NA.jpg") : (avifSupport ? "../images/NA.avif" : "../images/NA.jpg"); + }); +} + +// Link Handler +function linkHandler() { + document.querySelectorAll('.gameImageLink').forEach(link => { + link.setAttribute("target", "_blank"); + link.href = "https://github.com/red-prig/fpps4-game-compatibility/issues/" + link.dataset.id; + link.removeAttribute('data-id'); + }); +} + +// Game Status Colors +function gameColors() { + document.querySelectorAll('.gameContainer').forEach(game => { + const status = game.querySelector('.gameStatus').textContent; + const statusClass = (['Nothing', 'Boots', 'Ingame', 'Menus', 'Playable'].includes(status) ? status : 'Nothing'); + game.querySelector('.gameStatus').classList.add(statusClass); + game.querySelector('.gameSeparator').classList.add(statusClass); + }); +} + +// Image Effect +function imageEffect() { + document.querySelectorAll('.gameImage').forEach(image => { + image.addEventListener('mousemove', e => { + const r = image.getBoundingClientRect(); + const x = e.clientX - r.left; + const y = e.clientY - r.top; + image.style.transformOrigin = `${x}px ${y}px`; + image.style.transform = 'scale(1.08)'; + }); + image.addEventListener('mouseleave', () => { + image.style.transform = 'scale(1)'; + }); + }); +} + +function applyMods() { + gameColors(); + imageHandler(); + linkHandler(); + imageEffect(); + dateHandler(); + updatePageSelector(); + updateFooter() +} + +// update footer position +function updateFooter() { + const viewportHeight = window.innerHeight; + const bodyHeight = document.body.clientHeight; + if (bodyHeight < viewportHeight) { + document.getElementById('footer').style.position = 'fixed'; + } else { + document.getElementById('footer').style = ''; + } +} + +// NoImage button +function imageButton() { + e = document.getElementById('imageButton'); + if (noImagesLocked === true) { + e.classList.add('selected'); + e.style.cursor = 'default'; + } else { + clearTimeout(iTimer); + e.classList.toggle('selected'); + + imageLoading = !imageLoading; + setCookie("imageLoading", imageLoading); + iTimer = setTimeout(imageHandler, 450); + } +} + +// show last updated dates button +function dateButton() { + datesButton = !datesButton; + setCookie("datesButton", datesButton); + dateHandler(); +} + +function dateHandler() { + var gameCusaText = document.querySelectorAll(".gameCusa"); + var button = document.getElementById('datesButton'); + datesButton ? button.classList.add('selected') : button.classList.remove('selected'); + + gameCusaText.forEach(element => { + var currentText = element.textContent; + var date = element.getAttribute('data'); + + if (datesButton === true) { + element.dataset.status = currentText; + element.textContent = date; + } else if (currentText === date) { + element.textContent = element.dataset.status; + } + }); +} + +// Sorting Button +function sortButton(e) { + clearTimeout(iTimer); + oldestFilter = !oldestFilter; + e.classList.toggle('selected'); + iTimer = setTimeout(UpdateSearchResults, 450); +} + +function toggleLightMode() { + lightTheme = !lightTheme; + setCookie("lightTheme", lightTheme); + document.body.classList.toggle("lightMode"); + var x = document.getElementById("darkModeIcon") + var y = document.getElementById("lightModeIcon") + if (y.style.display === "none" || y.style.display == '') { + y.style.display = "flex"; + x.style.display = "none"; + } else { + x.style.opacity = "1"; + x.style.display = "flex"; + y.style.display = "none"; + }; +}; + +function toggleMenu() { + // var x = document.getElementById("overlay"); + var y = document.getElementById("close-icon") + var z = document.getElementById("menu-icon") + console.log('pretend like you see a menu') + if (y.style.display === "none" || y.style.display == '') { + // x.style.display = "flex"; + y.style.display = "flex"; + z.style.display = "none"; + setTimeout(function() { + // x.style.opacity = 1; + }, 50); + } else { + // x.style.opacity = 0; + y.style.display = "none"; + z.style.display = "flex"; + setTimeout(function() { + // x.style.display = "none"; + }, 300); + }; +}; + +function updatePageSelector() { + const inputElement = document.getElementById('search2'); + let maxValue = document.getElementById("totalPages").getAttribute('data'); + if (updatePageValue === true) { + inputValue = 1; + updatePageValue = false; + } + + inputElement.placeholder = inputValue + "/" + maxValue; + + inputElement.addEventListener('input', function(event) { + clearTimeout(pTimer); + inputValue = parseInt(inputElement.value, 10); + + if (isNaN(parseFloat(inputValue))) { + inputValue = 1; + } + if (inputValue > maxValue) { + inputValue = maxValue; + } else if (inputValue < 1) { + inputValue = 1; + } + + inputElement.placeholder = inputValue + "/" + maxValue; + pTimer = setTimeout(() => { + if (inputValue > 9) { + inputElement.style.padding = "0 0.3rem 0 0.7rem"; + } else if (inputValue < 10) { + inputElement.style.padding = ""; + } + inputElement.value = ""; + pageNumber = inputValue; + UpdateSearchResults(); + }, 500); + }); +} + +// Footer Load +fetch('https://fpps4.net/parts/footer.html') +.then(response => response.text()) +.then(data => { + document.querySelector('#footer').innerHTML = data; +}) +.catch(console.error); \ No newline at end of file diff --git a/public_html/compatibility/index.html b/public_html/compatibility/index.html index f222a2c..5644a98 100644 --- a/public_html/compatibility/index.html +++ b/public_html/compatibility/index.html @@ -3,7 +3,7 @@ - + fpPS4 - Compatibility List @@ -13,43 +13,56 @@ - + + -
-

Game Compatibility List

-

Soon

-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
+ +
+

Game Compatibility List

+

These are the games that have been tested with fpPS4.
Click on an image to view the game's GitHub issue.
The database is updated every 20 minutes.

+
+
Playable
+
+
Menus
+
Ingame
-
-
-
-
-
-
-
+
+
Nothing
+
Boots
+
+
+
+ + +
+ +
+
Oldest
+
No Images
+
Dates
+
+
+
+
+
+
+
+
+
- +
Trademarks and logos displayed on this website are the property of their respective owners.
The use of any third-party trademarks, brand names, product names, and logos is for
informational purposes only and does not imply endorsement or sponsorship.
+
+
+
+ \ No newline at end of file diff --git a/public_html/compatibility/styles.css b/public_html/compatibility/styles.css index eae7299..ec884ff 100644 --- a/public_html/compatibility/styles.css +++ b/public_html/compatibility/styles.css @@ -2,33 +2,59 @@ /*+ scrollbar */ ::-webkit-scrollbar { - width: 9px;} + width: 0.57rem;} ::-webkit-scrollbar-thumb { - background: var(--main0); - border-radius: 2px;} + background: var(--hover); + border-radius: 0.13rem;} ::-webkit-scrollbar-thumb:hover { - background: var(--main1); } + background: var(--selected); } /*- scrollbar */ -.skeletonAnimation { - background-image: linear-gradient(90deg, var(--border), var(--main1), var(--border)) !important; - background-size: 200% 100% !important; - animation: skeletonLoading 2s ease-in-out infinite; -} -@keyframes skeletonLoading { - 0% {background-position: -100% 0;} - 100% {background-position: 100% 0;} -} :root { --text: #eceff4; + --whiteText: #eceff4; --border: #e1e1e10d; --solidBorder: #393b3e; --background: #101112; --main0: #26272a; --main1: #2f3134; --hover: #393b3e; + --selected: #424448; +} + +.lightMode { + --text: #2E3440; + --border: #b2b8c770; + --solidBorder: #b2b8c7; + --background: #ECEFF4; + --main0: #E5E9F0; + --main1: #D8DEE9; + --hover: #c6ccd9; + --selected: #b0b7c7; +} +.lightMode .lightmodeText{color: #eceff4} + +/* .lightmode ::selection{background-color: #3864d4} +::selection { + background-color: yellow; +} */ + +.Nothing {--status-color: #1F2325;} +.Boots {--status-color: #F2766E;} +.Menus {--status-color: #4288B7;} +.Ingame {--status-color: #fabb44;} +.Playable {--status-color: #54A396;} + +.skeletonAnimation { + background-image: linear-gradient(90deg, var(--solidBorder), var(--main1), var(--solidBorder)) !important; + background-size: 200% 100% !important; + animation: skeletonLoading 2s ease-in-out infinite; +} +@keyframes skeletonLoading { + 0% {background-position: -100% 0;} + 100% {background-position: 100% 0;} } * { @@ -39,6 +65,7 @@ color: var(--text); text-decoration: none; font-weight: 400; + -webkit-tap-highlight-color: transparent; } html { @@ -47,22 +74,22 @@ html { body{ background: var(--background); - overflow-x: hidden; - display: flex; flex-direction: column; align-items: center; overflow-y: scroll; + overflow-x: hidden; + display: flex; } .main { display: flex; flex-direction: column; - position: absolute; + position: relative; align-items: center; box-shadow: 0 0.3rem 0.7rem 0 rgba(0, 0, 0, 0.5); background: var(--main0); border-radius: 6px; - margin-top: 90px; + margin-top: 6rem; padding: 2rem 0 0; width: 40rem; border: 0.2rem solid var(--main0); @@ -71,7 +98,7 @@ body{ .mainText {font-weight: 500;} .smolMainText { - width: 600px; + width: 37.5rem; padding: 4% 2% 3%; text-align: center; font-size: 1.1rem; @@ -85,8 +112,8 @@ body{ gap: 1rem; } -.progressWrap { - border: 0.05rem solid var(--border); +.progressWrap {border: 0.05rem solid var(--border);} +.progressWrap, .progressWrapb { background: var(--main1); position: relative; overflow: hidden; @@ -101,21 +128,82 @@ body{ cursor: pointer; user-select: none; } -.progressWrap:hover {background: var(--hover);} +.progressWrap:hover, .progressWrapb:hover {background: var(--hover);} + +.progressWrapb::after { /*fix black progressbar*/ + content: ''; + position: absolute; + width: 100%; + height: 100%; + box-sizing: border-box; + border: 0.05rem solid var(--border); + border-radius: 0.5rem; + z-index: 4; +} + +.progressBar { + position: absolute; + height: 100%; + width: 0%; + border-radius: 0.3rem; + transition: width 0.5s ease-in-out; +} + +.progressBarText { + position: absolute; + font-weight: 500; + left: 1rem; + z-index: 3; +} + +.progressBarInfo { + position: absolute; + z-index: 4; + right: 0.5rem; + font-size: 0.9rem; + transition: opacity 0.15s ease-in-out; +} +.selected, .selected:hover {background: var(--selected) !important;} /*- PROGRESS BAR*/ -.searchBar { + +.searchBarContainer {display: flex;} +.searchBar, +.pageSelector { padding-left: 1rem; - width: 19.4rem; + /* width: 19.4rem; */ height: 2.15rem; margin: 1.9rem 0 0.3rem; background: var(--main1); border: 0.05rem solid var(--border); - border-radius: 0.6rem; + /* border-radius: 0.6rem; */ + line-height: 0; + font-size: 0.9rem; } -.searchBar:focus {outline: none; background: var(--hover); transition: background 0.1s ease-in-out;} -.searchBar::placeholder {color: #8C8C8C; font-weight: 500; font-size: 0.8rem;} -.searchBar:hover {background-color: var(--hover);} + +.searchBar { + width: 14.3rem; + border-radius: 0.6rem 0rem 0rem 0.6rem; +} + +.pageSelector { + width: 3rem; + border-radius: 0rem 0.6rem 0.6rem 0rem; + font-weight: 500; + -moz-appearance: textfield; + appearance: textfield; +} + +.pageSelector::-webkit-inner-spin-button, +.pageSelector::-webkit-outer-spin-button { + -webkit-appearance: none; +} + +.searchBar:focus, +.pageSelector:focus {outline: none; background: var(--hover); transition: background 0.1s ease-in-out;} +.searchBar::placeholder {color: #8C8C8C; font-weight: 500;} +.pageSelector::placeholder {color: var(--text);} +.searchBar:hover, .pageSelector:hover {background-color: var(--hover);} .optionButtons { display: flex; @@ -138,7 +226,20 @@ body{ } .optionButton:hover {background: var(--hover);} +.noResultsText { + text-align: center; + padding: 2rem 0 0rem; + font-weight: 500; +} + +.noResultsEmoji { + text-align: center; + font-size: 2.5rem; + padding: 2rem 0 3rem; +} + /*+ GAME CARDS*/ + .gameWrapper {width: 95%;} .gameContainer { @@ -168,17 +269,101 @@ body{ z-index: 2; } .gameContainer:hover {background: var(--hover);} + +.gameImageLink { + border-radius: 0.6rem 0.4rem 0.4rem 0.6rem; + overflow: hidden; +} + +.gameImage { + width: 5.3rem; + height: 5.3rem; + max-width: 5.3rem; + border-radius: 0.5rem 0.4rem 0.4rem 0.5rem; + transition: transform 0.2s ease-in-out; +} + +.gameSeparator { + height: 3.9rem; + margin: 0.7rem 1.25rem; + border-right: 0.375rem solid var(--status-color); + border-radius: 0.25rem; +} + +.gameDetails { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; +} + +.gameName { + font-weight: 500; + font-size: 1.125rem; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + transition: width 0.4s ease-out; + width: 22rem; +} +.gameName:hover {width: 29.3rem;} + +.gameCusa { + position: absolute; + right: 0.94rem; + padding: 0.25rem 0.38rem; + font-weight: 500; + font-size: 0.87rem; +} + +.gameStatus { + width: 5.7rem; + height: 1.5rem; + margin-top: 0.94rem; + background-color: var(--status-color); + border-radius: 0.25rem; + text-align: center; + line-height: 1.5rem; + user-select: none; + color: var(--whiteText); +} /*- GAME CARDS*/ -@media screen and (max-width: 750px) { +.totalTimeText { + text-align: center; + font-size: 1rem; + padding: 0 0 0.7rem; +} + +@media screen and (max-width: 700px) { .main { - width: 35rem; - border-radius: 1rem; + width: 34rem; + border-radius: 0.8rem; } .gameName { - width: 18.5rem; + width: 17.5rem; } .gameName:hover { - width: 24rem; + width: 23rem; } + + .smolMainText { + width: 30rem; + } + .footerText { + font-size: 0.89rem !important; + } +} +@media screen and (max-width: 550px) { + .optionButton:hover {background: var(--main1);} + .progressWrap:hover, .progressWrapb:hover {background: var(--main1);} + .selected .progressBarInfo{opacity: 1;} + .menu-icon:hover, .gh-logo:hover, .dc-logo:hover, .lightModeButton:hover, .logo:hover {background-color: #00000000 !important}; + .gameDetails:hover .gameName {width: 23rem !important;} + .footerSeparator {display: flex !important;} + .footerContent:hover {background-color: #00000000 !important;} +} + +@media screen and (min-width: 550px) { + .progressWrap:hover .progressBarInfo, .progressWrapb:hover .progressBarInfo{opacity: 1;} } \ No newline at end of file diff --git a/public_html/images/NA.avif b/public_html/images/NA.avif new file mode 100644 index 0000000000000000000000000000000000000000..73ef871626150d1688fee268ca78dfd2f3e308d5 GIT binary patch literal 998 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 L(Lgp~|NkZc^&bh~ literal 0 HcmV?d00001 diff --git a/public_html/scripts/issue_fetcher.php b/public_html/scripts/issue_fetcher.php index 1097f00..b644926 100644 --- a/public_html/scripts/issue_fetcher.php +++ b/public_html/scripts/issue_fetcher.php @@ -173,7 +173,7 @@ function get_image($cusaCode, $conn, $serverUsername) { $httpsIconUrl = str_replace('http://', 'https://', $iconUrl); $imagick = new Imagick(); - $avifIconURL = "/home/{$serverUsername}/domains/fpps4.net/public_html/beta/images/CUSA/{$cusaCode}.avif"; + $avifIconURL = "/home/{$serverUsername}/domains/fpps4.net/public_html/images/CUSA/{$cusaCode}.avif"; $result = null; if ($imagick->readImage($httpsIconUrl)) { $imageFormat = $imagick->getImageFormat(); @@ -226,7 +226,7 @@ for ($page = 1; $page <= $total_pages; $page++) { foreach ($issues_array as $issue) { $title = $issue['title']; $cusaCode = extract_cusaCode($title); - $avifIconURL = "/home/{$serverUsername}/domains/fpps4.net/public_html/beta/images/CUSA/{$cusaCode}.avif"; + $avifIconURL = "/home/{$serverUsername}/domains/fpps4.net/public_html/images/CUSA/{$cusaCode}.avif"; // Check if cusa code is empty if ($cusaCode) { diff --git a/public_html/scripts/search.php b/public_html/scripts/search.php new file mode 100644 index 0000000..40c206c --- /dev/null +++ b/public_html/scripts/search.php @@ -0,0 +1,182 @@ +"; + } + + +/// processing user input + $searchQuery = isset($_GET['q']) ? $_GET['q'] : ''; + $tags = isset($_GET['tag']) ? $_GET['tag'] : ''; + $page = isset($_GET['page']) ? $_GET['page'] : 1; + $oldest = isset($_GET['oldest']) ? $_GET['oldest'] : false; + $giveStats = isset($_GET['stats']) ? true : false; + + $searchQuery = filter_var($searchQuery, FILTER_SANITIZE_STRING); + $tags = filter_var($tags, FILTER_SANITIZE_STRING); + $page = filter_var($page, FILTER_VALIDATE_INT); + $oldest = filter_var($oldest, FILTER_VALIDATE_BOOLEAN); + $giveStats = filter_var($giveStats, FILTER_VALIDATE_BOOLEAN); + + +/// defining stuff lmao + $maxResults = (!empty($searchQuery)) ? 10 : 20; + $pageNumber = ($page - 1) * $maxResults; + $sqlQuery = "SELECT * FROM issues"; + $params = array(); + $conditions = array(); + + +/// if there is an search query in the request + if (!empty($searchQuery)) { + $searchQuery = strtolower($searchQuery); + $conditions[] = strpos($searchQuery, 'cusa') ? "cusaCode LIKE :searchQuery" : "title LIKE :searchQuery"; + $params[':searchQuery'] = $searchQuery . '%'; + } + + +/// if there are tags in the request + if (!empty($tags)) { + $tagFilters = explode(',', $tags); + $tagConditions = []; + $tagParams = []; + + foreach ($tagFilters as $index => $tag) { + $tagParam = ":tagFilter{$index}"; + $tagConditions[] = "(CONCAT(',', tags, ',') LIKE CONCAT('%,', {$tagParam}, ',%'))"; + $tagParams[$tagParam] = "{$tag}"; + } + + if (!empty($tagConditions)) { + $conditions[] = "(" . implode(" OR ", $tagConditions) . ")"; + $params = array_merge($params, $tagParams); + } + } + + +/// im too lazy to write a comment + if (!empty($conditions)) { + $sqlQuery .= " WHERE " . implode(" AND ", $conditions); + } + + +/// gets the total amount of issues based on the search query + $sqlQueryForTotal = "SELECT COUNT(*) AS total FROM ($sqlQuery) AS totalIssues"; + $stmtTotal = $conn->prepare($sqlQueryForTotal); + + foreach ($params as $param => &$value) { + $stmtTotal->bindParam($param, $value, PDO::PARAM_STR); + } + + $stmtTotal->execute(); + $totalIssuesAmount = $stmtTotal->fetch(PDO::FETCH_ASSOC)['total']; + $totalPages = ceil($totalIssuesAmount / $maxResults); + echo "
"; + if ($totalIssuesAmount < 1) { + echo "

No results found based on your query

"; + echo "

¯\_(ツ)_/¯

"; + } + +/// forming and executing sql query + $sqlQuery .= " ORDER BY id " . ($oldest ? "ASC" : "DESC"); + $sqlQuery .= " LIMIT :offset, :limit"; + $params[':offset'] = $pageNumber; + $params[':limit'] = $maxResults; + + $stmt = $conn->prepare($sqlQuery); + + foreach ($params as $param => &$value) { + if (is_int($value)) { + $stmt->bindParam($param, $value, PDO::PARAM_INT); + } else { + $stmt->bindParam($param, $value, PDO::PARAM_STR); + } + } + + $stmt->execute(); + $result = $stmt->fetchAll(PDO::FETCH_ASSOC); + + +/// Outputing results + foreach ($result as $row) { + $id = $row['id']; + $cusaCode = $row['cusaCode']; + $clean_title = $row['title']; + $tags = $row['tags']; + $updatedAt = $row['updatedDate']; + + $dataCusa = ''; + $avifIconURL = "../images/CUSA/{$cusaCode}.avif"; + if (!file_exists($avifIconURL)) { + $dataCusa = null; // Skip image + } else { + $dataCusa = $cusaCode; + } + + echo "
"; + echo ""; + echo "$clean_title"; + echo ""; + echo "
"; + echo "
"; + echo "

$clean_title

"; + echo "

$cusaCode

"; + echo "

$tags

"; + echo "
"; + echo "
"; + } + + +/// stats on the compatibility list + if ($giveStats === true) { + $availableTags = ['N/A', 'Nothing', 'Boots', 'Menus', 'Ingame', 'Playable']; + $tagPercentages = []; + + $stmt = $conn->prepare("SELECT COUNT(*) AS total FROM issues"); + $stmt->execute(); + $total = $stmt->fetch(PDO::FETCH_ASSOC)['total']; + + foreach ($availableTags as $tag) { + $stmt = $conn->prepare("SELECT COUNT(*) AS count FROM issues WHERE tags = :tag"); + $stmt->bindParam(':tag', $tag, PDO::PARAM_STR); + $stmt->execute(); + + $count = $stmt->fetch(PDO::FETCH_ASSOC)['count']; + $percentage = ($total > 0) ? ($count / $total) * 100 : 0; + $tagPercentages[$tag] = $percentage; + $tagCount[$tag] = $count; + } + + $amount = (100 - array_sum($tagPercentages)) / count($tagPercentages); + + foreach ($tagPercentages as $tag => $percentage) { + $percentage += $amount; + $percentage = round($percentage, 2); + echo "
"; + } + } + + +/// end + $conn = null; //exit connection + $endTime = microtime(true); + $executionTime = round(($endTime - $startTime) * 1000, 2); // Convert to milliseconds + echo "

$totalIssuesAmount results in {$executionTime}ms

"; +?> \ No newline at end of file -- 2.51.2