From 22398ac9cd6c945127ff8732dba7208a8ffe686d Mon Sep 17 00:00:00 2001 From: Karol Blaszczak Date: Wed, 13 Apr 2022 12:39:36 +0200 Subject: [PATCH] sphinx google search (#11439) (#11506) porting from 22.1 as per Andrey's request from 04.08 * sphinx google search * fixes * fixes * fix version tabs Co-authored-by: Nikolay Tyukaev --- docs/_static/css/custom.css | 5 + docs/_static/css/gsearch.css | 98 ++++++++ docs/_static/js/custom.js | 5 + docs/_static/js/gsearch.js | 376 ++++++++++++++++++++++++++++++ docs/_templates/search-field.html | 4 + docs/_templates/search.html | 31 +++ 6 files changed, 519 insertions(+) create mode 100644 docs/_static/css/gsearch.css create mode 100644 docs/_static/js/gsearch.js create mode 100644 docs/_templates/search-field.html create mode 100644 docs/_templates/search.html diff --git a/docs/_static/css/custom.css b/docs/_static/css/custom.css index fd48b99c149..2a39f1f3383 100644 --- a/docs/_static/css/custom.css +++ b/docs/_static/css/custom.css @@ -115,3 +115,8 @@ div.highlight { max-width: 1800px; } } + +main .searchForm { + margin-bottom: 2rem; + margin-top: 2rem; +} diff --git a/docs/_static/css/gsearch.css b/docs/_static/css/gsearch.css new file mode 100644 index 00000000000..ebd160a9fea --- /dev/null +++ b/docs/_static/css/gsearch.css @@ -0,0 +1,98 @@ +/* Google Search */ + +div.gs-result { + padding: 15px 0px; +} + +div.gs-result::after { + clear: both; + content: ""; + display: block; + } + +div.gs-title > a { + color: #2171b8; + font-size: 18px; +} + +div.gs-title > a:visited { + color: #609; +} + +div.gs-url { + font-size: 12px; + color: #1E9F40; +} + +div.gs-cursor { + margin-top: 40px; + cursor: pointer; +} + +a.gs-cursor-page { + margin-right: 10px; +} + +a.gs-cursor-page-previous { + margin-right: 30px; +} + +a.gs-cursor-page-next { + margin-left: 30px; +} + +div.gs-cursor a.active { + color: #2171b8; + font-weight: bold; +} + +div.gs-media { + float: left; + margin-right: 4px; +} + +div.gs-media img { + max-width: 60px; + max-height: 120px; +} + +div.gs-file-format-box { + font-size: 12px; +} + +span.gs-file-format { + color: #6e6e6e; +} + +div.gs-file-type { + font-weight: bold; +} + +div.gs-no-results { + border: 1px solid rgb(255,204,51); + background-color: rgb(255,244,194); + color: black; +} + +/* Version tabs */ + +#gs-tabs-area { + margin-top:2rem; +} + +a.gs-tab { + display: inline-block; + padding: 0px 10px; + margin-bottom: 1px; + text-transform: uppercase; + cursor: pointer; +} + +a.gs-tab-active { + border-bottom: 2px solid #2171b8; + pointer-events: none; + cursor: default; + text-decoration: none; + color: #2171b8 !important; + font-weight: bold; +} diff --git a/docs/_static/js/custom.js b/docs/_static/js/custom.js index 8e323f6fa6e..7ef541f26f6 100644 --- a/docs/_static/js/custom.js +++ b/docs/_static/js/custom.js @@ -45,6 +45,7 @@ $(document).ready(function () { addTableSort(); } addLegalNotice(); + updateSearchForm(); createSphinxTabSets(); }); @@ -108,6 +109,10 @@ function getCurrentVersion() { return encodeURI(wordAfterDomain); } +function updateSearchForm() { + var currentVersion = getCurrentVersion(); + $('.searchForm').append(''); +} function createVersions() { var currentVersion = getCurrentVersion(); diff --git a/docs/_static/js/gsearch.js b/docs/_static/js/gsearch.js new file mode 100644 index 00000000000..122fd92dd3c --- /dev/null +++ b/docs/_static/js/gsearch.js @@ -0,0 +1,376 @@ +/* API config */ +var cx = '012886673101693426607:wjpczzz2qi3'; +var path = 'https://content.googleapis.com/discovery/v1/apis/customsearch/v1/rest'; +var apiKey = 'AIzaSyBpwSxfoP2T_EK62wwNhZ9zyxUzeRjmOwA'; + + +var potKeywords = ['int8', '8-bit', '8 bit', 'quantization', 'quantized', + 'quantizer', 'fakequantize', 'fq', 'model optimization', + 'low precision', 'low-precision', 'lower precision', 'pot', + 'compression', 'calibration', 'post-training', 'post training']; + +// get the docs versions stored in versions_raw.js +var versions; +try { + versions = JSON.parse(data); +} +catch(e) { + versions = []; +}; + +function hasPotTerms(query) { + q = query.toLowerCase(); + return potKeywords.some(function(k) { + k = k.toLowerCase(); + return q.indexOf(k) != -1; + }); +} + +function boostPage(name, pages, pos) { + var tmp; + pages = pages || []; + for (var i = 1; i < pages.length; i++) { + if (name === getPageName(pages[i].link)) { + tmp = pages[pos]; + pages[pos] = pages[i]; + pages[i] = tmp; + pos++; + } + } +} + +function getPageName(url) { + urlArr = url.split('/'); + return urlArr[urlArr.length - 1]; +} + +function getURLParameter(name) { + return decodeURIComponent((new RegExp('[?|&]'+name+ + '='+'([^&;]+?)(&|#|;|$)').exec(location.search) + ||[,""])[1].replace(/\+/g, '%20'))||null; +} + +function search(query, selectedVersion, page, key) { + // make a query to Google Search API and render the results + var start = getStartIndex(page); // get start index from page number + var documentationHost = 'https://docs.openvino.ai'; + if (selectedVersion === 'ALL') { + var siteSearch = documentationHost; + } + else { + var siteSearch = documentationHost + '/' + selectedVersion; + } + var path = 'https://www.googleapis.com/customsearch/v1?cx=' + cx + '&q=' + query + '&siteSearch=' + siteSearch + '&start=' + start; + var hasPot = hasPotTerms(query); + if (hasPot) { + path += '&qt="pot"'; + } + + return async function() { + await gapi.client.init({ + 'apiKey': apiKey, + }).then(function() { + return gapi.client.request({ + 'path': path + }) + }).then(function(response) { + handleResponse(response, selectedVersion, hasPot); + }, function(err) { + console.log(err); + handleNoResults(); + }); + } +} + +function getPageNum(index) { + // get page number from page index + return Math.floor((index-1) / 10) + 1; +} + +function getStartIndex(page) { + // get start index from page number + return (page - 1) * 10 + 1; +} + +function handleNoResults() { + var $noResults = $('
', {'class': 'gs-no-results', 'text': 'No Results'}); + $('#searchinfo').append($noResults); +} + +function handleResponse(response, selectedVersion, hasPot) { + var result = response.result; + var searchInformation = result.searchInformation; + var totalResults = parseInt(searchInformation.totalResults); + var limit = null; + var previousPage, nextPage; + previousPage = nextPage = null; + var queries = result.queries; + var request = queries.request[0]; + var searchTerms = request.searchTerms; + var startIndex = request.startIndex; + var currentPage = getPageNum(startIndex); + + $('#searchinfo').text('Search Results for "' + searchTerms + '"'); + $('#searchfield').val(searchTerms); + if (queries.hasOwnProperty('nextPage')) { + nextPage = getPageNum(queries.nextPage[0].startIndex); + if (nextPage > 10) { + nextPage = null; + limit = currentPage; + } + } + else { + limit = currentPage; + } + if (queries.hasOwnProperty('previousPage')) { + previousPage = getPageNum(queries.previousPage[0].startIndex); + } + + var count = parseInt(request.count); + totalResults = (count + totalResults) > 100 ? 100 : totalResults; + var query = request.searchTerms; + var totalPages = Math.ceil(totalResults / count); + var items = response.result.items; + if (hasPot) { + boostPage('pot_README.html', items, 0); + } + + // add version tabs + addVersionTabs(selectedVersion, query); + + if (!(totalPages || items)) { + handleNoResults(); + totalPages = limit; + } + else { + // render the results + renderResults(items); + } + addPagination(query, selectedVersion, currentPage, previousPage, nextPage, totalPages, limit); +} + +function addVersionTabs(selectedVersion, query) { + // remove latest + var tab_versions = [{'version': 'ALL'}]; + var latestVersion; + if (versions.length) { + tab_versions = [{'version': 'ALL'}].concat(versions.slice(1)); + latestVersion = tab_versions[2].version; + if (selectedVersion === 'latest') { + selectedVersion = versions[2].version; + } + } + for (var i = 0; i < tab_versions.length; i++) { + var href; + var $elem = $('', { 'class': 'gs-tab', 'href': 'search.html?version=' + tab_versions[i].version + '&query=' + query, 'text': tab_versions[i].version }); + if (tab_versions[i].version === 'ALL') { + href = 'search.html?query=' + query; + } + else { + href = 'search.html?query=' + query + '&version=' + tab_versions[i].version + } + var $elem = $('', { 'class': 'gs-tab', 'href': href, 'text': tab_versions[i].version }); + if (selectedVersion === tab_versions[i].version) { + $elem.addClass('gs-tab-active'); + } + if (latestVersion === tab_versions[i].version) { + $elem.text('latest (' + $elem.text() + ')'); + } + $('#gs-tabs-area').append($elem); + } +} + +function renderResults(items) { + var $elements = $('
', {'class': 'gs-results'}); + $.each(items, function(i, item) { + // result item + var $resultItem = $('
', {'class': 'gs-result' }); + var $title = $('
', {'class': 'gs-title'}); + $title.append($('', {'target': '_blank', 'href': item['link'], 'html': item['htmlTitle']})); + var $url = $('
', {'class': 'gs-url', 'html': item['link']}); + var $context = $('
', {'class': 'gs-context'}); + var $snippet = $('
', {'class': 'gs-snippet', 'html': item['htmlSnippet'].replace(/\/g, '')}); + + // media + if (item.pagemap.hasOwnProperty('cse_thumbnail')) { + var $media = $('
', {'class': 'gs-media'}); + var cse_thumbnail = item.pagemap.cse_thumbnail[0]; + var $link = $('', {'href': item['link'], 'target': '_blank'}); + var $image = $('', { 'src': cse_thumbnail.src, 'alt': 'Thumbnail image' }); + $link.append($image); + $media.append($link); + $context.append($media); + } + + // file format + if (item.hasOwnProperty('fileFormat')) { + var $ffBox = $('
', {'class': 'gs-file-format-box'}); + var $fileFormat = $('', {'class': 'gs-file-format', 'text': 'File format: '}); + var $type = $('', {'class': 'gs-file-type', 'text': item['fileFormat']}); + $ffBox.append($fileFormat); + $ffBox.append($type); + $context.append($ffBox); + } + + $context.append($snippet); + + $resultItem.append($title); + $resultItem.append($url); + $resultItem.append($context); + $elements.append($resultItem); + + }); + $('#searchresults').append($elements); +} + +function addPagination(query, selectedVersion, currentPage, previousPage, nextPage, totalPages, limit) { + function getPageList(currentPage, totalPages, limit) { + var x, left, right, pageList; + left = right = currentPage; + pageList = []; + x = 1; + while (x < 10 && x < totalPages) { + if (left > 1) { + left--; + x++; + } + if (right < totalPages) { + right++; + x++; + } + } + + if (limit === currentPage) { + right = currentPage; + } + + var diff = (right - left) + 1; + var rest = 10 - diff; + + if (rest !== 0) { + left = left - rest < 1 ? 1 : left - rest; + } + + for(i = left; i <= right; i++) { + pageList.push(i); + } + return pageList; + } + + var pageList = getPageList(currentPage, totalPages, limit); + + var $cursor = $('
', {'class': 'gs-cursor'}); + + if (previousPage) { + var $previous = $('', { + 'text': 'Previous', + 'class': 'gs-cursor-page-previous', + 'href': 'search.html?query=' + query + '&page=' + previousPage + '&version=' + selectedVersion + }); + $cursor.append($previous); + } + + var $page; + for(var i = 0; i < pageList.length; i++) { + $page = $('', { + 'class': 'gs-cursor-page', + 'href': 'search.html?query=' + query + '&page=' + pageList[i] + '&version=' + selectedVersion, + 'text': pageList[i] + }); + if (currentPage === pageList[i]) { + $page.addClass('active'); + } + $cursor.append($page); + } + + if (nextPage) { + var $next = $('', { + 'text': 'Next', + 'class': 'gs-cursor-page-next', + 'href': 'search.html?query=' + query + '&page=' + nextPage + '&version=' + selectedVersion + }); + $cursor.append($next); + } + $('#searchresults').append($cursor); +} + +function trim(s) { + return s?s.replace(/^\s\s*/, '').replace(/\s\s*$/, ''):''; +} + +var entityMap = { + "&": "&", + "<": "<", + ">": ">", + '"': '"', + "'": ''', + "/": '/' +}; + +function escapeHtml(s) { + return String(s).replace(/[&<>"'\/]/g, function (s) { + return entityMap[s]; + }); +} + + +function versionExists(v) { + for (var i = 0; i < versions.length; i++) { + if (versions[i].version === v) { + return true; + } + } + return false; +} + +function getCurrentPath() { + var windowLocationArr = window.location.href.split("/"); + var currentPath = encodeURI( + windowLocationArr[windowLocationArr.length - 1].replace( + window.location.hash, + "" + )); + return currentPath; +} + +function getURLParameter(name) { + return decodeURIComponent((new RegExp('[?|&]'+name+ + '='+'([^&;]+?)(&|#|;|$)').exec(location.search) + ||[,""])[1].replace(/\+/g, '%20'))||null; +} + +$(document).ready(function() { + // remove duplicate search + $('#top > #MSearchField').remove(); + + var query = trim(getURLParameter('query')); + if (!query) { + handleNoResults(); + return; + } + var page = trim(getURLParameter('page')) || 1; + var selectedVersion = trim(getURLParameter('version')); + if (versionExists(selectedVersion)) { + if (versions[1] && selectedVersion === versions[1].version) { + selectedVersion = 'latest'; + } + if (window.location.pathname.startsWith('/cn')) { + selectedVersion = 'cn/' + selectedVersion; + } + } + else { + selectedVersion = 'ALL'; + } + + // the key is used to store and retrieve data from sessionStorage + var key = getCurrentPath(); + + var data; + if (key in sessionStorage) { + try { + data = JSON.parse(sessionStorage[key]); + } + catch(err) {}; + } + gapi.load('client', search(query, selectedVersion,page, key)); +}); diff --git a/docs/_templates/search-field.html b/docs/_templates/search-field.html new file mode 100644 index 00000000000..4ed6c3803df --- /dev/null +++ b/docs/_templates/search-field.html @@ -0,0 +1,4 @@ + diff --git a/docs/_templates/search.html b/docs/_templates/search.html new file mode 100644 index 00000000000..7b9b218860e --- /dev/null +++ b/docs/_templates/search.html @@ -0,0 +1,31 @@ +{%- extends "layout.html" %} +{% set title = _('Search') %} +{%- block scripts %} + {{ super() }} + + + +{%- endblock %} +{% block body %} +

{{ _('Search') }}

+

+ Search Results... +

+
+ {% block scriptwarning %} +
+ +
+ {% endblock %} + {% block searchbox %} +
+ + + +
+ {% endblock %} + {% block searchresults %} +
+
+ {% endblock %} +{% endblock %}