Skip to content

Embedded js: remove some dependency from jquery #9508

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion common
39 changes: 13 additions & 26 deletions readthedocs/core/static-src/core/js/doc-embed/footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,6 @@ function injectFooter(data) {
}


function setupBookmarkCSRFToken() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha, old school 🙃

function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (!csrfSafeMethod(settings.type)) {
xhr.setRequestHeader("X-CSRFToken", $('a.bookmark[token]').attr('token'));
}
}
});
}

function init() {
var rtd = rtddata.get();

Expand Down Expand Up @@ -86,25 +71,27 @@ function init() {
versionCompare.init(data.version_compare);
}
injectFooter(data);
setupBookmarkCSRFToken();
},
error: function () {
console.error('Error loading Read the Docs footer');
}
});

// Register page view.
$.ajax({
url: rtd.proxied_api_host + "/api/v2/analytics/",
data: {
project: rtd['project'],
version: rtd['version'],
absolute_uri: window.location.href,
},
cache: false,
error: function () {
console.error('Error registering page view');
let data = {
project: rtd['project'],
version: rtd['version'],
absolute_uri: window.location.href,
};
let url = rtd.proxied_api_host + '/api/v2/analytics/?' + new URLSearchParams(data).toString();
fetch(url, {method: 'GET', cache: 'no-store'})
.then(response => {
if (!response.ok) {
throw new Error();
}
})
.catch(error => {
console.error('Error registering page view');
});
}

Expand Down
183 changes: 104 additions & 79 deletions readthedocs/core/static-src/core/js/doc-embed/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,23 @@ var xss = require('xss/lib/index');
var MAX_RESULT_PER_SECTION = 3;
var MAX_SUBSTRING_LIMIT = 100;

/**
* Create and return DOM nodes with given attributes.
*
* @param {String} nodeName name of the node
* @param {Object} attributes obj of attributes to be assigned to the node
* @return {Object} DOM node
*/
const createDomNode = (nodeName, attributes) => {
let node = document.createElement(nodeName);
if (attributes) {
for (let attr of Object.keys(attributes)) {
node.setAttribute(attr, attributes[attr]);
}
}
return node;
};


/*
* Search query override for hitting our local API instead of the standard
Expand Down Expand Up @@ -58,19 +75,19 @@ function attach_elastic_search_query_sphinx(data) {
* @param {String} link.
* @param {Array} contents.
*/
var buildSection = function (title, link, contents) {
const buildSection = function (title, link, contents) {
var div_title = document.createElement("div");
var a_element = document.createElement("a");
a_element.href = link;
a_element.innerHTML = title;
div_title.appendChild(a_element);
html = div_title.outerHTML;
for (var i = 0; i < contents.length; i += 1) {
var div_content = document.createElement("div");
div_content.innerHTML = contents[i];
html += div_content.outerHTML;
let elements = [div_title];
for (let content of contents) {
let div_content = document.createElement("div");
div_content.innerHTML = content;
elements.push(div_content);
}
return html;
return elements;
};

search_def
Expand All @@ -81,7 +98,7 @@ function attach_elastic_search_query_sphinx(data) {
for (var i = 0; i < results.length; i += 1) {
var result = results[i];
var blocks = result.blocks;
var list_item = $('<li>');
let list_item = createDomNode('li');

var title = result.title;
// if highlighted title is present, use that.
Expand All @@ -91,23 +108,24 @@ function attach_elastic_search_query_sphinx(data) {

var link = result.path + "?highlight=" + encodeURIComponent(query);

var item = $('<a>', {'href': link});

item.html(title);
item.find('span').addClass('highlighted');
list_item.append(item);
let item = createDomNode('a', {href: link});
item.innerHTML = title;
for (let element of item.getElementsByTagName('span')) {
element.className = 'highlighted';
}
list_item.appendChild(item);

// If the document is from a subproject, add extra information
if (result.project !== project) {
var text = " (from project " + result.project_alias + ")";
var extra = $('<span>', {'text': text});
list_item.append(extra);
let extra = createDomNode('span');
extra.innerText = " (from project " + result.project_alias + ")";
list_item.appendChild(extra);
}

for (var block_index = 0; block_index < blocks.length; block_index += 1) {
var current_block = blocks[block_index];

var contents = $('<div class="context">');
let contents = createDomNode('div', {class: 'context'});

// if the result is page section
if (current_block.type === "section") {
Expand All @@ -131,12 +149,12 @@ function attach_elastic_search_query_sphinx(data) {
section_content.push("... " + xss(content[k]) + " ...");
}
}

contents.append(buildSection(
let sections = buildSection(
section_subtitle,
section_subtitle_link,
section_content
));
);
sections.forEach(element => { contents.appendChild(element); });
}

// if the result is a sphinx domain object
Expand All @@ -161,27 +179,30 @@ function attach_elastic_search_query_sphinx(data) {

var domain_subtitle = "[" + domain_role_name + "]: " + domain_name;

contents.append(buildSection(
let sections = buildSection(
domain_subtitle,
domain_subtitle_link,
[domain_content]
));
);
sections.forEach(element => { contents.appendChild(element); });
}

contents.find('span').addClass('highlighted');
list_item.append(contents);
for (let element of contents.getElementsByTagName('span')) {
element.className = 'highlighted';
}
list_item.appendChild(contents);

// Create some spacing between the results.
// Also, don't add this spacing in the last hit.
if (block_index < blocks.length - 1) {
list_item.append($("<div class='rtd_search_hits_spacing'></div>"));
list_item.appendChild(createDomNode('div', {class: 'rtd_search_hits_spacing'}));
}
}

if (Search.output.jquery) {
Search.output.append(list_item);
Search.output.append($(list_item));
} else {
Search.output.appendChild(list_item.get(0));
Search.output.appendChild(list_item);
}
}
setText(
Expand All @@ -198,30 +219,30 @@ function attach_elastic_search_query_sphinx(data) {
Search.query_fallback(query);
})
.always(function () {
$('#search-progress').empty();
let progress = document.getElementById('search-progress');
if (progress !== null) {
progress.replaceChildren();
}
Search.stopPulse();
setText(Search.title, _('Search Results'));
});

$.ajax({
url: search_url.href,
crossDomain: true,
xhrFields: {
withCredentials: true,
},
complete: function (resp, status_code) {
if (
status_code !== 'success' ||
typeof (resp.responseJSON) === 'undefined' ||
resp.responseJSON.count === 0
) {
return search_def.reject();
}
return search_def.resolve(resp.responseJSON);
fetch(search_url.href, {method: 'GET'})
.then(response => {
if (!response.ok) {
throw new Error();
}
return response.json();
})
.then(data => {
if (data.results.length > 0) {
search_def.resolve(data);
} else {
search_def.reject();
}
})
.fail(function (resp, status_code, error) {
return search_def.reject();
.catch(error => {
search_def.reject();
});
};

Expand Down Expand Up @@ -275,23 +296,27 @@ function attach_elastic_search_query_mkdocs(data) {
var results = data.results || [];

if (results.length) {
var searchResults = $('#mkdocs-search-results');
searchResults.empty();
let searchResults = document.getElementById('mkdocs-search-results');
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also just kill the mkdocs integration, isn't currently being used (only on my test project).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm 👍🏼 on removing things we are not using 😄

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this is something that we instead address with generic version of our search integration. I'm 👍 in both directions, if we aren't using the code it shouldn't hurt to keep it around either, but also happy to see unused code disappeared.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should remove it, and work towards shipping our livesearch as a generic JS library. The less code we have to maintain the better. 👍

if (searchResults != null) {
searchResults.replaceChildren();
}

for (var i = 0; i < results.length; i += 1) {
var result = results[i];
var blocks = result.blocks;

var link = result.path;

var item = $('<article>');
item.append(
$('<h3>').append($('<a>', {'href': link, 'text': result.title}))
);
let item = createDomNode('article');
let a_element = createDomNode('a', {href: link});
a_element.innerText = result.title;
let title_element = createDomNode('h3');
title_element.appendChild(a_element);
item.appendChild(title_element);

if (result.project !== project) {
var text = '(from project ' + result.project_alias + ')';
item.append($('<span>', {'text': text}));
let text = '(from project ' + result.project_alias + ')';
item.appendChild(createDomNode('span', {'text': text}));
}

for (var j = 0; j < blocks.length; j += 1) {
Expand Down Expand Up @@ -325,20 +350,23 @@ function attach_elastic_search_query_mkdocs(data) {
section_title = xss(section_title)
.replace(/<span>/g, '<mark>')
.replace(/<\/span>/g, '</mark>');
item.append(
$('<h4>')
.append($('<a>', {'href': section_link}).html(section_title))
);

let title_element = createDomNode('h4');
let a_element = createDomNode('a', {href: section_link});
a_element.innerHTML = section_title;
title_element.appendChild(a_element);
item.appendChild(title_element);

for (var m = 0; m < section_contents.length; m += 1) {
var content = xss(section_contents[m]);
let content = xss(section_contents[m]);
content = content
.replace(/<span>/g, '<mark>')
.replace(/<\/span>/g, '</mark>');
item.append(
$('<p>').html(content)
);
let p_element = createDomNode('p');
p_element.innerHTML = content;
item.appendChild(p_element);
}
searchResults.append(item);
searchResults.appendChild(item);
}
}
}
Expand All @@ -352,25 +380,22 @@ function attach_elastic_search_query_mkdocs(data) {
fallbackSearch();
});

$.ajax({
url: search_url.href,
crossDomain: true,
xhrFields: {
withCredentials: true,
},
complete: function (resp, status_code) {
if (
status_code !== 'success' ||
typeof (resp.responseJSON) === 'undefined' ||
resp.responseJSON.count === 0
) {
return search_def.reject();
}
return search_def.resolve(resp.responseJSON);
fetch(search_url.href, {method: 'GET'})
.then(response => {
if (!response.ok) {
throw new Error();
}
return response.json();
})
.then(data => {
if (data.results.length > 0) {
search_def.resolve(data);
} else {
search_def.reject();
}
})
.fail(function (resp, status_code, error) {
return search_def.reject();
.catch(error => {
search_def.reject();
});
};

Expand Down
2 changes: 1 addition & 1 deletion readthedocs/core/static/core/js/readthedocs-doc-embed.js

Large diffs are not rendered by default.