added dynamic lloading for images
This commit is contained in:
parent
8f3f1e2d3e
commit
78c8fdbb4a
3 changed files with 211 additions and 133 deletions
|
@ -1357,10 +1357,10 @@ p {
|
|||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
padding: 10px;
|
||||
background-color: var(--html-bg);
|
||||
background-color: var(--search-bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 0 10px rgba(0,0,0,0.1);
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 0 10px var(--box-shadow);
|
||||
z-index: 1000;
|
||||
width: auto;
|
||||
max-width: 80%;
|
||||
|
@ -1370,9 +1370,9 @@ p {
|
|||
|
||||
/* Map container */
|
||||
#map {
|
||||
height: calc(100% - 60px); /* Adjust this value based on the header height */
|
||||
height: calc(100% - 60px);
|
||||
width: 100%;
|
||||
top: 60px; /* Same value as in the results-search-container margin-top */
|
||||
top: 60px;
|
||||
}
|
||||
|
||||
/* Leaflet control buttons */
|
||||
|
@ -1465,86 +1465,22 @@ p {
|
|||
text-shadow: 1px 1px 2px var(--border) !important; /* Adjust text shadow */
|
||||
}
|
||||
|
||||
/* Ensuring dark theme compliance */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.leaflet-control-locate,
|
||||
.leaflet-control-layers-toggle,
|
||||
.leaflet-bar a,
|
||||
.leaflet-bar a:hover,
|
||||
.leaflet-popup-content-wrapper,
|
||||
.leaflet-popup-tip,
|
||||
.leaflet-control-attribution,
|
||||
.leaflet-control-scale,
|
||||
.leaflet-control-scale-line {
|
||||
background-color: var(--button) !important;
|
||||
border: 1px solid var(--border) !important;
|
||||
color: var(--fg) !important;
|
||||
text-shadow: 1px 1px 2px var(--background-color) !important; /* Dark theme shadow adjustment */
|
||||
}
|
||||
|
||||
.leaflet-control-attribution a {
|
||||
color: var(--link) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- */
|
||||
|
||||
/* Variables for light theme */
|
||||
:root {
|
||||
--background-color: #ffffff;
|
||||
--text-color: #000000;
|
||||
--highlight: #007bff;
|
||||
--border-color: #dddddd;
|
||||
--search-bg: #f1f3f4;
|
||||
--search-bg-input: #ffffff;
|
||||
--search-bg-input-border: #dfe1e5;
|
||||
--button: #f8f9fa;
|
||||
--link: #1a0dab;
|
||||
--fg: #202124;
|
||||
--html-bg: #ffffff;
|
||||
--snip-border: #dfe1e5;
|
||||
--snip-background: #ffffff;
|
||||
--image-view: #ffffff;
|
||||
--image-view-titlebar: #f1f3f4;
|
||||
--search-button: #5f6368;
|
||||
--image-select: #e8f0fe;
|
||||
--view-image-color: #f8f9fa;
|
||||
--footer-bg: #f2f2f2;
|
||||
--footer-font: #70757a;
|
||||
--border: #e0e0e0;
|
||||
--link-visited: #660099;
|
||||
--publish-info: #70757a;
|
||||
--green: #3c802c;
|
||||
}
|
||||
|
||||
/* Styles for dark theme */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background-color: #202124;
|
||||
--text-color: #e8eaed;
|
||||
--highlight: #8ab4f8;
|
||||
--border-color: #5f6368;
|
||||
--search-bg: #303134;
|
||||
--search-bg-input: #202124;
|
||||
--search-bg-input-border: #5f6368;
|
||||
--button: #3c4043;
|
||||
--link: #8ab4f8;
|
||||
--fg: #e8eaed;
|
||||
--html-bg: #202124;
|
||||
--snip-border: #5f6368;
|
||||
--snip-background: #303134;
|
||||
--image-view: #202124;
|
||||
--image-view-titlebar: #303134;
|
||||
--search-button: #e8eaed;
|
||||
--image-select: #5f6368;
|
||||
--view-image-color: #202124;
|
||||
--footer-bg: #303134;
|
||||
--footer-font: #e8eaed;
|
||||
--border: #5f6368;
|
||||
--link-visited: #c79fff;
|
||||
--publish-info: #e8eaed;
|
||||
--green: #8ab4f8;
|
||||
}
|
||||
.message-bottom-left {
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
background-color: var(--search-bg);
|
||||
color: var(--text-color);
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
z-index: 1000;
|
||||
text-align: center;
|
||||
flex-direction: column;
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: 0 0 10px var(--box-shadow);
|
||||
}
|
||||
|
||||
body, h1, p, a, input, button {
|
||||
|
@ -1889,3 +1825,87 @@ body, h1, p, a, input, button {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
/* --- */
|
||||
|
||||
/* Ensuring dark theme compliance */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.leaflet-control-locate,
|
||||
.leaflet-control-layers-toggle,
|
||||
.leaflet-bar a,
|
||||
.leaflet-bar a:hover,
|
||||
.leaflet-popup-content-wrapper,
|
||||
.leaflet-popup-tip,
|
||||
.leaflet-control-attribution,
|
||||
.leaflet-control-scale,
|
||||
.leaflet-control-scale-line {
|
||||
background-color: var(--button) !important;
|
||||
border: 1px solid var(--border) !important;
|
||||
color: var(--fg) !important;
|
||||
text-shadow: 1px 1px 2px var(--background-color) !important; /* Dark theme shadow adjustment */
|
||||
}
|
||||
|
||||
.leaflet-control-attribution a {
|
||||
color: var(--link) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Variables for light theme */
|
||||
:root {
|
||||
--background-color: #ffffff;
|
||||
--text-color: #000000;
|
||||
--highlight: #007bff;
|
||||
--border-color: #dddddd;
|
||||
--search-bg: #f1f3f4;
|
||||
--search-bg-input: #ffffff;
|
||||
--search-bg-input-border: #dfe1e5;
|
||||
--button: #f8f9fa;
|
||||
--link: #1a0dab;
|
||||
--fg: #202124;
|
||||
--html-bg: #ffffff;
|
||||
--snip-border: #dfe1e5;
|
||||
--snip-background: #ffffff;
|
||||
--image-view: #ffffff;
|
||||
--image-view-titlebar: #f1f3f4;
|
||||
--search-button: #5f6368;
|
||||
--image-select: #e8f0fe;
|
||||
--view-image-color: #f8f9fa;
|
||||
--footer-bg: #f2f2f2;
|
||||
--footer-font: #70757a;
|
||||
--border: #e0e0e0;
|
||||
--link-visited: #660099;
|
||||
--publish-info: #70757a;
|
||||
--green: #3c802c;
|
||||
--box-shadow: #00000020;
|
||||
}
|
||||
|
||||
/* Styles for dark theme */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background-color: #202124;
|
||||
--text-color: #e8eaed;
|
||||
--highlight: #8ab4f8;
|
||||
--border-color: #5f6368;
|
||||
--search-bg: #303134;
|
||||
--search-bg-input: #202124;
|
||||
--search-bg-input-border: #5f6368;
|
||||
--button: #3c4043;
|
||||
--link: #8ab4f8;
|
||||
--fg: #e8eaed;
|
||||
--html-bg: #202124;
|
||||
--snip-border: #5f6368;
|
||||
--snip-background: #303134;
|
||||
--image-view: #202124;
|
||||
--image-view-titlebar: #303134;
|
||||
--search-button: #e8eaed;
|
||||
--image-select: #5f6368;
|
||||
--view-image-color: #202124;
|
||||
--footer-bg: #303134;
|
||||
--footer-font: #e8eaed;
|
||||
--border: #5f6368;
|
||||
--link-visited: #c79fff;
|
||||
--publish-info: #e8eaed;
|
||||
--green: #8ab4f8;
|
||||
--box-shadow: #ffffff20;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,10 +11,10 @@
|
|||
<form action="/search" id="prev-next-form" class="results-search-container" method="GET" autocomplete="off">
|
||||
<h1 class="logomobile"><a class="no-decoration" href="./">Ocásek</a></h1>
|
||||
<div class="wrapper-results">
|
||||
<input type="text" name="q" value="{{ .Query }}" id="search-input" placeholder="Type to search..." />
|
||||
<button id="search-wrapper-ico" class="material-icons-round" name="t" value="image">search</button>
|
||||
<input type="submit" class="hide" name="t" value="image" />
|
||||
</div>
|
||||
<input type="text" name="q" value="{{ .Query }}" id="search-input" placeholder="Type to search..." />
|
||||
<button id="search-wrapper-ico" class="material-icons-round" name="t" value="image">search</button>
|
||||
<input type="submit" class="hide" name="t" value="image" />
|
||||
</div>
|
||||
<div class="sub-search-button-wrapper">
|
||||
<div class="search-container-results-btn">
|
||||
<button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="text">search</button>
|
||||
|
@ -33,17 +33,16 @@
|
|||
<button name="t" value="forum" class="clickable">Forums</button>
|
||||
</div>
|
||||
<div id="content" class="js-enabled">
|
||||
<div class="search-container-results-btn">
|
||||
<button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="map">map</button>
|
||||
<button name="t" value="map" class="clickable">Maps</button>
|
||||
</div>
|
||||
<div class="search-container-results-btn">
|
||||
<button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="map">map</button>
|
||||
<button name="t" value="map" class="clickable">Maps</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-container-results-btn">
|
||||
<button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="file">share</button>
|
||||
<button name="t" value="file" class="clickable">Torrents</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<form class="results_settings" action="/search" method="get">
|
||||
<input type="hidden" name="q" value="{{ .Query }}">
|
||||
|
@ -58,27 +57,10 @@
|
|||
</select>
|
||||
<button class="results-save" name="t" value="image">Apply settings</button>
|
||||
</form>
|
||||
<div class="search-results">
|
||||
<div class="search-results" id="results">
|
||||
<!-- Results go here -->
|
||||
{{ if .Results }}
|
||||
<div class="images images_viewer_hidden">
|
||||
<!-- Image Viewer Placeholder - Adapt as necessary -->
|
||||
<div class="image_view image_hide">
|
||||
<!-- Navigation and Close for the Image Viewer -->
|
||||
<!-- Placeholder for dynamic interaction, adapt the onclick functionality as needed -->
|
||||
<div class="image-view-close">
|
||||
<button class="btn-nostyle"><div class="material-icons-round icon_visibility clickable">navigate_before</div></button>
|
||||
<button class="btn-nostyle"><div class="material-icons-round icon_visibility clickable">navigate_next</div></button>
|
||||
<button class="btn-nostyle"><div class="material-icons-round icon_visibility clickable">close</div></button>
|
||||
</div>
|
||||
<!-- Placeholder for selected image -->
|
||||
<a class="image-viewer-link clickable" href="#">
|
||||
<div class="view-image">
|
||||
<img class="view-image-img" src="" alt="Selected Image"/>
|
||||
</div>
|
||||
</a>
|
||||
<!-- Additional image details here -->
|
||||
</div>
|
||||
<!-- Images Grid -->
|
||||
{{ range .Results }}
|
||||
<div class="image">
|
||||
|
@ -93,29 +75,90 @@
|
|||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
<!-- Pagination -->
|
||||
<div class="prev-next prev-img">
|
||||
<form action="/search" method="get">
|
||||
<input type="hidden" name="q" value="{{ .Query }}">
|
||||
<input type="hidden" name="t" value="image">
|
||||
{{ if .HasPrevPage }}
|
||||
<!-- Subtract 1 from the current page for the Previous button -->
|
||||
<button type="submit" name="p" value="{{ sub .Page 1 }}">Previous</button>
|
||||
{{ end }}
|
||||
{{ if .HasNextPage }}
|
||||
<!-- Add 1 to the current page for the Next button -->
|
||||
<button type="submit" name="p" value="{{ add .Page 1 }}">Next</button>
|
||||
{{ end }}
|
||||
</form>
|
||||
</div>
|
||||
{{ else }}
|
||||
<noscript>
|
||||
<div class="prev-next prev-img">
|
||||
<form action="/search" method="get">
|
||||
<input type="hidden" name="q" value="{{ .Query }}">
|
||||
<input type="hidden" name="t" value="image">
|
||||
{{ if .HasPrevPage }}
|
||||
<!-- Subtract 1 from the current page for the Previous button -->
|
||||
<button type="submit" name="p" value="{{ sub .Page 1 }}">Previous</button>
|
||||
{{ end }}
|
||||
{{ if .HasNextPage }}
|
||||
<!-- Add 1 to the current page for the Next button -->
|
||||
<button type="submit" name="p" value="{{ add .Page 1 }}">Next</button>
|
||||
{{ end }}
|
||||
</form>
|
||||
</div>
|
||||
</noscript>
|
||||
{{ else if .NoResults }}
|
||||
<div class="no-results">No results found for '{{ .Query }}'. Try different keywords.</div>
|
||||
{{ else }}
|
||||
<div class="no-more-results">Looks like this is the end of results.</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
<div class="message-bottom-left" id="message-bottom-left">
|
||||
<span>Searching for new results...</span>
|
||||
</div>
|
||||
<script>
|
||||
// Check if JavaScript is enabled and modify the DOM accordingly
|
||||
document.getElementById('content').classList.remove('js-enabled');
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
let page = {{ .Page }};
|
||||
const query = "{{ .Query }}";
|
||||
let loading = false;
|
||||
let hasMoreResults = true;
|
||||
const loadingIndicator = document.getElementById('message-bottom-left');
|
||||
let loadingTimeout;
|
||||
|
||||
function loadResults(newPage) {
|
||||
if (loading || !hasMoreResults) return;
|
||||
loading = true;
|
||||
|
||||
// Show loading indicator if taking more than 100ms
|
||||
loadingTimeout = setTimeout(() => {
|
||||
loadingIndicator.style.display = 'flex';
|
||||
}, 100);
|
||||
|
||||
fetch(`/search?q=${encodeURIComponent(query)}&t=image&p=${newPage}`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.text();
|
||||
})
|
||||
.then(data => {
|
||||
clearTimeout(loadingTimeout);
|
||||
loadingIndicator.style.display = 'none';
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(data, 'text/html');
|
||||
const newResults = doc.getElementById('results').innerHTML;
|
||||
const noResultsMessage = "No results found for '{{ .Query }}'. Try different keywords.";
|
||||
const endOfResultsMessage = "Looks like this is the end of results.";
|
||||
const serverError = "Internal Server Error";
|
||||
|
||||
if (newResults.includes(noResultsMessage) || newResults.includes(endOfResultsMessage) || newResults.includes(serverError)) {
|
||||
document.getElementById('results').innerHTML += newResults;
|
||||
hasMoreResults = false;
|
||||
} else {
|
||||
document.getElementById('results').innerHTML += newResults;
|
||||
page = newPage;
|
||||
}
|
||||
loading = false;
|
||||
})
|
||||
.catch(error => {
|
||||
clearTimeout(loadingTimeout);
|
||||
loadingIndicator.style.display = 'none';
|
||||
console.error('Error loading results:', error);
|
||||
hasMoreResults = false; // Stop further attempts
|
||||
loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', () => {
|
||||
if (window.innerHeight + window.scrollY >= document.body.offsetHeight) {
|
||||
loadResults(page + 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -73,6 +73,9 @@
|
|||
<div class="no-more-results">Looks like this is the end of results.</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="message-bottom-left" id="message-bottom-left">
|
||||
<span>Searching for new results...</span>
|
||||
</div>
|
||||
<div class="prev-next prev-img" id="prev-next">
|
||||
<form action="/search" method="get">
|
||||
<input type="hidden" name="q" value="{{ .Query }}">
|
||||
|
@ -94,13 +97,23 @@
|
|||
const query = "{{ .Query }}";
|
||||
let loading = false;
|
||||
let hasMoreResults = true;
|
||||
const loadingIndicator = document.getElementById('message-bottom-left');
|
||||
let loadingTimeout;
|
||||
|
||||
function loadResults(newPage) {
|
||||
if (loading || !hasMoreResults) return;
|
||||
loading = true;
|
||||
|
||||
// Show loading indicator if taking more than 100ms
|
||||
loadingTimeout = setTimeout(() => {
|
||||
loadingIndicator.style.display = 'flex';
|
||||
}, 100);
|
||||
|
||||
fetch(`/search?q=${encodeURIComponent(query)}&t=text&p=${newPage}`)
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
clearTimeout(loadingTimeout);
|
||||
loadingIndicator.style.display = 'none';
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(data, 'text/html');
|
||||
const newResults = doc.getElementById('results').innerHTML;
|
||||
|
@ -116,6 +129,8 @@
|
|||
loading = false;
|
||||
})
|
||||
.catch(error => {
|
||||
clearTimeout(loadingTimeout);
|
||||
loadingIndicator.style.display = 'none';
|
||||
console.error('Error loading results:', error);
|
||||
loading = false;
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue