From 225353f6be04f2fa178ec2350e3c27599b6b2ea1 Mon Sep 17 00:00:00 2001 From: Santos Gallegos Date: Tue, 9 Aug 2022 16:54:19 -0500 Subject: [PATCH] Refactor: don't depend on underscore.js (#116) This uses the DOM API to crate the elements instead of using templates from underscorejs. The html structure is exactly the same. --- docs/customization.rst | 9 +- sphinx_search/static/js/rtd_sphinx_search.js | 191 +++++++++--------- .../static/js/rtd_sphinx_search.min.js | 2 +- 3 files changed, 106 insertions(+), 96 deletions(-) diff --git a/docs/customization.rst b/docs/customization.rst index f1e1c1a..04fe2b5 100644 --- a/docs/customization.rst +++ b/docs/customization.rst @@ -106,9 +106,14 @@ is given below for reference:
- + - http:get + /api/v2/section/ + + +
+ [http:get] +
diff --git a/sphinx_search/static/js/rtd_sphinx_search.js b/sphinx_search/static/js/rtd_sphinx_search.js index c058e90..0d22513 100644 --- a/sphinx_search/static/js/rtd_sphinx_search.js +++ b/sphinx_search/static/js/rtd_sphinx_search.js @@ -42,18 +42,46 @@ const debounce = (func, wait) => { /** - * Wrapper around underscorejs's template function. - * - * This is to make it work with new and old versions. - */ -const render_template = (template, data) => { - // pre-1.7 syntax from underscorejs. - let result = $u.template(template, data); - if (typeof result === 'function') { - // New syntax. - result = $u.template(template)(data); + * Build a section with its matching results. + * + * A section has the form: + * + *
+ *
+ * + * {title} + * + *

+ * {contents[0]} + *

+ *

+ * {contents[1]} + *

+ * ... + *
+ *
+ * + * @param {String} id. + * @param {String} title. + * @param {String} link. + * @param {Array} contents. + */ +const buildSection = function (id, title, link, contents) { + let span_element = createDomNode("span", {class: "search__result__subheading"}); + span_element.innerHTML = title; + + let div_element = createDomNode("div", {class: "outer_div_page_results", id: id}); + div_element.appendChild(span_element); + + for (var i = 0; i < contents.length; i += 1) { + let p_element = createDomNode("p", {class: "search__result__content"}); + p_element.innerHTML = contents[i]; + div_element.appendChild(p_element); } - return result; + + let section = createDomNode("a", {href: link}); + section.appendChild(div_element); + return section; }; @@ -105,8 +133,10 @@ const isModalVisible = () => { */ const createDomNode = (nodeName, attributes) => { let node = document.createElement(nodeName); - for (let attr in attributes) { - node.setAttribute(attr, attributes[attr]); + if (attributes !== null) { + for (let attr in attributes) { + node.setAttribute(attr, attributes[attr]); + } } return node; }; @@ -135,21 +165,6 @@ const _is_string = str => { * @param {Number} id to be used in for this section */ const get_section_html = (sectionData, page_link, id) => { - let section_template = - ' \ -
\ - \ - <%= section_subheading %> \ - \ - <% for (var i = 0; i < section_content.length; ++i) { %> \ -

\ - <%= section_content[i] %> \ -

\ - <% } %>\ -
\ -
\ -
'; - let section_subheading = sectionData.title; let highlights = sectionData.highlights; if (highlights.title.length) { @@ -173,17 +188,8 @@ const get_section_html = (sectionData, page_link, id) => { } let section_link = `${page_link}#${sectionData.id}`; - let section_id = "hit__" + id; - - let section_html = render_template(section_template, { - section_link: section_link, - section_id: section_id, - section_subheading: section_subheading, - section_content: section_content - }); - - return section_html; + return buildSection(section_id, section_subheading, section_link, section_content); }; /** @@ -195,20 +201,6 @@ const get_section_html = (sectionData, page_link, id) => { * @param {Number} id to be used in for this section */ const get_domain_html = (domainData, page_link, id) => { - let domain_template = - ' \ -
\ - \ - <%= domain_subheading %> \ -
\ - <%= domain_role_name %> \ -
\ -
\ -

<%= domain_content %>

\ -
\ -
\ -
'; - let domain_link = `${page_link}#${domainData.id}`; let domain_role_name = domainData.role; let domain_name = domainData.name; @@ -224,37 +216,50 @@ const get_domain_html = (domainData, page_link, id) => { } let domain_id = "hit__" + id; - domain_role_name = "[" + domain_role_name + "]"; - - let domain_html = render_template(domain_template, { - domain_link: domain_link, - domain_id: domain_id, - domain_content: domain_content, - domain_subheading: domain_name, - domain_role_name: domain_role_name - }); - return domain_html; + let div_role_name = createDomNode("div", {class: "search__domain_role_name"}); + div_role_name.innerText = `[${domain_role_name}]`; + domain_name += div_role_name.outerHTML; + + return buildSection( + domain_id, + domain_name, + domain_link, + [domain_content] + ); }; + /** * Generate search results for a single page. * + * This has the form: + *
+ * + *

+ * {title} + * {subtitle} + *
+ *

+ *
+ * + * + * {section} + * + *
+ * + * + * {section} + * + *
+ *
+ * * @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, id) => { - let content = createDomNode("div"); - - let page_link_template = - ' \ -

\ - <%= page_title %> \ -

\ -
'; - let page_link = resultData.path; let page_title = resultData.title; let highlights = resultData.highlights; @@ -263,47 +268,47 @@ const generateSingleResult = (resultData, projectName, id) => { page_title = highlights.title[0]; } - // if result is not from the same project, - // then it must be from subproject. + let h2_element = createDomNode("h2", {class: "search__result__title"}); + h2_element.innerHTML = page_title; + + // If the result is not from the same project, + // then it's from a subproject. if (projectName !== resultData.project) { - page_title += - " " + - render_template( - ' \ - (from project <%= project %>) \ - ', - { - project: resultData.project - } - ); + let subtitle = createDomNode("small", {class: "rtd_ui_search_subtitle"}); + subtitle.innerText = `(from project ${resultData.project})`; + h2_element.appendChild(subtitle); } + h2_element.appendChild(createDomNode("br")) - page_title += "
"; + let a_element = createDomNode("a", {href: page_link}); + a_element.appendChild(h2_element); - content.innerHTML += render_template(page_link_template, { - page_link: page_link, - page_title: page_title - }); + let content = createDomNode("div"); + content.appendChild(a_element); + let separator = createDomNode("br", {class: "br-for-hits"}); for (let i = 0; i < resultData.blocks.length; ++i) { let block = resultData.blocks[i]; - let html_structure = ""; - + let section = null; id += 1; if (block.type === "section") { - html_structure = get_section_html( + section = get_section_html( block, page_link, id, ); } else if (block.type === "domain") { - html_structure = get_domain_html( + section = get_domain_html( block, page_link, id, ); } - content.innerHTML += html_structure; + + if (section !== null) { + content.appendChild(section); + content.appendChild(separator); + } } return content; }; diff --git a/sphinx_search/static/js/rtd_sphinx_search.min.js b/sphinx_search/static/js/rtd_sphinx_search.min.js index daec7ef..ed5d7bb 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,RTD_SEARCH_PARAMETER="rtd_search",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},render_template=function(e,t){var r=$u.template(e,t);return r="function"==typeof r?$u.template(e)(t):r},updateUrl=function(){var e=new URL(window.location.href),t=getSearchTerm();0
<%= section_subheading %> <% for (var i = 0; i < section_content.length; ++i) { %>

<%= section_content[i] %>

<% } %>

',{section_link:e,section_id:"hit__"+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;return e.name.length&&(o=e.name[0]),e.content.length&&(t=e.content[0]),render_template('
<%= domain_subheading %>
<%= domain_role_name %>

<%= domain_content %>


',{domain_link:n,domain_id:"hit__"+r,domain_content:t,domain_subheading:o,domain_role_name:a="["+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+=" "+render_template(' (from project <%= project %>) ',{project:e.project})),o+="
",n.innerHTML+=render_template('

<%= page_title %>

',{page_link:a,page_title:o});for(var c=0;cSearching ....",a.appendChild(e);return debounce(function(){updateUrl(),updateSearchBar();var e=t+"?"+new URLSearchParams(r).toString();fetch(e,{method:"GET"}).then(function(e){if(!e.ok)throw new Error;return e.json()}).then(function(e){var t;0Searching ....",o.appendChild(e);return debounce(function(){updateUrl(),updateSearchBar();var e=t+"?"+new URLSearchParams(r).toString();fetch(e,{method:"GET"}).then(function(e){if(!e.ok)throw new Error;return e.json()}).then(function(e){var t;0