diff --git a/package.json b/package.json index b2c3d42..0b7734f 100644 --- a/package.json +++ b/package.json @@ -21,14 +21,14 @@ "@babel/preset-env": "^7.4.5", "browserslist": "^4.6.1", "del": "^4.1.1", + "github-changelog": "git+https://github.com/agjohnson/github-changelog.git", "gulp": "^3.9.1", "gulp-autoprefixer": "^6.1.0", "gulp-babel": "^8.0.0", "gulp-csso": "^3.0.1", "gulp-rename": "^1.4.0", "gulp-uglify": "^3.0.2", - "run-sequence": "^2.2.1", - "github-changelog": "git+https://github.com/agjohnson/github-changelog.git" + "run-sequence": "^2.2.1" }, "browserslist": [ "last 10 versions", diff --git a/sphinx_search/static/js/rtd_sphinx_search.js b/sphinx_search/static/js/rtd_sphinx_search.js index 46a5fb9..2440326 100644 --- a/sphinx_search/static/js/rtd_sphinx_search.js +++ b/sphinx_search/static/js/rtd_sphinx_search.js @@ -5,10 +5,6 @@ const ANIMATION_TIME = 200; const FETCH_RESULTS_DELAY = 250; const CLEAR_RESULTS_DELAY = 300; -// this is used to store the total result counts, -// which includes all the sections and domains of all the pages. -let COUNT = 0; - /** * Debounce the function. * Usage:: @@ -179,8 +175,9 @@ const _is_array = arr => { * * @param {Object} sectionData object containing the result data * @param {String} page_link link of the main page. It is used to construct the section link + * @param {Number} id to be used in for this section */ -const get_section_html = (sectionData, page_link) => { +const get_section_html = (sectionData, page_link, id) => { let section_template = ' \
\ @@ -220,7 +217,7 @@ const get_section_html = (sectionData, page_link) => { let section_link = `${page_link}#${sectionData.id}`; - let section_id = "hit__" + COUNT; + let section_id = "hit__" + id; let section_html = $u.template(section_template, { section_link: section_link, @@ -238,8 +235,9 @@ const get_section_html = (sectionData, page_link) => { * * @param {Object} domainData object containing the result data * @param {String} page_link link of the main page. It is used to construct the section link + * @param {Number} id to be used in for this section */ -const get_domain_html = (domainData, page_link) => { +const get_domain_html = (domainData, page_link, id) => { let domain_template = ' \
\ @@ -268,7 +266,7 @@ const get_domain_html = (domainData, page_link) => { domain_content = highlights.content[0]; } - let domain_id = "hit__" + COUNT; + let domain_id = "hit__" + id; domain_role_name = "[" + domain_role_name + "]"; let domain_html = $u.template(domain_template, { @@ -286,9 +284,11 @@ const get_domain_html = (domainData, page_link) => { * Generate search results for a single page. * * @param {Object} resultData search results of a page + * @param {String} projectName + * @param {Number} id from the last section * @return {Object} a
node with the results of a single page */ -const generateSingleResult = (resultData, projectName) => { +const generateSingleResult = (resultData, projectName, id) => { let content = createDomNode("div"); let page_link_template = @@ -330,18 +330,20 @@ const generateSingleResult = (resultData, projectName) => { for (let i = 0; i < resultData.blocks.length; ++i) { let block = resultData.blocks[i]; - COUNT += 1; let html_structure = ""; + id += 1; if (block.type === "section") { html_structure = get_section_html( block, - page_link + page_link, + id, ); } else if (block.type === "domain") { html_structure = get_domain_html( block, - page_link + page_link, + id, ); } content.innerHTML += html_structure; @@ -362,15 +364,18 @@ const generateSuggestionsList = (data, projectName) => { }); let max_results = Math.min(MAX_SUGGESTIONS, data.results.length); + let id = 0; for (let i = 0; i < max_results; ++i) { let search_result_single = createDomNode("div", { class: "search__result__single" }); - let content = generateSingleResult(data.results[i], projectName); + let content = generateSingleResult(data.results[i], projectName, id); search_result_single.appendChild(content); search_result_box.appendChild(search_result_single); + + id += data.results[i].blocks.length; } return search_result_box; }; @@ -379,7 +384,7 @@ const generateSuggestionsList = (data, projectName) => { * Removes .active class from all the suggestions. */ const removeAllActive = () => { - const results = document.querySelectorAll(".outer_div_page_results"); + const results = document.querySelectorAll(".outer_div_page_results.active"); const results_arr = Object.keys(results).map(i => results[i]); for (let i = 1; i <= results_arr.length; ++i) { results_arr[i - 1].classList.remove("active"); @@ -388,13 +393,12 @@ const removeAllActive = () => { /** * Add .active class to the search suggestion - * corresponding to serial number current_focus', - * and scroll to that suggestion smoothly. + * corresponding to `id`, and scroll to that suggestion smoothly. * - * @param {Number} current_focus serial no. of suggestions which will be active + * @param {Number} id of the suggestion to activate */ -const addActive = current_focus => { - const current_item = document.querySelector("#hit__" + current_focus); +const addActive = (id) => { + const current_item = document.querySelector("#hit__" + id); // in case of no results or any error, // current_item will not be found in the DOM. if (current_item !== null) { @@ -407,6 +411,51 @@ const addActive = current_focus => { } }; + +/* + * Select next/previous result. + * Go to the first result if already in the last result, + * or to the last result if already in the first result. + * + * @param {Boolean} forward. + */ +const selectNextResult = (forward) => { + const all = document.querySelectorAll(".outer_div_page_results"); + const current = document.querySelector(".outer_div_page_results.active"); + + let next_id = 1; + let last_id = 1; + + if (all.length > 0) { + let last = all[all.length - 1]; + if (last.id !== null) { + let match = last.id.match(/\d+/); + if (match !== null) { + last_id = Number(match[0]); + } + } + } + + if (current !== null && current.id !== null) { + let match = current.id.match(/\d+/); + if (match !== null) { + next_id = Number(match[0]); + next_id += forward? 1 : -1; + } + } + + // Cycle to the first or last result. + if (next_id <= 0) { + next_id = last_id; + } else if (next_id > last_id) { + next_id = 1; + } + + removeAllActive(); + addActive(next_id); +}; + + /** * Returns initial search input field, * which is already present in the docs. @@ -643,10 +692,6 @@ window.addEventListener("DOMContentLoaded", evt => { ); let cross_icon = document.querySelector(".search__cross"); - // this denotes the search suggestion which is currently selected - // via tha ArrowUp/ArrowDown keys. - let current_focus = 0; - // this stores the current request. let current_request = null; @@ -657,7 +702,6 @@ window.addEventListener("DOMContentLoaded", evt => { search_outer_input.addEventListener("input", e => { let search_query = getSearchTerm(); - COUNT = 0; let search_params = { q: search_query, @@ -696,23 +740,13 @@ window.addEventListener("DOMContentLoaded", evt => { // if "ArrowDown is pressed" if (e.keyCode === 40) { e.preventDefault(); - current_focus += 1; - if (current_focus > COUNT) { - current_focus = 1; - } - removeAllActive(); - addActive(current_focus); + selectNextResult(true); } // if "ArrowUp" is pressed. if (e.keyCode === 38) { e.preventDefault(); - current_focus -= 1; - if (current_focus < 1) { - current_focus = COUNT; - } - removeAllActive(); - addActive(current_focus); + selectNextResult(false); } // if "Enter" key is pressed. diff --git a/sphinx_search/static/js/rtd_sphinx_search.min.js b/sphinx_search/static/js/rtd_sphinx_search.min.js index c0e6f00..82d9b28 100644 --- a/sphinx_search/static/js/rtd_sphinx_search.min.js +++ b/sphinx_search/static/js/rtd_sphinx_search.min.js @@ -1 +1 @@ -"use strict";var MAX_SUGGESTIONS=50,MAX_SECTION_RESULTS=3,MAX_SUBSTRING_LIMIT=100,ANIMATION_TIME=200,FETCH_RESULTS_DELAY=250,CLEAR_RESULTS_DELAY=300,COUNT=0,debounce=function(r,n){function e(){var e=this,t=arguments;clearTimeout(a),a=setTimeout(function(){return r.apply(e,t)},n)}var a;return e.cancel=function(){clearTimeout(a),a=null},e},convertObjToUrlParams=function(t){for(var e=Object.keys(t).map(function(e){if(_is_string(e))return e+"="+encodeURI(t[e])}),r=[],n=0;n
<%= section_subheading %> <% for (var i = 0; i < section_content.length; ++i) { %>

<%= section_content[i] %>

<% } %>

',{section_link:i,section_id:c,section_subheading:r,section_content:a})},get_domain_html=function(e,t){var r="".concat(t,"#").concat(e.id),n=e.role,a=e.name,o=e.content.substr(0,MAX_SUBSTRING_LIMIT)+" ...",s=e.highlights;s.name.length&&(a=s.name[0]),s.content.length&&(o=s.content[0]);var i="hit__"+COUNT,n="["+n+"]";return $u.template('
<%= domain_subheading %>
<%= domain_role_name %>

<%= domain_content %>


',{domain_link:r,domain_id:i,domain_content:o,domain_subheading:a,domain_role_name:n})},generateSingleResult=function(e,t){var r=createDomNode("div"),n=e.path,a=e.title,o=e.highlights;o.title.length&&(a=o.title[0]),t!==e.project&&(a+=" "+$u.template(' (from project <%= project %>) ',{project:e.project})),a+="
",r.innerHTML+=$u.template('

<%= page_title %>

',{page_link:n,page_title:a});for(var s=0;s
\x3c!--?xml version="1.0" encoding="UTF-8"?--\x3e
Search by Read the Docs & readthedocs-sphinx-search
'},showSearchModal=function(t){removeResults();var r=getInputField();r.blur(),$(".search__outer__wrapper").fadeIn(ANIMATION_TIME,function(){var e=document.querySelector(".search__outer__input");null!==e&&(void 0!==t&&_is_string(t)?(e.value=t,r.value=t):e.value=r.value,e.focus())})},removeSearchModal=function(){removeResults(),updateSearchBar();var e=document.querySelector(".search__outer__input");null!==e&&(e.value="",e.blur()),updateUrl(),$(".search__outer__wrapper").fadeOut(ANIMATION_TIME)};window.addEventListener("DOMContentLoaded",function(e){var n,a,o,t,r,s,i,c,l,u,d,_,h;window.hasOwnProperty("READTHEDOCS_DATA")?(n=READTHEDOCS_DATA.project,a=READTHEDOCS_DATA.version,o=READTHEDOCS_DATA.language||"en",t=generateAndReturnInitialHtml(),document.body.innerHTML+=t,r=document.querySelector(".search__outer__wrapper"),s=document.querySelector(".search__outer__input"),i=document.querySelector(".search__cross"),c=0,l=null,(u=getInputField()).addEventListener("focus",function(e){showSearchModal()}),s.addEventListener("input",function(e){var t=getSearchTerm();COUNT=0;var r="/_/api/v2/search/?"+convertObjToUrlParams({q:t,project:n,version:a,language:o});0
<%= section_subheading %> <% for (var i = 0; i < section_content.length; ++i) { %>

<%= section_content[i] %>

<% } %>

',{section_link:e,section_id:r,section_subheading:n,section_content:o})},get_domain_html=function(e,t,r){var n="".concat(t,"#").concat(e.id),a=e.role,o=e.name,t=e.content.substr(0,MAX_SUBSTRING_LIMIT)+" ...",e=e.highlights;e.name.length&&(o=e.name[0]),e.content.length&&(t=e.content[0]);r="hit__"+r,a="["+a+"]";return $u.template('
<%= domain_subheading %>
<%= domain_role_name %>

<%= domain_content %>


',{domain_link:n,domain_id:r,domain_content:t,domain_subheading:o,domain_role_name:a})},generateSingleResult=function(e,t,r){var n=createDomNode("div"),a=e.path,o=e.title,s=e.highlights;s.title.length&&(o=s.title[0]),t!==e.project&&(o+=" "+$u.template(' (from project <%= project %>) ',{project:e.project})),o+="
",n.innerHTML+=$u.template('

<%= page_title %>

',{page_link:a,page_title:o});for(var i=0;i
\x3c!--?xml version="1.0" encoding="UTF-8"?--\x3e
Search by Read the Docs & readthedocs-sphinx-search
'},showSearchModal=function(t){removeResults();var r=getInputField();r.blur(),$(".search__outer__wrapper").fadeIn(ANIMATION_TIME,function(){var e=document.querySelector(".search__outer__input");null!==e&&(void 0!==t&&_is_string(t)?(e.value=t,r.value=t):e.value=r.value,e.focus())})},removeSearchModal=function(){removeResults(),updateSearchBar();var e=document.querySelector(".search__outer__input");null!==e&&(e.value="",e.blur()),updateUrl(),$(".search__outer__wrapper").fadeOut(ANIMATION_TIME)};window.addEventListener("DOMContentLoaded",function(e){var n,a,o,t,r,s,i,l;window.hasOwnProperty("READTHEDOCS_DATA")?(n=READTHEDOCS_DATA.project,a=READTHEDOCS_DATA.version,o=READTHEDOCS_DATA.language||"en",l=generateAndReturnInitialHtml(),document.body.innerHTML+=l,t=document.querySelector(".search__outer__wrapper"),r=document.querySelector(".search__outer__input"),l=document.querySelector(".search__cross"),s=null,(i=getInputField()).addEventListener("focus",function(e){showSearchModal()}),r.addEventListener("input",function(e){var t=getSearchTerm(),r="/_/api/v2/search/?"+convertObjToUrlParams({q:t,project:n,version:a,language:o});0