older-blog-posts #56
39
dist/api/blog/blogData.php
vendored
39
dist/api/blog/blogData.php
vendored
@ -673,9 +673,17 @@ EOD;
|
||||
*/
|
||||
public function changeHTMLSrc(string $body, string $to, string $from): string
|
||||
{
|
||||
// $body = preg_replace_callback('/>([^<]+)</', function($matches) {
|
||||
// // Convert special characters to HTML entities
|
||||
// return '>' . htmlentities(trim($matches[1]), ENT_QUOTES | ENT_HTML5, 'UTF-8') . '<';
|
||||
// }, $body);
|
||||
|
||||
$htmlDoc = new DOMDocument();
|
||||
$body = mb_convert_encoding($body, "HTML-ENTITIES", "UTF-8");
|
||||
$htmlDoc->loadHTML($body, LIBXML_NOERROR);
|
||||
|
||||
// Load the raw HTML content into DOMDocument
|
||||
@$htmlDoc->loadHTML($body, LIBXML_NOERROR);
|
||||
|
||||
// Get the body and process images
|
||||
$doc = $htmlDoc->getElementsByTagName('body')->item(0);
|
||||
$imgs = $doc->getElementsByTagName('img');
|
||||
|
||||
@ -688,9 +696,11 @@ EOD;
|
||||
$srcList[] = $src;
|
||||
$fileName = basename($src);
|
||||
|
||||
// Update the src attribute to the new location
|
||||
$img->setAttribute("src", substr($to, 2) . $fileName);
|
||||
}
|
||||
|
||||
// Rename files and clean up old ones
|
||||
$files = scandir($from);
|
||||
foreach ($files as $file)
|
||||
{
|
||||
@ -706,15 +716,36 @@ EOD;
|
||||
}
|
||||
}
|
||||
|
||||
// Process the HTML content for output
|
||||
$newBody = '';
|
||||
foreach ($doc->childNodes as $node)
|
||||
{
|
||||
$newHTML = $htmlDoc->saveHTML($node);
|
||||
$newBody .= mb_convert_encoding($newHTML, "UTF-8", mb_detect_encoding($newHTML));
|
||||
// Only convert text nodes to HTML entities
|
||||
if ($node->nodeType === XML_TEXT_NODE)
|
||||
{
|
||||
$newBody .= $this->convertToHtmlEntities($node->nodeValue); // Convert text nodes
|
||||
}
|
||||
else
|
||||
{
|
||||
$newBody .= $htmlDoc->saveHTML($node); // Keep HTML tags intact
|
||||
}
|
||||
}
|
||||
|
||||
return $newBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert all characters in a string to HTML entities while leaving HTML tags intact.
|
||||
* @param string $text - The text to convert
|
||||
* @return string - The converted text with HTML entities
|
||||
*/
|
||||
private function convertToHtmlEntities(string $text): string
|
||||
{
|
||||
// Convert characters to HTML entities using mb_encode_numericentity
|
||||
return htmlentities($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all posts with the given category
|
||||
* @param string $category - Category of the post
|
||||
|
2
dist/api/user/userRoutes.php
vendored
2
dist/api/user/userRoutes.php
vendored
@ -181,7 +181,7 @@ class userRoutes implements routesInterface
|
||||
$inactive = 60 * 60 * 48; // 2 days
|
||||
$_SESSION["timeout"] = time() + $inactive;
|
||||
|
||||
return $response->withHeader("Location", "https://rohitpai.co.uk/editor/")->withStatus(302);
|
||||
return $response->withHeader("Location", "https://rohitpai.co.uk/editor/")->withStatus(302);
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode(array("error" => "Unauthorised")));
|
||||
|
2
dist/blog/css/main.css
vendored
2
dist/blog/css/main.css
vendored
File diff suppressed because one or more lines are too long
2
dist/blog/js/index.js
vendored
2
dist/blog/js/index.js
vendored
File diff suppressed because one or more lines are too long
2
dist/blog/js/prism.js
vendored
2
dist/blog/js/prism.js
vendored
File diff suppressed because one or more lines are too long
2
dist/editor/js/CKEditor/ckeditor.js
vendored
2
dist/editor/js/CKEditor/ckeditor.js
vendored
File diff suppressed because one or more lines are too long
2
dist/editor/js/editor.js
vendored
2
dist/editor/js/editor.js
vendored
File diff suppressed because one or more lines are too long
2786
package-lock.json
generated
2786
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -674,8 +674,11 @@ EOD;
|
||||
public function changeHTMLSrc(string $body, string $to, string $from): string
|
||||
{
|
||||
$htmlDoc = new DOMDocument();
|
||||
$body = mb_convert_encoding($body, "HTML-ENTITIES", "UTF-8");
|
||||
$htmlDoc->loadHTML($body, LIBXML_NOERROR);
|
||||
|
||||
// Load the raw HTML content into DOMDocument
|
||||
@$htmlDoc->loadHTML($body, LIBXML_NOERROR);
|
||||
|
||||
// Get the body and process images
|
||||
$doc = $htmlDoc->getElementsByTagName('body')->item(0);
|
||||
$imgs = $doc->getElementsByTagName('img');
|
||||
|
||||
@ -688,9 +691,11 @@ EOD;
|
||||
$srcList[] = $src;
|
||||
$fileName = basename($src);
|
||||
|
||||
// Update the src attribute to the new location
|
||||
$img->setAttribute("src", substr($to, 2) . $fileName);
|
||||
}
|
||||
|
||||
// Rename files and clean up old ones
|
||||
$files = scandir($from);
|
||||
foreach ($files as $file)
|
||||
{
|
||||
@ -706,15 +711,36 @@ EOD;
|
||||
}
|
||||
}
|
||||
|
||||
// Process the HTML content for output
|
||||
$newBody = '';
|
||||
foreach ($doc->childNodes as $node)
|
||||
{
|
||||
$newHTML = $htmlDoc->saveHTML($node);
|
||||
$newBody .= mb_convert_encoding($newHTML, "UTF-8", mb_detect_encoding($newHTML));
|
||||
// Only convert text nodes to HTML entities
|
||||
if ($node->nodeType === XML_TEXT_NODE)
|
||||
{
|
||||
$newBody .= $this->convertToHtmlEntities($node->nodeValue); // Convert text nodes
|
||||
}
|
||||
else
|
||||
{
|
||||
$newBody .= $htmlDoc->saveHTML($node); // Keep HTML tags intact
|
||||
}
|
||||
}
|
||||
|
||||
return $newBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert all characters in a string to HTML entities while leaving HTML tags intact.
|
||||
* @param string $text - The text to convert
|
||||
* @return string - The converted text with HTML entities
|
||||
*/
|
||||
private function convertToHtmlEntities(string $text): string
|
||||
{
|
||||
// Convert characters to HTML entities using mb_encode_numericentity
|
||||
return htmlentities($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all posts with the given category
|
||||
* @param string $category - Category of the post
|
||||
|
@ -123,3 +123,102 @@ section.largePost .outerContent .postContent a {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
section#olderPosts {
|
||||
/*max-width: 90%;*/
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
section#olderPosts .carousel {
|
||||
position: relative;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
section#olderPosts .arrow {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
display: flex;
|
||||
width: 2.5em;
|
||||
height: 2.5em;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 50%;
|
||||
z-index: 1;
|
||||
font-size: 1.625em;
|
||||
color: white;
|
||||
background: var(--mutedBlack);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
section#olderPosts .arrow:hover {
|
||||
background: var(--grey);
|
||||
}
|
||||
|
||||
section#olderPosts #prev {
|
||||
left: -4em;
|
||||
}
|
||||
|
||||
section#olderPosts #next {
|
||||
right: -4em;
|
||||
}
|
||||
|
||||
section#olderPosts .carouselOuter {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 1em;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
section#olderPosts #allCarouselItems {
|
||||
display: none;
|
||||
}
|
||||
|
||||
section#olderPosts #carouselInner {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
justify-content: center;
|
||||
gap: 1em;
|
||||
transition: transform 0.5s ease-in-out;
|
||||
left: 0;
|
||||
height: 50%;
|
||||
}
|
||||
|
||||
section#olderPosts #carouselInner .cardItem {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border: 2px solid var(--primaryDefault);
|
||||
-webkit-border-radius: 0.625rem;
|
||||
-moz-border-radius: 0.625rem;
|
||||
border-radius: 0.625rem;
|
||||
width: 30em;
|
||||
height: 40rem;
|
||||
}
|
||||
|
||||
section#olderPosts #carouselInner .cardItem img {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
object-fit: cover;
|
||||
object-position: left;
|
||||
-webkit-border-radius: 0.625rem;
|
||||
-moz-border-radius: 0.625rem;
|
||||
border-radius: 0.625rem;
|
||||
color: #FFFFFF;
|
||||
|
||||
}
|
||||
|
||||
section#olderPosts #carouselInner .cardItem .content {
|
||||
height: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
align-items: center;
|
||||
background: #FFFFFF;
|
||||
margin: 0 2em 1.5em;
|
||||
padding: 0;
|
||||
}
|
||||
|
@ -9,6 +9,12 @@
|
||||
@import "home.css";
|
||||
@import "category.css";
|
||||
|
||||
div.menuBar a.link::before, main a.link::before,
|
||||
div.menuBar a.link::after, main a.link::after {
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
/* Modal Styling */
|
||||
.policy {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -73,6 +79,11 @@
|
||||
|
||||
@media screen and (max-width: 90em) {
|
||||
|
||||
|
||||
section#olderPosts #carouselInner .cardItem {
|
||||
width: 25em;
|
||||
}
|
||||
|
||||
/***** Individual Blog Posts ***/
|
||||
|
||||
div.mainContent {
|
||||
@ -124,6 +135,25 @@
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
section#olderPosts .arrow {
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
|
||||
section#olderPosts #prev {
|
||||
left: -3em;
|
||||
}
|
||||
|
||||
section#olderPosts #next {
|
||||
right: -3em;
|
||||
}
|
||||
|
||||
section#olderPosts #carouselInner .cardItem {
|
||||
width: 20em;
|
||||
}
|
||||
|
||||
/***** Individual Blog Posts ***/
|
||||
section#individualPost {
|
||||
flex-direction: column-reverse;
|
||||
@ -219,6 +249,24 @@
|
||||
max-width: 75%;
|
||||
}
|
||||
|
||||
section#olderPosts .arrow {
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
section#olderPosts #prev {
|
||||
left: -1.75em;
|
||||
}
|
||||
|
||||
section#olderPosts #next {
|
||||
right: -1.75em;
|
||||
}
|
||||
|
||||
section#olderPosts #carouselInner .cardItem {
|
||||
width: 15em;
|
||||
}
|
||||
|
||||
/***** Individual Blog Posts ***/
|
||||
|
||||
aside.sideContent > div.authorInfo {
|
||||
|
@ -41,7 +41,6 @@ function goToURL(url)
|
||||
if (url === '/blog' || url === 'blog' || url === '/blog/')
|
||||
{
|
||||
loadHomeContent();
|
||||
// window.history.pushState(null, null, url);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -162,6 +161,10 @@ function submitNewsletter()
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* unsubscribe by email
|
||||
* @param email the email to unsubscribe
|
||||
*/
|
||||
function unsubscribe(email)
|
||||
{
|
||||
fetch(`/api/blog/newsletter/${email}`, {
|
||||
@ -243,6 +246,32 @@ function createLargePost(post)
|
||||
return outerContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a card post element
|
||||
* @param post the object
|
||||
* @returns {HTMLDivElement} the outer content of the post
|
||||
*/
|
||||
function createCardPost(post)
|
||||
{
|
||||
let cardItem = document.createElement('div');
|
||||
cardItem.classList.add('cardItem');
|
||||
cardItem.id = 'post' + post.ID;
|
||||
let img = document.createElement('img');
|
||||
img.className = 'cardImg';
|
||||
img.src = post.headerImg.replaceAll('%2F', '/');
|
||||
img.alt = post.title;
|
||||
cardItem.appendChild(img);
|
||||
let content = document.createElement('div');
|
||||
content.classList.add('content');
|
||||
content.innerHTML = `
|
||||
<h2>${post.title}</h2>
|
||||
<h3>Last updated: ${createFormattedDate(post.dateModified)}</h3>
|
||||
<a href="/blog/post/${post.title}" class="btn btnPrimary">See Post</a>
|
||||
`;
|
||||
cardItem.appendChild(content);
|
||||
return cardItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the home content
|
||||
*/
|
||||
@ -250,6 +279,30 @@ function loadHomeContent()
|
||||
{
|
||||
fetch('/api/blog/post').then(res => res.json().then(json =>
|
||||
{
|
||||
// older posts outside the carousel and loop
|
||||
let olderPosts = document.createElement('section');
|
||||
olderPosts.classList.add('largePost');
|
||||
olderPosts.id = 'olderPosts';
|
||||
let h1 = document.createElement('h1');
|
||||
h1.innerHTML = 'older posts';
|
||||
olderPosts.appendChild(h1);
|
||||
|
||||
let carousel = document.createElement('div');
|
||||
carousel.classList.add('carousel');
|
||||
carousel.innerHTML += `<div class="arrow" id="prev"><i class="fa-solid fa-chevron-left"></i></div>
|
||||
<div class="arrow" id="next"><i class="fa-solid fa-chevron-right"></i></div>
|
||||
`;
|
||||
let carouselOuter = document.createElement('div');
|
||||
let carouselInner = document.createElement('div');
|
||||
let allCarouselItems = document.createElement('div');
|
||||
carouselOuter.classList.add('carouselOuter');
|
||||
carouselInner.id = 'carouselInner';
|
||||
allCarouselItems.id = 'allCarouselItems';
|
||||
carouselOuter.appendChild(allCarouselItems);
|
||||
carouselOuter.appendChild(carouselInner);
|
||||
carousel.appendChild(carouselOuter);
|
||||
olderPosts.appendChild(carousel);
|
||||
|
||||
for (let i = 0; i < json.length; i++)
|
||||
{
|
||||
if (json[i].featured === 1)
|
||||
@ -278,10 +331,142 @@ function loadHomeContent()
|
||||
document.querySelector('#main').appendChild(latestPost);
|
||||
}
|
||||
|
||||
if (i > 1)
|
||||
{
|
||||
allCarouselItems.appendChild(createCardPost(json[i]));
|
||||
}
|
||||
}
|
||||
document.querySelector('#main').appendChild(olderPosts);
|
||||
//carousel loop
|
||||
carouselLoop(carouselInner, allCarouselItems);
|
||||
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the loop for the carousel
|
||||
* @param {HTMLDivElement} carouselInner
|
||||
* @param {HTMLDivElement} allItems
|
||||
*/
|
||||
function carouselLoop(carouselInner, allItems)
|
||||
{
|
||||
const prev = document.querySelector('#prev');
|
||||
const next = document.querySelector('#next');
|
||||
const mediaBig = window.matchMedia('(max-width: 75em)');
|
||||
const mediaSmall = window.matchMedia('(max-width: 30em)');
|
||||
let cards = document.querySelectorAll('#allCarouselItems .cardItem');
|
||||
let visibleCardsCount = 3;
|
||||
|
||||
if (mediaBig.matches)
|
||||
{
|
||||
visibleCardsCount = 2; // only show 2 cards if on a slightly smaller screen e.g. tablet/laptop
|
||||
}
|
||||
|
||||
if (mediaSmall.matches)
|
||||
{
|
||||
visibleCardsCount = 1; // only show 1 card if on a mobile, although it'll only work on portrait
|
||||
}
|
||||
|
||||
let visibleCards = [];
|
||||
|
||||
// put the first n (3, 2, or 1) cards in the carousel
|
||||
for (let i = 0; i < visibleCardsCount; i++)
|
||||
{
|
||||
carouselInner.appendChild(cards[i]);
|
||||
visibleCards.push(cards[i]);
|
||||
}
|
||||
|
||||
next.addEventListener('click', () =>
|
||||
{
|
||||
const firstCard = visibleCards.shift();
|
||||
firstCard.style.visibility = 'hidden'; // hide the first card
|
||||
|
||||
// add the next card to the end and off the screen
|
||||
const nextCard = allItems.querySelector('.cardItem');
|
||||
nextCard.style.transform = 'translateX(100%)';
|
||||
nextCard.style.transition = 'none';
|
||||
carouselInner.appendChild(nextCard);
|
||||
visibleCards.push(nextCard);
|
||||
|
||||
// transition all the cards to the left after 50ms
|
||||
// i.e. after the next card is added hence the setTimeout
|
||||
setTimeout(() =>
|
||||
{
|
||||
// move all the cards to the left
|
||||
for (let i = 0; i < carouselInner.children.length; i++)
|
||||
{
|
||||
carouselInner.children[i].style.transition = 'transform 0.5s ease-out';
|
||||
carouselInner.children[i].style.transform = 'translateX(-100%)';
|
||||
}
|
||||
}, 50);
|
||||
|
||||
// after 500ms move all cards back to the center and move
|
||||
// the first card to the hidden div
|
||||
setTimeout(() =>
|
||||
{
|
||||
// move the first card back to the center
|
||||
// instantly and move it to the hidden div
|
||||
firstCard.style.transition = 'none';
|
||||
firstCard.style.transform = 'translateX(0)';
|
||||
allItems.appendChild(firstCard);
|
||||
firstCard.style.visibility = 'visible'; // make it visible again
|
||||
|
||||
// for the remaining cards, reset them
|
||||
for (let i = 0; i < carouselInner.children.length; i++)
|
||||
{
|
||||
carouselInner.children[i].style.transition = 'none';
|
||||
carouselInner.children[i].style.transform = 'translateX(0%)';
|
||||
}
|
||||
|
||||
}, 500);
|
||||
});
|
||||
|
||||
// everything is done in reverse
|
||||
prev.addEventListener('click', () =>
|
||||
{
|
||||
const lastCard = visibleCards.pop();
|
||||
lastCard.style.visibility = 'hidden'; // hide the last card
|
||||
|
||||
// add the previous card to the beginning and off the screen
|
||||
const prevCard = allItems.querySelector('.cardItem:last-child');
|
||||
prevCard.style.transform = 'translateX(-100%)';
|
||||
prevCard.style.transition = 'none';
|
||||
carouselInner.insertBefore(prevCard, carouselInner.firstChild);
|
||||
visibleCards.unshift(prevCard);
|
||||
|
||||
// transition all the cards to the right after 50ms
|
||||
// i.e. after the previous card is added hence the setTimeout
|
||||
setTimeout(() =>
|
||||
{
|
||||
// move all the cards to the right
|
||||
for (let i = 0; i < carouselInner.children.length; i++)
|
||||
{
|
||||
carouselInner.children[i].style.transition = 'transform 0.5s ease-out';
|
||||
carouselInner.children[i].style.transform = 'translateX(100%)';
|
||||
}
|
||||
}, 50);
|
||||
|
||||
// after 500ms move all cards back to the center and move
|
||||
// the last card to the hidden div
|
||||
setTimeout(() =>
|
||||
{
|
||||
// move the last card back to the center
|
||||
// instantly and move it to the hidden div
|
||||
lastCard.style.transition = 'none';
|
||||
lastCard.style.transform = 'translateX(0)';
|
||||
allItems.insertBefore(lastCard, allItems.firstChild);
|
||||
lastCard.style.visibility = 'visible';
|
||||
|
||||
// for the remaining cards reset them
|
||||
for (let i = 0; i < carouselInner.children.length; i++)
|
||||
{
|
||||
carouselInner.children[i].style.transition = 'none';
|
||||
carouselInner.children[i].style.transform = 'translateX(0%)';
|
||||
}
|
||||
}, 500);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the latest and featured posts
|
||||
* @returns {Promise<any[]>} the latest and featured posts
|
||||
@ -848,6 +1033,9 @@ function loadPrivacyPolicy()
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the cookie policy
|
||||
*/
|
||||
function loadCookiePolicy()
|
||||
{
|
||||
document.querySelector('#main').innerHTML = `
|
||||
|
@ -876,7 +876,7 @@ function createEditors(...ids)
|
||||
|
||||
return (
|
||||
`<iframe width="560" height="315" style="text-align:center;" src="https://www.youtube.com/embed/${id}${time ? `?start=${time}` : ''}" ` +
|
||||
'frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen>' +
|
||||
'allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen>' +
|
||||
'</iframe>'
|
||||
);
|
||||
},
|
||||
@ -1229,7 +1229,7 @@ function updateProjectItem(id, e)
|
||||
data['gitLink'] = document.querySelector(`#git${id}proj`).value;
|
||||
|
||||
let imgData = new FormData();
|
||||
imgData.append("img", document.querySelector(`#img${id}`).files[0]);
|
||||
imgData.append('img', document.querySelector(`#img${id}proj`).files[0]);
|
||||
|
||||
fetch("/api/projectData/" + id, {
|
||||
method: "PATCH",
|
||||
@ -1254,8 +1254,8 @@ function updateProjectItem(id, e)
|
||||
}
|
||||
|
||||
document.querySelector(`#projectItem${id}`).classList.toggle("editing");
|
||||
document.querySelector(`#title${id}`).setAttribute("disabled", "");
|
||||
document.querySelector(`#info${id}`).setAttribute("disabled", "");
|
||||
document.querySelector(`#title${id}proj`).setAttribute('disabled', '');
|
||||
document.querySelector(`#info${id}proj`).setAttribute('disabled', '');
|
||||
return;
|
||||
}
|
||||
console.log("updating image")
|
||||
@ -1293,8 +1293,8 @@ function updateProjectItem(id, e)
|
||||
}
|
||||
|
||||
document.querySelector(`#projectItem${id}`).classList.toggle("editing");
|
||||
document.querySelector(`#title${id}`).setAttribute("disabled", "");
|
||||
document.querySelector(`#info${id}`).setAttribute("disabled", "");
|
||||
document.querySelector(`#title${id}proj`).setAttribute('disabled', '');
|
||||
document.querySelector(`#info${id}proj`).setAttribute('disabled', '');
|
||||
document.querySelector(`#projectImage${id}`).src = updatedProjectImage.imgLocation;
|
||||
return;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user