Skip to content
This repository was archived by the owner on Apr 8, 2025. It is now read-only.

Commit 5880a64

Browse files
authored
Remove usage of jquery (#115)
There are still more code that depends on jquery and underscorejs, but this is a good start. - Use the fetch API instead of jquery's ajax. - Use the URL and URLSearchParams objects instead of our custom code to parse search parameters. - Had to update the tests, because the new version of selenium remove the `find_element_by*` methods (pytest-selenium installs the latest version by default). - Also updated pytest-selenium, which restricts the version of selenium to 4.x.
1 parent ec07503 commit 5880a64

File tree

6 files changed

+192
-219
lines changed

6 files changed

+192
-219
lines changed

docs/js-api-reference.rst

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,8 @@ JavaScript API Reference
44
Following are the functions that are defined in `rtd_sphinx_search.js`_,
55

66
.. js:autofunction:: debounce
7-
.. js:autofunction:: convertObjToUrlParams
87
.. js:autofunction:: updateUrl
98
.. js:autofunction:: createDomNode
10-
.. js:autofunction:: _is_string
11-
.. js:autofunction:: _is_array
129
.. js:autofunction:: get_section_html
1310
.. js:autofunction:: get_domain_html
1411
.. js:autofunction:: generateSingleResult

sphinx_search/static/js/rtd_sphinx_search.js

Lines changed: 55 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const MAX_SUBSTRING_LIMIT = 100;
44
const ANIMATION_TIME = 200;
55
const FETCH_RESULTS_DELAY = 250;
66
const CLEAR_RESULTS_DELAY = 300;
7+
const RTD_SEARCH_PARAMETER = "rtd_search";
78

89
/**
910
* Debounce the function.
@@ -42,7 +43,7 @@ const debounce = (func, wait) => {
4243

4344
/**
4445
* Wrapper around underscorejs's template function.
45-
*
46+
*
4647
* This is to make it work with new and old versions.
4748
*/
4849
const render_template = (template, data) => {
@@ -55,69 +56,21 @@ const render_template = (template, data) => {
5556
return result;
5657
};
5758

58-
/**
59-
* Take an object as parameter and convert it to
60-
* url params string.
61-
*
62-
* Eg. if ``obj = { 'a': 1, 'b': 2, 'c': ['hello', 'world'] }``, then it will return
63-
* the string ``a=1&b=2&c=hello,world``
64-
*
65-
* @param {Object} obj the object to be converted
66-
* @return {String|Array} object in url params form
67-
*/
68-
const convertObjToUrlParams = obj => {
69-
let params = Object.keys(obj).map(function(key) {
70-
if (_is_string(key)) {
71-
const s = key + "=" + encodeURI(obj[key]);
72-
return s;
73-
}
74-
});
75-
76-
// removing empty strings from the 'params' array
77-
let final_params = [];
78-
for (let i = 0; i < params.length; ++i) {
79-
if (_is_string(params[i])) {
80-
final_params.push(params[i]);
81-
}
82-
}
83-
if (final_params.length === 1) {
84-
return final_params[0];
85-
} else {
86-
let final_url_params = final_params.join("&");
87-
return final_url_params;
88-
}
89-
};
90-
9159

9260
/**
9361
* Adds/removes "rtd_search" url parameter to the url.
9462
*/
9563
const updateUrl = () => {
96-
let origin = window.location.origin;
97-
let path = window.location.pathname;
98-
let url_params = $.getQueryParameters();
99-
let hash = window.location.hash;
64+
let parsed_url = new URL(window.location.href);
10065
let search_query = getSearchTerm();
101-
// search_query should not be an empty string
66+
// search_query should not be an empty string.
10267
if (search_query.length > 0) {
103-
url_params.rtd_search = search_query;
68+
parsed_url.searchParams.set(RTD_SEARCH_PARAMETER, search_query);
10469
} else {
105-
delete url_params.rtd_search;
70+
parsed_url.searchParams.delete(RTD_SEARCH_PARAMETER);
10671
}
107-
108-
let window_location_search = convertObjToUrlParams(url_params) + hash;
109-
110-
// this happens during the tests,
111-
// when window.location.origin is "null" in Firefox
112-
// then correct URL is contained by window.location.pathname
113-
// which starts with "file://"
114-
let url = path + "?" + window_location_search;
115-
if (origin.substring(0, 4) === "http") {
116-
url = origin + url;
117-
}
118-
119-
// update url
120-
window.history.pushState({}, null, url);
72+
// Update url.
73+
window.history.pushState({}, null, parsed_url.toString());
12174
};
12275

12376

@@ -172,18 +125,6 @@ const _is_string = str => {
172125
}
173126
};
174127

175-
/**
176-
* Checks if data type is a non-empty array
177-
* @param {*} data data whose type is to be checked
178-
* @return {Boolean} returns true if data is non-empty array, else returns false
179-
*/
180-
const _is_array = arr => {
181-
if (Array.isArray(arr) && arr.length > 0) {
182-
return true;
183-
} else {
184-
return false;
185-
}
186-
};
187128

188129
/**
189130
* Generate and return html structure
@@ -538,11 +479,12 @@ const getErrorDiv = err_msg => {
538479
* and appends the results to <div class="search__outer"> node,
539480
* which is already created when the page was loaded.
540481
*
541-
* @param {String} search_url url on which request will be sent
542-
* @param {String} projectName name (slug) of the project
482+
* @param {String} api_endpoint: API endpoint
483+
* @param {Object} parameters: search parameters
484+
* @param {String} projectName: name (slug) of the project
543485
* @return {Function} debounced function with debounce time of 500ms
544486
*/
545-
const fetchAndGenerateResults = (search_url, projectName) => {
487+
const fetchAndGenerateResults = (api_endpoint, parameters, projectName) => {
546488
let search_outer = document.querySelector(".search__outer");
547489

548490
// Removes all results (if there is any),
@@ -553,52 +495,48 @@ const fetchAndGenerateResults = (search_url, projectName) => {
553495
search_loding.innerHTML = "<strong>Searching ....</strong>";
554496
search_outer.appendChild(search_loding);
555497

556-
let ajaxFunc = () => {
498+
let fetchFunc = () => {
557499
// Update URL just before fetching the results
558500
updateUrl();
559501
updateSearchBar();
560502

561-
$.ajax({
562-
url: search_url,
563-
crossDomain: true,
564-
xhrFields: {
565-
withCredentials: true
566-
},
567-
complete: (resp, status_code) => {
568-
if (
569-
status_code === "success" ||
570-
typeof resp.responseJSON !== "undefined"
571-
) {
572-
if (resp.responseJSON.results.length > 0) {
573-
let search_result_box = generateSuggestionsList(
574-
resp.responseJSON,
575-
projectName
576-
);
577-
removeResults();
578-
search_outer.appendChild(search_result_box);
579-
580-
// remove active classes from all suggestions
581-
// if the mouse hovers, otherwise styles from
582-
// :hover and .active will clash.
583-
search_outer.addEventListener("mouseenter", e => {
584-
removeAllActive();
585-
});
586-
} else {
587-
removeResults();
588-
let err_div = getErrorDiv("No results found");
589-
search_outer.appendChild(err_div);
590-
}
591-
}
592-
},
593-
error: (resp, status_code, error) => {
503+
const url = api_endpoint + "?" + new URLSearchParams(parameters).toString();
504+
505+
fetch(url, {method: "GET"})
506+
.then(response => {
507+
if (!response.ok) {
508+
throw new Error();
509+
}
510+
return response.json();
511+
})
512+
.then(data => {
513+
if (data.results.length > 0) {
514+
let search_result_box = generateSuggestionsList(
515+
data,
516+
projectName
517+
);
518+
removeResults();
519+
search_outer.appendChild(search_result_box);
520+
521+
// remove active classes from all suggestions
522+
// if the mouse hovers, otherwise styles from
523+
// :hover and .active will clash.
524+
search_outer.addEventListener("mouseenter", e => {
525+
removeAllActive();
526+
});
527+
} else {
594528
removeResults();
595-
let err_div = getErrorDiv("There was an error. Please try again.");
529+
let err_div = getErrorDiv("No results found");
596530
search_outer.appendChild(err_div);
597531
}
532+
})
533+
.catch(error => {
534+
removeResults();
535+
let err_div = getErrorDiv("There was an error. Please try again.");
536+
search_outer.appendChild(err_div);
598537
});
599538
};
600-
ajaxFunc = debounce(ajaxFunc, FETCH_RESULTS_DELAY);
601-
return ajaxFunc;
539+
return debounce(fetchFunc, FETCH_RESULTS_DELAY);
602540
};
603541

604542
/**
@@ -696,7 +634,6 @@ window.addEventListener("DOMContentLoaded", () => {
696634
if (window.hasOwnProperty("READTHEDOCS_DATA")) {
697635
const project = READTHEDOCS_DATA.project;
698636
const version = READTHEDOCS_DATA.version;
699-
const language = READTHEDOCS_DATA.language || "en";
700637
const api_host = READTHEDOCS_DATA.proxied_api_host || '/_';
701638

702639
let initialHtml = generateAndReturnInitialHtml();
@@ -720,25 +657,18 @@ window.addEventListener("DOMContentLoaded", () => {
720657

721658
search_outer_input.addEventListener("input", e => {
722659
let search_query = getSearchTerm();
723-
724-
let search_params = {
725-
q: search_query,
726-
project: project,
727-
version: version,
728-
language: language,
729-
};
730-
731-
const search_url =
732-
api_host +
733-
"/api/v2/search/?" +
734-
convertObjToUrlParams(search_params);
735-
736660
if (search_query.length > 0) {
737661
if (current_request !== null) {
738662
// cancel previous ajax request.
739663
current_request.cancel();
740664
}
741-
current_request = fetchAndGenerateResults(search_url, project);
665+
const search_endpoint = api_host + "/api/v2/search/";
666+
const search_params = {
667+
q: search_query,
668+
project: project,
669+
version: version,
670+
};
671+
current_request = fetchAndGenerateResults(search_endpoint, search_params, project);
742672
current_request();
743673
} else {
744674
// if the last request returns the results,
@@ -825,9 +755,9 @@ window.addEventListener("DOMContentLoaded", () => {
825755
// if "rtd_search" is present in URL parameters,
826756
// then open the search modal and show the results
827757
// for the value of "rtd_search"
828-
let url_params = $.getQueryParameters();
829-
if (_is_array(url_params.rtd_search)) {
830-
let query = decodeURIComponent(url_params.rtd_search);
758+
const url_params = new URLSearchParams(document.location.search);
759+
const query = url_params.get(RTD_SEARCH_PARAMETER);
760+
if (query !== null) {
831761
showSearchModal(query);
832762
search_outer_input.value = query;
833763

0 commit comments

Comments
 (0)