Added header template for autocomplete on results view
New header template adds full control over search field on desktop and mobile, which now allows for autocomplete suggestions on the results page Also fixed autocomplete results format, since opensearch requires a suggestions response of [<original query>, [<suggestion array>]]
This commit is contained in:
parent
6c31a3eef8
commit
137b0ef8db
|
@ -91,9 +91,13 @@ class Filter:
|
|||
script.decompose()
|
||||
|
||||
footer = soup.find('div', id='sfooter')
|
||||
if footer is not None:
|
||||
if footer:
|
||||
footer.decompose()
|
||||
|
||||
header = soup.find('header')
|
||||
if header:
|
||||
header.decompose()
|
||||
|
||||
return soup
|
||||
|
||||
def remove_ads(self, soup):
|
||||
|
|
|
@ -15,9 +15,7 @@ DESKTOP_UA = '{}/5.0 (X11; {} x86_64; rv:75.0) Gecko/20100101 {}/75.0'
|
|||
VALID_PARAMS = ['tbs', 'tbm', 'start', 'near']
|
||||
|
||||
|
||||
def gen_user_agent(normal_ua):
|
||||
is_mobile = 'Android' in normal_ua or 'iPhone' in normal_ua
|
||||
|
||||
def gen_user_agent(normal_ua, is_mobile):
|
||||
mozilla = random.choice(['Moo', 'Woah', 'Bro', 'Slow']) + 'zilla'
|
||||
firefox = random.choice(['Choir', 'Squier', 'Higher', 'Wire']) + 'fox'
|
||||
linux = random.choice(['Win', 'Sin', 'Gin', 'Fin', 'Kin']) + 'ux'
|
||||
|
@ -66,8 +64,9 @@ def gen_query(query, args, config, near_city=None):
|
|||
|
||||
class Request:
|
||||
def __init__(self, normal_ua, language='lang_en'):
|
||||
self.modified_user_agent = gen_user_agent(normal_ua)
|
||||
self.language = language
|
||||
self.mobile = 'Android' in normal_ua or 'iPhone' in normal_ua
|
||||
self.modified_user_agent = gen_user_agent(normal_ua, self.mobile)
|
||||
|
||||
def __getitem__(self, name):
|
||||
return getattr(self, name)
|
||||
|
|
|
@ -96,7 +96,7 @@ def autocomplete():
|
|||
if not q:
|
||||
return jsonify({'results': []})
|
||||
|
||||
return jsonify({'results': g.user_request.autocomplete(q)})
|
||||
return jsonify([q, g.user_request.autocomplete(q)])
|
||||
|
||||
|
||||
@app.route('/search', methods=['GET', 'POST'])
|
||||
|
@ -132,7 +132,14 @@ def search():
|
|||
else:
|
||||
formatted_results = content_filter.clean(dirty_soup)
|
||||
|
||||
return render_template('display.html', query=urlparse.unquote(q), response=formatted_results)
|
||||
return render_template(
|
||||
'display.html',
|
||||
query=urlparse.unquote(q),
|
||||
response=formatted_results,
|
||||
search_header=render_template(
|
||||
'header.html',
|
||||
q=urlparse.unquote(q),
|
||||
mobile=g.user_request.mobile))
|
||||
|
||||
|
||||
@app.route('/config', methods=['GET', 'POST'])
|
||||
|
|
55
app/static/css/header.css
Normal file
55
app/static/css/header.css
Normal file
|
@ -0,0 +1,55 @@
|
|||
header {
|
||||
font-family: Roboto,HelveticaNeue,Arial,sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #3C4043;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.logo-link, .logo-letter {
|
||||
text-decoration: none !important;
|
||||
letter-spacing: -1px;
|
||||
text-align: center;
|
||||
border-radius: 2px 0 0 0;
|
||||
}
|
||||
|
||||
.mobile-logo {
|
||||
font: 22px/36px Futura, Arial, sans-serif;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.logo-div {
|
||||
letter-spacing: -1px;
|
||||
text-align: center;
|
||||
font: 22pt Futura, Arial, sans-serif;
|
||||
padding: 10px 0 5px 0;
|
||||
height: 37px;
|
||||
font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.search-div {
|
||||
border-radius: 8px 8px 0 0;
|
||||
box-shadow: 0 1px 6px rgba(32, 33, 36, 0.18);
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
height: 39px;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
background: none;
|
||||
margin: 2px 4px 2px 8px;
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
padding: 0 0 0 8px;
|
||||
flex: 1;
|
||||
height: 35px;
|
||||
outline: none;
|
||||
border: none;
|
||||
width: 100%;
|
||||
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
||||
overflow: hidden;
|
||||
}
|
|
@ -1,11 +1,29 @@
|
|||
function autocomplete(searchInput, autocompleteResults) {
|
||||
const handleUserInput = searchBar => {
|
||||
let xhrRequest = new XMLHttpRequest();
|
||||
xhrRequest.open("POST", "/autocomplete");
|
||||
xhrRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||
xhrRequest.onload = function() {
|
||||
if (xhrRequest.readyState === 4 && xhrRequest.status !== 200) {
|
||||
alert("Error fetching autocomplete results");
|
||||
return;
|
||||
}
|
||||
|
||||
// Fill autocomplete with fetched results
|
||||
let autocompleteResults = JSON.parse(xhrRequest.responseText);
|
||||
autocomplete(searchBar, autocompleteResults[1]);
|
||||
};
|
||||
|
||||
xhrRequest.send('q=' + searchBar.value);
|
||||
};
|
||||
|
||||
const autocomplete = (searchInput, autocompleteResults) => {
|
||||
let currentFocus;
|
||||
|
||||
searchInput.addEventListener("input", function () {
|
||||
let autocompleteList, autocompleteItem, i, val = this.value;
|
||||
closeAllLists();
|
||||
|
||||
if (!val) {
|
||||
if (!val || !autocompleteResults) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -47,7 +65,7 @@ function autocomplete(searchInput, autocompleteResults) {
|
|||
}
|
||||
});
|
||||
|
||||
function addActive(suggestion) {
|
||||
const addActive = suggestion => {
|
||||
if (!suggestion || !suggestion[currentFocus]) return false;
|
||||
removeActive(suggestion);
|
||||
|
||||
|
@ -55,25 +73,26 @@ function autocomplete(searchInput, autocompleteResults) {
|
|||
if (currentFocus < 0) currentFocus = (suggestion.length - 1);
|
||||
|
||||
suggestion[currentFocus].classList.add("autocomplete-active");
|
||||
}
|
||||
};
|
||||
|
||||
function removeActive(suggestion) {
|
||||
const removeActive = suggestion => {
|
||||
for (let i = 0; i < suggestion.length; i++) {
|
||||
suggestion[i].classList.remove("autocomplete-active");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function closeAllLists(el) {
|
||||
const closeAllLists = el => {
|
||||
let suggestions = document.getElementsByClassName("autocomplete-items");
|
||||
for (let i = 0; i < suggestions.length; i++) {
|
||||
if (el !== suggestions[i] && el !== searchInput) {
|
||||
suggestions[i].parentNode.removeChild(suggestions[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Close lists and search when user selects a suggestion
|
||||
document.addEventListener("click", function (e) {
|
||||
closeAllLists(e.target);
|
||||
document.getElementById("search-form").submit();
|
||||
});
|
||||
}
|
||||
};
|
|
@ -1,21 +1,3 @@
|
|||
const handleUserInput = searchBar => {
|
||||
let xhrRequest = new XMLHttpRequest();
|
||||
xhrRequest.open("POST", "/autocomplete");
|
||||
xhrRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||
xhrRequest.onload = function() {
|
||||
if (xhrRequest.readyState === 4 && xhrRequest.status !== 200) {
|
||||
alert("Error fetching autocomplete results");
|
||||
return;
|
||||
}
|
||||
|
||||
// Fill autocomplete with fetched results
|
||||
let autocompleteResults = JSON.parse(xhrRequest.responseText);
|
||||
autocomplete(searchBar, autocompleteResults["results"]);
|
||||
};
|
||||
|
||||
xhrRequest.send('q=' + searchBar.value);
|
||||
};
|
||||
|
||||
const setupSearchLayout = () => {
|
||||
// Setup search field
|
||||
const searchBar = document.getElementById("search-bar");
|
||||
|
|
|
@ -5,9 +5,13 @@
|
|||
<link rel="search" href="/opensearch.xml" type="application/opensearchdescription+xml" title="Whoogle Search">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="referrer" content="no-referrer">
|
||||
<script type="text/javascript" src="/static/js/autocomplete.js"></script>
|
||||
<link rel="stylesheet" href="/static/css/search.css">
|
||||
<link rel="stylesheet" href="/static/css/header.css">
|
||||
<title>{{ query }} - Whoogle Search</title>
|
||||
</head>
|
||||
<body>
|
||||
{{ search_header|safe }}
|
||||
{{ response|safe }}
|
||||
</body>
|
||||
</html>
|
||||
|
|
53
app/templates/header.html
Normal file
53
app/templates/header.html
Normal file
|
@ -0,0 +1,53 @@
|
|||
{% if mobile %}
|
||||
<header>
|
||||
<div class="bz1lBb">
|
||||
<form class="Pg70bf" id="search-form" method="POST">
|
||||
<a class="logo-link mobile-logo"
|
||||
href="/"
|
||||
style="display:flex; justify-content:center; align-items:center; color:#685e79; font-size:18px; ">
|
||||
<span class="V6gwVd">Wh</span><span class="iWkuvd">o</span><span class="cDrQ7">o</span><span
|
||||
class="V6gwVd">g</span><span class="ntlR9">l</span><span
|
||||
class="iWkuvd tJ3Myc">e</span>
|
||||
</a>
|
||||
<div class="H0PQec" style="width: 100%;">
|
||||
<div class="sbc esbc autocomplete">
|
||||
<input id="search-bar" autocapitalize="none" autocomplete="off" class="noHIxc" name="q"
|
||||
spellcheck="false" type="text" value="{{ q }}">
|
||||
<div class="sc"></div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</header>
|
||||
{% else %}
|
||||
<header>
|
||||
<div class="logo-div">
|
||||
<a class="logo-link" href="/">
|
||||
<span class="V6gwVd logo-letter">Wh</span><span class="iWkuvd logo-letter">o</span><span
|
||||
class="cDrQ7 logo-letter">o</span><span class="V6gwVd logo-letter">g</span><span
|
||||
class="ntlR9 logo-letter">l</span><span class="iWkuvd tJ3Myc logo-letter">e</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="search-div">
|
||||
<form id="search-form" class="search-form" id="sf" method="POST">
|
||||
<div class="autocomplete" style="width: 100%; flex: 1">
|
||||
<div style="width: 100%; display: flex">
|
||||
<input id="search-bar" autocapitalize="none" autocomplete="off" class="noHIxc" name="q"
|
||||
spellcheck="false" type="text" value="{{ q }}">
|
||||
<div class="sc"></div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</header>
|
||||
{% endif %}
|
||||
|
||||
<script>
|
||||
const searchBar = document.getElementById("search-bar");
|
||||
|
||||
searchBar.addEventListener("keyup", function (event) {
|
||||
if (event.keyCode !== 13) {
|
||||
handleUserInput(searchBar);
|
||||
}
|
||||
});
|
||||
</script>
|
|
@ -28,7 +28,7 @@
|
|||
<body id="main" style="display: none; background-color: {{ bg }}">
|
||||
<div class="search-container">
|
||||
<img class="logo" src="/static/img/logo.png">
|
||||
<form action="/search" method="{{ request_type }}">
|
||||
<form id="search-form" action="/search" method="{{ request_type }}">
|
||||
<div class="search-fields">
|
||||
<div class="autocomplete">
|
||||
<input type="text" name="q" id="search-bar" autofocus="autofocus">
|
||||
|
|
Loading…
Reference in New Issue
Block a user