fix language/safe search gui

This commit is contained in:
partisan 2024-08-13 16:38:02 +02:00
parent e056832321
commit c00f20860d
13 changed files with 748 additions and 694 deletions

View file

@ -59,27 +59,31 @@ func handleFileSearch(w http.ResponseWriter, settings UserSettings, query string
} }
data := struct { data := struct {
Results []TorrentResult Results []TorrentResult
Query string Query string
Fetched string Fetched string
Category string Category string
Sort string Sort string
HasPrevPage bool Page int
HasNextPage bool HasPrevPage bool
Page int HasNextPage bool
Settings Settings LanguageOptions []LanguageOption
Theme string CurrentLang string
Theme string
Safe string
}{ }{
Results: combinedResults, Results: combinedResults,
Query: query, Query: query,
Fetched: fmt.Sprintf("%.2f", elapsedTime.Seconds()), Fetched: fmt.Sprintf("%.2f seconds", elapsedTime.Seconds()),
Category: "all", Category: "all",
Sort: "seed", Sort: "seed",
HasPrevPage: page > 1, Page: page,
HasNextPage: len(combinedResults) > 0, HasPrevPage: page > 1,
Page: page, HasNextPage: len(combinedResults) > 0,
Settings: Settings{UxLang: settings.Language, Safe: settings.SafeSearch}, // Now this is painful, are there two Settings variables?? LanguageOptions: languageOptions,
Theme: settings.Theme, CurrentLang: settings.Language,
Theme: settings.Theme,
Safe: settings.SafeSearch,
} }
// // Debugging: Print results before rendering template // // Debugging: Print results before rendering template

View file

@ -108,21 +108,23 @@ func handleForumsSearch(w http.ResponseWriter, settings UserSettings, query stri
data := struct { data := struct {
Query string Query string
Results []ForumSearchResult Results []ForumSearchResult
LanguageOptions []LanguageOption
CurrentLang string
Page int Page int
HasPrevPage bool HasPrevPage bool
HasNextPage bool HasNextPage bool
LanguageOptions []LanguageOption
CurrentLang string
Theme string Theme string
Safe string
}{ }{
Query: query, Query: query,
Results: results, Results: results,
LanguageOptions: languageOptions,
CurrentLang: settings.Language,
Page: page, Page: page,
HasPrevPage: page > 1, HasPrevPage: page > 1,
HasNextPage: len(results) == 25, HasNextPage: len(results) == 25,
LanguageOptions: languageOptions,
CurrentLang: settings.Language,
Theme: settings.Theme, Theme: settings.Theme,
Safe: settings.SafeSearch,
} }
funcMap := template.FuncMap{ funcMap := template.FuncMap{

View file

@ -37,23 +37,25 @@ func handleImageSearch(w http.ResponseWriter, settings UserSettings, query strin
Query string Query string
Page int Page int
Fetched string Fetched string
LanguageOptions []LanguageOption
CurrentLang string
HasPrevPage bool HasPrevPage bool
HasNextPage bool HasNextPage bool
NoResults bool NoResults bool
LanguageOptions []LanguageOption
CurrentLang string
Theme string Theme string
Safe string
}{ }{
Results: combinedResults, Results: combinedResults,
Query: query, Query: query,
Page: page, Page: page,
Fetched: fmt.Sprintf("%.2f seconds", elapsedTime.Seconds()), Fetched: fmt.Sprintf("%.2f seconds", elapsedTime.Seconds()),
LanguageOptions: languageOptions,
CurrentLang: settings.Language,
HasPrevPage: page > 1, HasPrevPage: page > 1,
HasNextPage: len(combinedResults) >= 50, HasNextPage: len(combinedResults) >= 50,
NoResults: len(combinedResults) == 0, NoResults: len(combinedResults) == 0,
LanguageOptions: languageOptions,
CurrentLang: settings.Language,
Theme: settings.Theme, Theme: settings.Theme,
Safe: settings.SafeSearch,
} }
err = tmpl.Execute(w, data) err = tmpl.Execute(w, data)

14
main.go
View file

@ -92,9 +92,21 @@ func handleSearch(w http.ResponseWriter, r *http.Request) {
} }
// Render the search page template if no query // Render the search page template if no query
data := struct {
LanguageOptions []LanguageOption
CurrentLang string
Theme string
Safe string
}{
LanguageOptions: languageOptions,
CurrentLang: settings.Language,
Theme: settings.Theme,
Safe: settings.SafeSearch,
}
if query == "" { if query == "" {
tmpl := template.Must(template.ParseFiles("templates/search.html")) tmpl := template.Must(template.ParseFiles("templates/search.html"))
tmpl.Execute(w, settings) tmpl.Execute(w, data)
return return
} }

1
map.go
View file

@ -59,6 +59,7 @@ func handleMapSearch(w http.ResponseWriter, settings UserSettings, query string)
"Longitude": longitude, "Longitude": longitude,
"Found": found, "Found": found,
"Theme": settings.Theme, "Theme": settings.Theme,
"Safe": settings.SafeSearch,
} }
tmpl, err := template.ParseFiles("templates/map.html") tmpl, err := template.ParseFiles("templates/map.html")

226
templates/files.html Normal file → Executable file
View file

@ -1,113 +1,113 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{.Query}} - Ocásek</title> <title>{{.Query}} - Ocásek</title>
<link rel="stylesheet" href="/static/css/style.css"> <link rel="stylesheet" href="/static/css/style.css">
<link rel="stylesheet" href="/static/css/{{.Theme}}.css"> <link rel="stylesheet" href="/static/css/{{.Theme}}.css">
<link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml"> <link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml">
</head> </head>
<body> <body>
<form action="/search" id="prev-next-form" class="results-search-container" method="GET" autocomplete="off"> <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> <h1 class="logomobile"><a class="no-decoration" href="./">Ocásek</a></h1>
<div class="wrapper-results"> <div class="wrapper-results">
<input type="text" name="q" value="{{ .Query }}" id="search-input" placeholder="Type to search..." /> <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="file">search</button> <button id="search-wrapper-ico" class="material-icons-round" name="t" value="file">search</button>
<input type="submit" class="hide" name="t" value="file" /> <input type="submit" class="hide" name="t" value="file" />
</div> </div>
<div class="sub-search-button-wrapper"> <div class="sub-search-button-wrapper">
<div class="search-container-results-btn"> <div class="search-container-results-btn">
<button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="text">search</button> <button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="text">search</button>
<button name="t" value="text" class="clickable">Web</button> <button name="t" value="text" class="clickable">Web</button>
</div> </div>
<div class="search-container-results-btn"> <div class="search-container-results-btn">
<button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="image">image</button> <button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="image">image</button>
<button name="t" value="image" class="clickable">Images</button> <button name="t" value="image" class="clickable">Images</button>
</div> </div>
<div class="search-container-results-btn"> <div class="search-container-results-btn">
<button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="video">movie</button> <button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="video">movie</button>
<button name="t" value="video" class="clickable">Videos</button> <button name="t" value="video" class="clickable">Videos</button>
</div> </div>
<div class="search-container-results-btn"> <div class="search-container-results-btn">
<button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="forum">forum</button> <button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="forum">forum</button>
<button name="t" value="forum" class="clickable">Forums</button> <button name="t" value="forum" class="clickable">Forums</button>
</div> </div>
<div id="content" class="js-enabled"> <div id="content" class="js-enabled">
<div class="search-container-results-btn"> <div class="search-container-results-btn">
<button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="map">map</button> <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> <button name="t" value="map" class="clickable">Maps</button>
</div> </div>
</div> </div>
<div class="search-container-results-btn"> <div class="search-container-results-btn">
<button id="sub-search-wrapper-ico" class="material-icons-round clickable search-active" name="t" value="file">share</button> <button id="sub-search-wrapper-ico" class="material-icons-round clickable search-active" name="t" value="file">share</button>
<button name="t" value="file" class="clickable search-active">Torrents</button> <button name="t" value="file" class="clickable search-active">Torrents</button>
</div> </div>
</div> </div>
</form> </form>
<p class="fetched fetched_dif fetched_tor">Fetched in {{ .Fetched }} seconds</p> <p class="fetched fetched_dif fetched_tor">Fetched in {{ .Fetched }} seconds</p>
{{ if .Results }} {{ if .Results }}
<form action="/search" class="torrent-sort" method="GET"> <form action="/search" class="torrent-sort" method="GET">
<input type="hidden" name="q" value="{{ .Query }}"> <input type="hidden" name="q" value="{{ .Query }}">
<input type="hidden" name="t" value="file"> <input type="hidden" name="t" value="file">
<select class="torrent-settings" name="sort"> <select class="torrent-settings" name="sort">
<option value="seed" {{ if eq .Sort "seed" }} selected {{ end }}>Number of Seeders</option> <option value="seed" {{ if eq .Sort "seed" }} selected {{ end }}>Number of Seeders</option>
<option value="leech" {{ if eq .Sort "leech" }} selected {{ end }}>Number of Leechers</option> <option value="leech" {{ if eq .Sort "leech" }} selected {{ end }}>Number of Leechers</option>
<option value="lth" {{ if eq .Sort "lth" }} selected {{ end }}>Size (Low to High)</option> <option value="lth" {{ if eq .Sort "lth" }} selected {{ end }}>Size (Low to High)</option>
<option value="htl" {{ if eq .Sort "htl" }} selected {{ end }}>Size (High to Low)</option> <option value="htl" {{ if eq .Sort "htl" }} selected {{ end }}>Size (High to Low)</option>
</select> </select>
<select class="torrent-cat" name="cat"> <select class="torrent-cat" name="cat">
<option value="all" {{ if eq .Category "all" }} selected {{ end }}>All Categories</option> <option value="all" {{ if eq .Category "all" }} selected {{ end }}>All Categories</option>
<option value="movie" {{ if eq .Category "movie" }} selected {{ end }}>Movies</option> <option value="movie" {{ if eq .Category "movie" }} selected {{ end }}>Movies</option>
<option value="audiobook" {{ if eq .Category "audiobook" }} selected {{ end }}>Audiobooks</option> <option value="audiobook" {{ if eq .Category "audiobook" }} selected {{ end }}>Audiobooks</option>
<option value="tv" {{ if eq .Category "tv" }} selected {{ end }}>TV Shows</option> <option value="tv" {{ if eq .Category "tv" }} selected {{ end }}>TV Shows</option>
<option value="games" {{ if eq .Category "games" }} selected {{ end }}>Games</option> <option value="games" {{ if eq .Category "games" }} selected {{ end }}>Games</option>
<option value="software" {{ if eq .Category "software" }} selected {{ end }}>Software</option> <option value="software" {{ if eq .Category "software" }} selected {{ end }}>Software</option>
<option value="anime" {{ if eq .Category "anime" }} selected {{ end }}>Anime</option> <option value="anime" {{ if eq .Category "anime" }} selected {{ end }}>Anime</option>
<option value="music" {{ if eq .Category "music" }} selected {{ end }}>Music</option> <option value="music" {{ if eq .Category "music" }} selected {{ end }}>Music</option>
{{ if eq .Settings.Safe "inactive" }} {{ if eq .Safe "disabled" }}
<option value="xxx" {{ if eq .Category "xxx" }} selected {{ end }}>XXX (18+)</option> <option value="xxx" {{ if eq .Category "xxx" }} selected {{ end }}>XXX (18+)</option>
{{ end }} {{ end }}
</select> </select>
<button type="submit" class="torrent-sort-save">Apply settings</button> <button type="submit" class="torrent-sort-save">Apply settings</button>
</form> </form>
<div class="clean"> <div class="clean">
{{ range .Results }} {{ range .Results }}
<div class="results" id="results"> <div class="results" id="results">
{{ if .Error }} {{ if .Error }}
<div class="error">{{ .Error }}</div> <div class="error">{{ .Error }}</div>
{{ else }} {{ else }}
<a id="link" href="{{ .URL }}">{{ .URL }}</a> <a id="link" href="{{ .URL }}">{{ .URL }}</a>
<a class="torrent" href="magnet:{{ .Magnet }}"><h3>{{ .Title }}</h3></a> <a class="torrent" href="magnet:{{ .Magnet }}"><h3>{{ .Title }}</h3></a>
<p class="stats">{{ if .Views }}{{ .Views }} views • {{ end }}{{ .Size }}</p> <p class="stats">{{ if .Views }}{{ .Views }} views • {{ end }}{{ .Size }}</p>
<p class="publish__info"> Seeders: <span class="seeders">{{ .Seeders }}</span> | Leechers: <span class="leechers">{{ .Leechers }}</span></p> <p class="publish__info"> Seeders: <span class="seeders">{{ .Seeders }}</span> | Leechers: <span class="leechers">{{ .Leechers }}</span></p>
{{ end }} {{ end }}
</div> </div>
{{ end }} {{ end }}
</div> </div>
<div class="prev-next prev-img"> <div class="prev-next prev-img">
<form action="/search" method="get"> <form action="/search" method="get">
<input type="hidden" name="q" value="{{ .Query }}"> <input type="hidden" name="q" value="{{ .Query }}">
<input type="hidden" name="t" value="file"> <input type="hidden" name="t" value="file">
{{ if .HasPrevPage }} {{ if .HasPrevPage }}
<button type="submit" name="p" value="{{ sub .Page 1 }}">Previous</button> <button type="submit" name="p" value="{{ sub .Page 1 }}">Previous</button>
{{ end }} {{ end }}
{{ if .HasNextPage }} {{ if .HasNextPage }}
<button type="submit" name="p" value="{{ add .Page 1 }}">Next</button> <button type="submit" name="p" value="{{ add .Page 1 }}">Next</button>
{{ end }} {{ end }}
</form> </form>
</div> </div>
{{ else }} {{ else }}
<div class="no-results-found"> <div class="no-results-found">
Your search '{{ .Query }}' came back with no results.<br> Your search '{{ .Query }}' came back with no results.<br>
Try rephrasing your search term and/or recorrect any spelling mistakes. Try rephrasing your search term and/or recorrect any spelling mistakes.
</div> </div>
{{ end }} {{ end }}
<script> <script>
// Check if JavaScript is enabled and modify the DOM accordingly // Check if JavaScript is enabled and modify the DOM accordingly
document.getElementById('content').classList.remove('js-enabled'); document.getElementById('content').classList.remove('js-enabled');
</script> </script>
</body> </body>
</html> </html>

184
templates/forums.html Normal file → Executable file
View file

@ -1,92 +1,92 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{.Query}} - Ocásek</title> <title>{{.Query}} - Ocásek</title>
<link rel="stylesheet" href="/static/css/style.css"> <link rel="stylesheet" href="/static/css/style.css">
<link rel="stylesheet" href="/static/css/{{.Theme}}.css"> <link rel="stylesheet" href="/static/css/{{.Theme}}.css">
<link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml"> <link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml">
</head> </head>
<body> <body>
<form action="/search" id="prev-next-form" class="results-search-container" method="GET" autocomplete="off"> <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> <h1 class="logomobile"><a class="no-decoration" href="./">Ocásek</a></h1>
<div class="wrapper-results"> <div class="wrapper-results">
<input type="text" name="q" value="{{ .Query }}" id="search-input" placeholder="Type to search..." /> <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="forum">search</button> <button id="search-wrapper-ico" class="material-icons-round" name="t" value="forum">search</button>
<input type="submit" class="hide" name="t" value="forum" /> <input type="submit" class="hide" name="t" value="forum" />
</div> </div>
<div class="sub-search-button-wrapper"> <div class="sub-search-button-wrapper">
<div class="search-container-results-btn"> <div class="search-container-results-btn">
<button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="text">search</button> <button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="text">search</button>
<button name="t" value="text" class="clickable">Web</button> <button name="t" value="text" class="clickable">Web</button>
</div> </div>
<div class="search-container-results-btn"> <div class="search-container-results-btn">
<button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="image">image</button> <button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="image">image</button>
<button name="t" value="image" class="clickable">Images</button> <button name="t" value="image" class="clickable">Images</button>
</div> </div>
<div class="search-container-results-btn"> <div class="search-container-results-btn">
<button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="video">movie</button> <button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="video">movie</button>
<button name="t" value="video" class="clickable">Videos</button> <button name="t" value="video" class="clickable">Videos</button>
</div> </div>
<div class="search-container-results-btn"> <div class="search-container-results-btn">
<button id="sub-search-wrapper-ico" class="material-icons-round clickable search-active" name="t" value="forum">forum</button> <button id="sub-search-wrapper-ico" class="material-icons-round clickable search-active" name="t" value="forum">forum</button>
<button name="t" value="forum" class="clickable search-active">Forums</button> <button name="t" value="forum" class="clickable search-active">Forums</button>
</div> </div>
<div id="content" class="js-enabled"> <div id="content" class="js-enabled">
<div class="search-container-results-btn"> <div class="search-container-results-btn">
<button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="map">map</button> <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> <button name="t" value="map" class="clickable">Maps</button>
</div> </div>
</div> </div>
<div class="search-container-results-btn"> <div class="search-container-results-btn">
<button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="file">share</button> <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> <button name="t" value="file" class="clickable">Torrents</button>
</div> </div>
</div> </div>
</form> </form>
<form class="results_settings" action="/search" method="get"> <form class="results_settings" action="/search" method="get">
<input type="hidden" name="q" value="{{ .Query }}"> <input type="hidden" name="q" value="{{ .Query }}">
<select class="results-settings" name="safe" id="safeSearchSelect"> <select class="results-settings" name="safe" id="safeSearchSelect">
<option value="">Safe Search Off</option> <option value="disabled" {{if eq .Safe "disabled"}}selected{{end}}>Safe Search Off</option>
<option value="active">Safe Search On</option> <option value="active" {{if eq .Safe "active"}}selected{{end}}>Safe Search On</option>
</select> </select>
<select class="results-settings" name="lang" id="languageSelect"> <select class="results-settings" name="lang" id="languageSelect">
{{range .LanguageOptions}} {{range .LanguageOptions}}
<option value="{{.Code}}" {{if eq .Code $.CurrentLang}}selected{{end}}>{{.Name}}</option> <option value="{{.Code}}" {{if eq .Code $.CurrentLang}}selected{{end}}>{{.Name}}</option>
{{end}} {{end}}
</select> </select>
<button class="results-save" name="t" value="text">Apply settings</button> <button class="results-save" name="t" value="text">Apply settings</button>
</form> </form>
<div class="results"> <div class="results">
{{if .Results}} {{if .Results}}
{{range .Results}} {{range .Results}}
<div class="result_item"> <div class="result_item">
<a id="link" href="{{.URL}}">{{.URL}}</a> <a id="link" href="{{.URL}}">{{.URL}}</a>
<a href="{{.URL}}"><h3>{{.Header}}</h3></a> <a href="{{.URL}}"><h3>{{.Header}}</h3></a>
<p>{{.Description}}</p> <p>{{.Description}}</p>
</div> </div>
<br> <br>
{{end}} {{end}}
{{else}} {{else}}
<div class="no-results">No results found for '{{ .Query }}'. Try different keywords.</div> <div class="no-results">No results found for '{{ .Query }}'. Try different keywords.</div>
{{end}} {{end}}
</div> </div>
<div class="prev-next prev-img"> <div class="prev-next prev-img">
<form action="/search" method="get"> <form action="/search" method="get">
<input type="hidden" name="q" value="{{ .Query }}"> <input type="hidden" name="q" value="{{ .Query }}">
<input type="hidden" name="t" value="text"> <input type="hidden" name="t" value="text">
{{ if .HasPrevPage }} {{ if .HasPrevPage }}
<button type="submit" name="p" value="{{ sub .Page 1 }}">Previous</button> <button type="submit" name="p" value="{{ sub .Page 1 }}">Previous</button>
{{ end }} {{ end }}
{{ if .HasNextPage }} {{ if .HasNextPage }}
<button type="submit" name="p" value="{{ add .Page 1 }}">Next</button> <button type="submit" name="p" value="{{ add .Page 1 }}">Next</button>
{{ end }} {{ end }}
</form> </form>
</div> </div>
<script> <script>
// Check if JavaScript is enabled and modify the DOM accordingly // Check if JavaScript is enabled and modify the DOM accordingly
document.getElementById('content').classList.remove('js-enabled'); document.getElementById('content').classList.remove('js-enabled');
</script> </script>
</body> </body>
</html> </html>

338
templates/images.html Normal file → Executable file
View file

@ -1,169 +1,169 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{.Query}} - Ocásek</title> <title>{{.Query}} - Ocásek</title>
<link rel="stylesheet" href="/static/css/style.css"> <link rel="stylesheet" href="/static/css/style.css">
<link rel="stylesheet" href="/static/css/{{.Theme}}.css"> <link rel="stylesheet" href="/static/css/{{.Theme}}.css">
<link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml"> <link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml">
</head> </head>
<body> <body>
<form action="/search" id="prev-next-form" class="results-search-container" method="GET" autocomplete="off"> <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> <h1 class="logomobile"><a class="no-decoration" href="./">Ocásek</a></h1>
<div class="wrapper-results"> <div class="wrapper-results">
<input type="text" name="q" value="{{ .Query }}" id="search-input" placeholder="Type to search..." /> <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> <button id="search-wrapper-ico" class="material-icons-round" name="t" value="image">search</button>
<input type="submit" class="hide" name="t" value="image" /> <input type="submit" class="hide" name="t" value="image" />
</div> </div>
<div class="sub-search-button-wrapper"> <div class="sub-search-button-wrapper">
<div class="search-container-results-btn"> <div class="search-container-results-btn">
<button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="text">search</button> <button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="text">search</button>
<button name="t" value="text" class="clickable">Web</button> <button name="t" value="text" class="clickable">Web</button>
</div> </div>
<div class="search-container-results-btn"> <div class="search-container-results-btn">
<button id="sub-search-wrapper-ico" class="material-icons-round clickable search-active" name="t" value="image">image</button> <button id="sub-search-wrapper-ico" class="material-icons-round clickable search-active" name="t" value="image">image</button>
<button name="t" value="image" class="clickable search-active">Images</button> <button name="t" value="image" class="clickable search-active">Images</button>
</div> </div>
<div class="search-container-results-btn"> <div class="search-container-results-btn">
<button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="video">movie</button> <button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="video">movie</button>
<button name="t" value="video" class="clickable">Videos</button> <button name="t" value="video" class="clickable">Videos</button>
</div> </div>
<div class="search-container-results-btn"> <div class="search-container-results-btn">
<button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="forum">forum</button> <button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="forum">forum</button>
<button name="t" value="forum" class="clickable">Forums</button> <button name="t" value="forum" class="clickable">Forums</button>
</div> </div>
<div id="content" class="js-enabled"> <div id="content" class="js-enabled">
<div class="search-container-results-btn"> <div class="search-container-results-btn">
<button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="map">map</button> <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> <button name="t" value="map" class="clickable">Maps</button>
</div> </div>
</div> </div>
<div class="search-container-results-btn"> <div class="search-container-results-btn">
<button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="file">share</button> <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> <button name="t" value="file" class="clickable">Torrents</button>
</div> </div>
</div> </div>
</form> </form>
<form class="results_settings" action="/search" method="get"> <form class="results_settings" action="/search" method="get">
<input type="hidden" name="q" value="{{ .Query }}"> <input type="hidden" name="q" value="{{ .Query }}">
<select class="results-settings" name="safe" id="safeSearchSelect"> <select class="results-settings" name="safe" id="safeSearchSelect">
<option value="">Safe Search Off</option> <option value="disabled" {{if eq .Safe "disabled"}}selected{{end}}>Safe Search Off</option>
<option value="active">Safe Search On</option> <option value="active" {{if eq .Safe "active"}}selected{{end}}>Safe Search On</option>
</select> </select>
<select class="results-settings" name="lang" id="languageSelect"> <select class="results-settings" name="lang" id="languageSelect">
{{range .LanguageOptions}} {{range .LanguageOptions}}
<option value="{{.Code}}" {{if eq .Code $.CurrentLang}}selected{{end}}>{{.Name}}</option> <option value="{{.Code}}" {{if eq .Code $.CurrentLang}}selected{{end}}>{{.Name}}</option>
{{end}} {{end}}
</select> </select>
<button class="results-save" name="t" value="image">Apply settings</button> <button class="results-save" name="t" value="image">Apply settings</button>
</form> </form>
<div class="search-results" id="results"> <div class="search-results" id="results">
<!-- Results go here --> <!-- Results go here -->
{{ if .Results }} {{ if .Results }}
<div class="images images_viewer_hidden"> <div class="images images_viewer_hidden">
<!-- Images Grid --> <!-- Images Grid -->
{{ range .Results }} {{ range .Results }}
<div class="image"> <div class="image">
<a class="clickable" href="{{ .ThumbProxy }}" target="_blank"> <a class="clickable" href="{{ .ThumbProxy }}" target="_blank">
<img src="{{ .ThumbProxy }}" alt="{{ .Title }}" data-media="{{ .Media }}"> <img src="{{ .ThumbProxy }}" alt="{{ .Title }}" data-media="{{ .Media }}">
<div class="resolution">{{ .Width }} × {{ .Height }}</div> <div class="resolution">{{ .Width }} × {{ .Height }}</div>
<div class="details"> <div class="details">
<div class="img_title">{{ .Title }}</div> <div class="img_title">{{ .Title }}</div>
<div class="img_source"><a href="{{ .Source }}" target="_blank">Source</a></div> <div class="img_source"><a href="{{ .Source }}" target="_blank">Source</a></div>
</div> </div>
</a> </a>
</div> </div>
{{ end }} {{ end }}
</div> </div>
<noscript> <noscript>
<div class="prev-next prev-img"> <div class="prev-next prev-img">
<form action="/search" method="get"> <form action="/search" method="get">
<input type="hidden" name="q" value="{{ .Query }}"> <input type="hidden" name="q" value="{{ .Query }}">
<input type="hidden" name="t" value="image"> <input type="hidden" name="t" value="image">
{{ if .HasPrevPage }} {{ if .HasPrevPage }}
<!-- Subtract 1 from the current page for the Previous button --> <!-- Subtract 1 from the current page for the Previous button -->
<button type="submit" name="p" value="{{ sub .Page 1 }}">Previous</button> <button type="submit" name="p" value="{{ sub .Page 1 }}">Previous</button>
{{ end }} {{ end }}
{{ if .HasNextPage }} {{ if .HasNextPage }}
<!-- Add 1 to the current page for the Next button --> <!-- Add 1 to the current page for the Next button -->
<button type="submit" name="p" value="{{ add .Page 1 }}">Next</button> <button type="submit" name="p" value="{{ add .Page 1 }}">Next</button>
{{ end }} {{ end }}
</form> </form>
</div> </div>
</noscript> </noscript>
{{ else if .NoResults }} {{ else if .NoResults }}
<div class="no-results">No results found for '{{ .Query }}'. Try different keywords.</div> <div class="no-results">No results found for '{{ .Query }}'. Try different keywords.</div>
{{ else }} {{ else }}
<div class="no-more-results">Looks like this is the end of results.</div> <div class="no-more-results">Looks like this is the end of results.</div>
{{ end }} {{ end }}
</div> </div>
<div class="message-bottom-left" id="message-bottom-left"> <div class="message-bottom-left" id="message-bottom-left">
<span>Searching for new results...</span> <span>Searching for new results...</span>
</div> </div>
<script> <script>
// Check if JavaScript is enabled and modify the DOM accordingly // Check if JavaScript is enabled and modify the DOM accordingly
document.getElementById('content').classList.remove('js-enabled'); document.getElementById('content').classList.remove('js-enabled');
</script> </script>
<script> <script>
document.addEventListener("DOMContentLoaded", function() { document.addEventListener("DOMContentLoaded", function() {
let page = {{ .Page }}; let page = {{ .Page }};
const query = "{{ .Query }}"; const query = "{{ .Query }}";
let loading = false; let loading = false;
let hasMoreResults = true; let hasMoreResults = true;
const loadingIndicator = document.getElementById('message-bottom-left'); const loadingIndicator = document.getElementById('message-bottom-left');
let loadingTimeout; let loadingTimeout;
function loadResults(newPage) { function loadResults(newPage) {
if (loading || !hasMoreResults) return; if (loading || !hasMoreResults) return;
loading = true; loading = true;
// Show loading indicator if taking more than 100ms // Show loading indicator if taking more than 100ms
loadingTimeout = setTimeout(() => { loadingTimeout = setTimeout(() => {
loadingIndicator.style.display = 'flex'; loadingIndicator.style.display = 'flex';
}, 100); }, 100);
fetch(`/search?q=${encodeURIComponent(query)}&t=image&p=${newPage}`) fetch(`/search?q=${encodeURIComponent(query)}&t=image&p=${newPage}`)
.then(response => { .then(response => {
if (!response.ok) { if (!response.ok) {
throw new Error('Network response was not ok'); throw new Error('Network response was not ok');
} }
return response.text(); return response.text();
}) })
.then(data => { .then(data => {
clearTimeout(loadingTimeout); clearTimeout(loadingTimeout);
loadingIndicator.style.display = 'none'; loadingIndicator.style.display = 'none';
const parser = new DOMParser(); const parser = new DOMParser();
const doc = parser.parseFromString(data, 'text/html'); const doc = parser.parseFromString(data, 'text/html');
const newResults = doc.getElementById('results').innerHTML; const newResults = doc.getElementById('results').innerHTML;
const noResultsMessage = "No results found for '{{ .Query }}'. Try different keywords."; const noResultsMessage = "No results found for '{{ .Query }}'. Try different keywords.";
const endOfResultsMessage = "Looks like this is the end of results."; const endOfResultsMessage = "Looks like this is the end of results.";
const serverError = "Internal Server Error"; const serverError = "Internal Server Error";
if (newResults.includes(noResultsMessage) || newResults.includes(endOfResultsMessage) || newResults.includes(serverError)) { if (newResults.includes(noResultsMessage) || newResults.includes(endOfResultsMessage) || newResults.includes(serverError)) {
document.getElementById('results').innerHTML += newResults; document.getElementById('results').innerHTML += newResults;
hasMoreResults = false; hasMoreResults = false;
} else { } else {
document.getElementById('results').innerHTML += newResults; document.getElementById('results').innerHTML += newResults;
page = newPage; page = newPage;
} }
loading = false; loading = false;
}) })
.catch(error => { .catch(error => {
clearTimeout(loadingTimeout); clearTimeout(loadingTimeout);
loadingIndicator.style.display = 'none'; loadingIndicator.style.display = 'none';
console.error('Error loading results:', error); console.error('Error loading results:', error);
hasMoreResults = false; // Stop further attempts hasMoreResults = false; // Stop further attempts
loading = false; loading = false;
}); });
} }
window.addEventListener('scroll', () => { window.addEventListener('scroll', () => {
if (window.innerHeight + window.scrollY >= document.body.offsetHeight) { if (window.innerHeight + window.scrollY >= document.body.offsetHeight) {
loadResults(page + 1); loadResults(page + 1);
} }
}); });
}); });
</script> </script>
</body> </body>
</html> </html>

168
templates/search.html Normal file → Executable file
View file

@ -1,73 +1,95 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Search with Ocásek</title> <title>Search with Ocásek</title>
<link rel="stylesheet" href="/static/css/style.css"> <link rel="stylesheet" href="/static/css/style.css">
<link rel="stylesheet" href="/static/css/{{.Theme}}.css"> <link rel="stylesheet" href="/static/css/{{.Theme}}.css">
<link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml"> <link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml">
</head> </head>
<body> <body>
<script> <script>
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
const settingsIcon = document.querySelector('.settings-icon-link-search'); // Define the updateSettings function first
const searchMenu = document.querySelector('.search-menu'); function updateSettings(settingKey, settingValue) {
const xhr = new XMLHttpRequest();
settingsIcon.addEventListener('click', function () { xhr.open('POST', '/updateSettings', true);
searchMenu.classList.toggle('settings-menu-hidden'); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
searchMenu.classList.toggle('settings-menu-visible'); xhr.onload = function () {
}); if (this.status >= 200 && this.status < 300) {
console.log('Settings updated successfully!');
// Theme change event listeners } else {
document.getElementById('dark_theme').addEventListener('click', function () { console.log('Failed to update settings.');
window.location.href = '/search?theme=dark'; }
}); };
document.getElementById('light_theme').addEventListener('click', function () { xhr.send(encodeURI(settingKey + '=' + settingValue));
window.location.href = '/search?theme=light'; }
});
}); const settingsIcon = document.querySelector('.settings-icon-link-search');
</script> const searchMenu = document.querySelector('.search-menu');
<div class="settings-search-div settings-search-div-search">
<button class="material-icons-round clickable settings-icon-link settings-icon-link-search">menu</button> settingsIcon.addEventListener('click', function () {
</div> searchMenu.classList.toggle('settings-menu-hidden');
<div class="search-menu settings-menu-hidden"> searchMenu.classList.toggle('settings-menu-visible');
<h2>Settings</h2> });
<div class="settings-content">
<button id="settingsButton" onclick="window.location.href='/settings'">All settings</button> <!-- Well its unessesary to use js here but this menu will not work without js anyway --> // Theme change event listeners
<div class="theme-settings"> document.getElementById('dark_theme').addEventListener('click', function () {
<p><span class="highlight">Theme: </span> <span id="theme_name">Default Theme</span></p> window.location.href = '/search?theme=dark';
<div class="themes-settings-menu"> });
<div><img class="view-image-search clickable" id="dark_theme" alt="Dark Theme" src="/static/images/dark.webp"></div> document.getElementById('light_theme').addEventListener('click', function () {
<div><img class="view-image-search clickable" id="light_theme" alt="Light Theme" src="/static/images/light.webp"></div> window.location.href = '/search?theme=light';
</div> });
</div>
<select class="lang" name="lang"> // Event listener for Safe Search Selection
<option value="en" selected>English</option> document.getElementById('safeSearchSelect').addEventListener('change', function () {
<option value="fr">Français</option> updateSettings('safe', this.value);
<option value="es">Español</option> });
<!-- Add other languages as needed -->
</select> // Event listener for Language Selection
<select class="domain" name="safe"> document.getElementById('languageSelect').addEventListener('change', function () {
<option value="active" selected>Safe search on</option> updateSettings('lang', this.value);
<option value="">Safe search off</option> });
<!-- Add other domains as needed --> });
</select> </script>
<div class="settings-search-div settings-search-div-search">
</div> <button class="material-icons-round clickable settings-icon-link settings-icon-link-search">menu</button>
</div> </div>
<form action="/search" class="search-container" method="post" autocomplete="off"> <div class="search-menu settings-menu-hidden">
<h1>Ocásek</h1> <h2>Settings</h2>
<div class="wrapper"> <div class="settings-content">
<input type="text" name="q" autofocus id="search-input" placeholder="Type to search..." /> <!-- <button id="settingsButton" onclick="window.location.href='/settings'">All settings</button> Well its unessesary to use js here but this menu will not work without js anyway -->
<button id="search-wrapper-ico" class="material-icons-round" name="t" value="text" type="submit">search</button> <div class="theme-settings">
<!-- <a id="clearSearch" class="material-icons-round">close</a> --> <p><span class="highlight">Current theme: </span> <span id="theme_name">{{.Theme}}</span></p>
</div> <div class="themes-settings-menu">
<div class="search-button-wrapper"> <div><img class="view-image-search clickable" id="dark_theme" alt="Dark Theme" src="/static/images/dark.webp"></div>
<input type="hidden" name="p" value="1"> <div><img class="view-image-search clickable" id="light_theme" alt="Light Theme" src="/static/images/light.webp"></div>
<button name="t" value="text" type="submit">Search Text</button> </div>
<button name="t" value="image" type="submit">Search Images</button> </div>
</div> <select class="lang" name="safe" id="safeSearchSelect">
</form> <option value="disabled" {{if eq .Safe "disabled"}}selected{{end}}>Safe Search Off</option>
</body> <option value="active" {{if eq .Safe "active"}}selected{{end}}>Safe Search On</option>
</html> </select>
<select class="lang" name="lang" id="languageSelect">
{{range .LanguageOptions}}
<option value="{{.Code}}" {{if eq .Code $.CurrentLang}}selected{{end}}>{{.Name}}</option>
{{end}}
</select>
</div>
</div>
<form action="/search" class="search-container" method="post" autocomplete="off">
<h1>Ocásek</h1>
<div class="wrapper">
<input type="text" name="q" autofocus id="search-input" placeholder="Type to search..." />
<button id="search-wrapper-ico" class="material-icons-round" name="t" value="text" type="submit">search</button>
<!-- <a id="clearSearch" class="material-icons-round">close</a> -->
</div>
<div class="search-button-wrapper">
<input type="hidden" name="p" value="1">
<button name="t" value="text" type="submit">Search Text</button>
<button name="t" value="image" type="submit">Search Images</button>
</div>
</form>
</body>
</html>

306
templates/text.html Normal file → Executable file
View file

@ -1,153 +1,153 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{.Query}} - Ocásek</title> <title>{{.Query}} - Ocásek</title>
<link rel="stylesheet" href="/static/css/style.css"> <link rel="stylesheet" href="/static/css/style.css">
<link rel="stylesheet" href="/static/css/{{.Theme}}.css"> <link rel="stylesheet" href="/static/css/{{.Theme}}.css">
<link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml"> <link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml">
</head> </head>
<body> <body>
<form action="/search" id="prev-next-form" class="results-search-container" method="GET" autocomplete="off"> <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> <h1 class="logomobile"><a class="no-decoration" href="./">Ocásek</a></h1>
<div class="wrapper-results"> <div class="wrapper-results">
<input type="text" name="q" value="{{ .Query }}" id="search-input" placeholder="Type to search..." /> <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="text">search</button> <button id="search-wrapper-ico" class="material-icons-round" name="t" value="text">search</button>
<input type="submit" class="hide" name="t" value="text" /> <input type="submit" class="hide" name="t" value="text" />
</div> </div>
<div class="sub-search-button-wrapper"> <div class="sub-search-button-wrapper">
<div class="search-container-results-btn"> <div class="search-container-results-btn">
<button id="sub-search-wrapper-ico" class="material-icons-round clickable search-active" name="t" value="text">search</button> <button id="sub-search-wrapper-ico" class="material-icons-round clickable search-active" name="t" value="text">search</button>
<button name="t" value="text" class="clickable search-active">Web</button> <button name="t" value="text" class="clickable search-active">Web</button>
</div> </div>
<div class="search-container-results-btn"> <div class="search-container-results-btn">
<button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="image">image</button> <button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="image">image</button>
<button name="t" value="image" class="clickable">Images</button> <button name="t" value="image" class="clickable">Images</button>
</div> </div>
<div class="search-container-results-btn"> <div class="search-container-results-btn">
<button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="video">movie</button> <button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="video">movie</button>
<button name="t" value="video" class="clickable">Videos</button> <button name="t" value="video" class="clickable">Videos</button>
</div> </div>
<div class="search-container-results-btn"> <div class="search-container-results-btn">
<button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="forum">forum</button> <button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="forum">forum</button>
<button name="t" value="forum" class="clickable">Forums</button> <button name="t" value="forum" class="clickable">Forums</button>
</div> </div>
<div id="content" class="js-enabled"> <div id="content" class="js-enabled">
<div class="search-container-results-btn"> <div class="search-container-results-btn">
<button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="map">map</button> <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> <button name="t" value="map" class="clickable">Maps</button>
</div> </div>
</div> </div>
<div class="search-container-results-btn"> <div class="search-container-results-btn">
<button id="sub-search-wrapper-ico" class="material-icons-round clickable" name="t" value="file">share</button> <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> <button name="t" value="file" class="clickable">Torrents</button>
</div> </div>
</div> </div>
</form> </form>
<form class="results_settings" action="/search" method="get"> <form class="results_settings" action="/search" method="get">
<input type="hidden" name="q" value="{{ .Query }}"> <input type="hidden" name="q" value="{{ .Query }}">
<select class="results-settings" name="safe" id="safeSearchSelect"> <select class="results-settings" name="safe" id="safeSearchSelect">
<option value="">Safe Search Off</option> <option value="disabled" {{if eq .Safe "disabled"}}selected{{end}}>Safe Search Off</option>
<option value="active">Safe Search On</option> <option value="active" {{if eq .Safe "active"}}selected{{end}}>Safe Search On</option>
</select> </select>
<select class="results-settings" name="lang" id="languageSelect"> <select class="results-settings" name="lang" id="languageSelect">
{{range .LanguageOptions}} {{range .LanguageOptions}}
<option value="{{.Code}}" {{if eq .Code $.CurrentLang}}selected{{end}}>{{.Name}}</option> <option value="{{.Code}}" {{if eq .Code $.CurrentLang}}selected{{end}}>{{.Name}}</option>
{{end}} {{end}}
</select> </select>
<button class="results-save" name="t" value="text">Apply settings</button> <button class="results-save" name="t" value="text">Apply settings</button>
</form> </form>
<div class="results" id="results"> <div class="results" id="results">
{{if .Results}} {{if .Results}}
{{range .Results}} {{range .Results}}
<div class="result_item"> <div class="result_item">
<a id="link" href="{{.URL}}">{{.URL}}</a> <a id="link" href="{{.URL}}">{{.URL}}</a>
<a href="{{.URL}}"><h3>{{.Header}}</h3></a> <a href="{{.URL}}"><h3>{{.Header}}</h3></a>
<p>{{.Description}}</p> <p>{{.Description}}</p>
</div> </div>
<br> <br>
{{end}} {{end}}
{{else if .NoResults}} {{else if .NoResults}}
<div class="no-results">No results found for '{{ .Query }}'. Try different keywords.</div> <div class="no-results">No results found for '{{ .Query }}'. Try different keywords.</div>
{{else}} {{else}}
<div class="no-more-results">Looks like this is the end of results.</div> <div class="no-more-results">Looks like this is the end of results.</div>
{{end}} {{end}}
</div> </div>
<div class="message-bottom-left" id="message-bottom-left"> <div class="message-bottom-left" id="message-bottom-left">
<span>Searching for new results...</span> <span>Searching for new results...</span>
</div> </div>
<div class="prev-next prev-img" id="prev-next"> <div class="prev-next prev-img" id="prev-next">
<form action="/search" method="get"> <form action="/search" method="get">
<input type="hidden" name="q" value="{{ .Query }}"> <input type="hidden" name="q" value="{{ .Query }}">
<input type="hidden" name="t" value="text"> <input type="hidden" name="t" value="text">
{{ if .HasPrevPage }} {{ if .HasPrevPage }}
<button type="submit" name="p" value="{{ sub .Page 1 }}">Previous</button> <button type="submit" name="p" value="{{ sub .Page 1 }}">Previous</button>
{{ end }} {{ end }}
{{ if .HasNextPage }} {{ if .HasNextPage }}
<button type="submit" name="p" value="{{ add .Page 1 }}">Next</button> <button type="submit" name="p" value="{{ add .Page 1 }}">Next</button>
{{ end }} {{ end }}
</form> </form>
</div> </div>
<script> <script>
// Check if JavaScript is enabled and modify the DOM accordingly // Check if JavaScript is enabled and modify the DOM accordingly
document.getElementById('content').classList.remove('js-enabled'); document.getElementById('content').classList.remove('js-enabled');
</script> </script>
<script> <script>
document.addEventListener("DOMContentLoaded", function() { document.addEventListener("DOMContentLoaded", function() {
if (document.getElementById('prev-next')) { if (document.getElementById('prev-next')) {
document.getElementById('prev-next').style.display = 'none'; document.getElementById('prev-next').style.display = 'none';
let page = {{ .Page }}; let page = {{ .Page }};
const query = "{{ .Query }}"; const query = "{{ .Query }}";
let loading = false; let loading = false;
let hasMoreResults = true; let hasMoreResults = true;
const loadingIndicator = document.getElementById('message-bottom-left'); const loadingIndicator = document.getElementById('message-bottom-left');
let loadingTimeout; let loadingTimeout;
function loadResults(newPage) { function loadResults(newPage) {
if (loading || !hasMoreResults) return; if (loading || !hasMoreResults) return;
loading = true; loading = true;
// Show loading indicator if taking more than 100ms // Show loading indicator if taking more than 100ms
loadingTimeout = setTimeout(() => { loadingTimeout = setTimeout(() => {
loadingIndicator.style.display = 'flex'; loadingIndicator.style.display = 'flex';
}, 100); }, 100);
fetch(`/search?q=${encodeURIComponent(query)}&t=text&p=${newPage}`) fetch(`/search?q=${encodeURIComponent(query)}&t=text&p=${newPage}`)
.then(response => response.text()) .then(response => response.text())
.then(data => { .then(data => {
clearTimeout(loadingTimeout); clearTimeout(loadingTimeout);
loadingIndicator.style.display = 'none'; loadingIndicator.style.display = 'none';
const parser = new DOMParser(); const parser = new DOMParser();
const doc = parser.parseFromString(data, 'text/html'); const doc = parser.parseFromString(data, 'text/html');
const newResults = doc.getElementById('results').innerHTML; const newResults = doc.getElementById('results').innerHTML;
const noResultsMessage = "No results found for '{{ .Query }}'. Try different keywords."; const noResultsMessage = "No results found for '{{ .Query }}'. Try different keywords.";
if (newResults.includes(noResultsMessage)) { if (newResults.includes(noResultsMessage)) {
document.getElementById('results').innerHTML += "<div class='no-more-results'>Looks like this is the end of results.</div>"; document.getElementById('results').innerHTML += "<div class='no-more-results'>Looks like this is the end of results.</div>";
hasMoreResults = false; hasMoreResults = false;
} else { } else {
document.getElementById('results').innerHTML += newResults; document.getElementById('results').innerHTML += newResults;
page = newPage; page = newPage;
} }
loading = false; loading = false;
}) })
.catch(error => { .catch(error => {
clearTimeout(loadingTimeout); clearTimeout(loadingTimeout);
loadingIndicator.style.display = 'none'; loadingIndicator.style.display = 'none';
console.error('Error loading results:', error); console.error('Error loading results:', error);
loading = false; loading = false;
}); });
} }
window.addEventListener('scroll', () => { window.addEventListener('scroll', () => {
if (window.innerHeight + window.scrollY >= document.body.offsetHeight) { if (window.innerHeight + window.scrollY >= document.body.offsetHeight) {
loadResults(page + 1); loadResults(page + 1);
} }
}); });
} }
}); });
</script> </script>
</body> </body>
</html> </html>

10
text.go
View file

@ -48,23 +48,25 @@ func HandleTextSearch(w http.ResponseWriter, settings UserSettings, query string
Query string Query string
Page int Page int
Fetched string Fetched string
LanguageOptions []LanguageOption
CurrentLang string
HasPrevPage bool HasPrevPage bool
HasNextPage bool HasNextPage bool
NoResults bool NoResults bool
LanguageOptions []LanguageOption
CurrentLang string
Theme string Theme string
Safe string
}{ }{
Results: combinedResults, Results: combinedResults,
Query: query, Query: query,
Page: page, Page: page,
Fetched: fmt.Sprintf("%.2f seconds", elapsedTime.Seconds()), Fetched: fmt.Sprintf("%.2f seconds", elapsedTime.Seconds()),
LanguageOptions: languageOptions,
CurrentLang: settings.Language,
HasPrevPage: page > 1, HasPrevPage: page > 1,
HasNextPage: len(combinedResults) >= 50, HasNextPage: len(combinedResults) >= 50,
NoResults: len(combinedResults) == 0, NoResults: len(combinedResults) == 0,
LanguageOptions: languageOptions,
CurrentLang: settings.Language,
Theme: settings.Theme, Theme: settings.Theme,
Safe: settings.SafeSearch,
} }
err = tmpl.Execute(w, data) err = tmpl.Execute(w, data)

114
user-settings.go Normal file → Executable file
View file

@ -1,54 +1,60 @@
package main package main
import "net/http" import "net/http"
type UserSettings struct { type UserSettings struct {
Theme string Theme string
Language string Language string
SafeSearch string SafeSearch string
} }
func loadUserSettings(r *http.Request) UserSettings { func loadUserSettings(r *http.Request) UserSettings {
var settings UserSettings var settings UserSettings
// Load theme // Load theme
if cookie, err := r.Cookie("theme"); err == nil { if cookie, err := r.Cookie("theme"); err == nil {
settings.Theme = cookie.Value settings.Theme = cookie.Value
} else { } else {
settings.Theme = "dark" // Default theme settings.Theme = "dark" // Default theme
} }
// Load language // Load language
if cookie, err := r.Cookie("language"); err == nil { if cookie, err := r.Cookie("language"); err == nil {
settings.Language = cookie.Value settings.Language = cookie.Value
} else { } else {
settings.Language = "en" // Default language settings.Language = "en" // Default language
} }
// Load safe search // Load safe search
if cookie, err := r.Cookie("safe"); err == nil { if cookie, err := r.Cookie("safe"); err == nil {
settings.SafeSearch = cookie.Value settings.SafeSearch = cookie.Value
} else { } else {
settings.SafeSearch = "" // Default safe search off settings.SafeSearch = "" // Default safe search off
} }
return settings return settings
} }
func saveUserSettings(w http.ResponseWriter, settings UserSettings) { func saveUserSettings(w http.ResponseWriter, settings UserSettings) {
http.SetCookie(w, &http.Cookie{ http.SetCookie(w, &http.Cookie{
Name: "theme", Name: "theme",
Value: settings.Theme, Value: settings.Theme,
Path: "/", Path: "/",
}) Secure: true, // Ensure cookie is sent over HTTPS only
http.SetCookie(w, &http.Cookie{ SameSite: http.SameSiteNoneMode, // Set SameSite to None
Name: "language", })
Value: settings.Language, http.SetCookie(w, &http.Cookie{
Path: "/", Name: "language",
}) Value: settings.Language,
http.SetCookie(w, &http.Cookie{ Path: "/",
Name: "safe", Secure: true, // Ensure cookie is sent over HTTPS only
Value: settings.SafeSearch, SameSite: http.SameSiteNoneMode, // Set SameSite to None
Path: "/", })
}) http.SetCookie(w, &http.Cookie{
} Name: "safe",
Value: settings.SafeSearch,
Path: "/",
Secure: true, // Ensure cookie is sent over HTTPS only
SameSite: http.SameSiteNoneMode, // Set SameSite to None
})
}

View file

@ -166,13 +166,16 @@ func handleVideoSearch(w http.ResponseWriter, settings UserSettings, query strin
} }
err = tmpl.Execute(w, map[string]interface{}{ err = tmpl.Execute(w, map[string]interface{}{
"Results": results, "Results": results,
"Query": query, "Query": query,
"Fetched": fmt.Sprintf("%.2f seconds", elapsed.Seconds()), "Fetched": fmt.Sprintf("%.2f seconds", elapsed.Seconds()),
"Page": page, "Page": page,
"HasPrevPage": page > 1, "HasPrevPage": page > 1,
"HasNextPage": len(results) > 0, // no "HasNextPage": len(results) > 0,
"Theme": settings.Theme, "LanguageOptions": languageOptions,
"CurrentLang": settings.Language,
"Theme": settings.Theme,
"Safe": settings.SafeSearch,
}) })
if err != nil { if err != nil {
printErr("Error executing template: %v", err) printErr("Error executing template: %v", err)