From 137b0ef8dbdf4eb919101c17b29b415db1cba273 Mon Sep 17 00:00:00 2001 From: Ben Busby <33362396+benbusby@users.noreply.github.com> Date: Sat, 23 May 2020 17:20:33 -0600 Subject: [PATCH] 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 [, []] --- app/filter.py | 6 +++- app/request.py | 7 ++--- app/routes.py | 11 +++++-- app/static/css/header.css | 55 +++++++++++++++++++++++++++++++++++ app/static/js/autocomplete.js | 37 +++++++++++++++++------ app/static/js/controller.js | 18 ------------ app/templates/display.html | 4 +++ app/templates/header.html | 53 +++++++++++++++++++++++++++++++++ app/templates/index.html | 2 +- 9 files changed, 158 insertions(+), 35 deletions(-) create mode 100644 app/static/css/header.css create mode 100644 app/templates/header.html diff --git a/app/filter.py b/app/filter.py index 5e5ec01..8a30b12 100644 --- a/app/filter.py +++ b/app/filter.py @@ -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): diff --git a/app/request.py b/app/request.py index 2b2e9ea..7ecd887 100644 --- a/app/request.py +++ b/app/request.py @@ -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) diff --git a/app/routes.py b/app/routes.py index 348a352..bc6e01c 100644 --- a/app/routes.py +++ b/app/routes.py @@ -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']) diff --git a/app/static/css/header.css b/app/static/css/header.css new file mode 100644 index 0000000..c3eebf8 --- /dev/null +++ b/app/static/css/header.css @@ -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; +} \ No newline at end of file diff --git a/app/static/js/autocomplete.js b/app/static/js/autocomplete.js index f65dd77..c5e2774 100644 --- a/app/static/js/autocomplete.js +++ b/app/static/js/autocomplete.js @@ -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(); }); -} \ No newline at end of file +}; \ No newline at end of file diff --git a/app/static/js/controller.js b/app/static/js/controller.js index 6dbfc66..4817195 100644 --- a/app/static/js/controller.js +++ b/app/static/js/controller.js @@ -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"); diff --git a/app/templates/display.html b/app/templates/display.html index 03ed151..e66de6f 100644 --- a/app/templates/display.html +++ b/app/templates/display.html @@ -5,9 +5,13 @@ + + + {{ query }} - Whoogle Search + {{ search_header|safe }} {{ response|safe }} diff --git a/app/templates/header.html b/app/templates/header.html new file mode 100644 index 0000000..e106ffe --- /dev/null +++ b/app/templates/header.html @@ -0,0 +1,53 @@ +{% if mobile %} +
+
+
+ +
+
+ +
+
+
+
+
+
+{% else %} +
+ +
+
+
+
+ +
+
+
+
+
+
+{% endif %} + + \ No newline at end of file diff --git a/app/templates/index.html b/app/templates/index.html index aab6a19..ab46526 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -28,7 +28,7 @@
-
+