mirror of
https://github.com/snachodog/just-the-docs.git
synced 2025-04-15 23:52:23 -06:00
Hello everyone, this is my implementation for the copy button on the snippet (requested in #924) The implementation is made 100% javascript as with or without a jekyll template modification you still have to execute some javascript code, and I consider it the best choice. the button only appears if the mouse is over it, to allow the entire line to be read the important CSS changes were made to make the copy button work even in the long code situation:  to avoid this:  Co-authored-by: Matt Wang <matt@matthewwang.me>
526 lines
17 KiB
JavaScript
526 lines
17 KiB
JavaScript
---
|
|
---
|
|
(function (jtd, undefined) {
|
|
|
|
// Event handling
|
|
|
|
jtd.addEvent = function(el, type, handler) {
|
|
if (el.attachEvent) el.attachEvent('on'+type, handler); else el.addEventListener(type, handler);
|
|
}
|
|
jtd.removeEvent = function(el, type, handler) {
|
|
if (el.detachEvent) el.detachEvent('on'+type, handler); else el.removeEventListener(type, handler);
|
|
}
|
|
jtd.onReady = function(ready) {
|
|
// in case the document is already rendered
|
|
if (document.readyState!='loading') ready();
|
|
// modern browsers
|
|
else if (document.addEventListener) document.addEventListener('DOMContentLoaded', ready);
|
|
// IE <= 8
|
|
else document.attachEvent('onreadystatechange', function(){
|
|
if (document.readyState=='complete') ready();
|
|
});
|
|
}
|
|
|
|
// Show/hide mobile menu
|
|
|
|
function initNav() {
|
|
jtd.addEvent(document, 'click', function(e){
|
|
var target = e.target;
|
|
while (target && !(target.classList && target.classList.contains('nav-list-expander'))) {
|
|
target = target.parentNode;
|
|
}
|
|
if (target) {
|
|
e.preventDefault();
|
|
target.parentNode.classList.toggle('active');
|
|
}
|
|
});
|
|
|
|
const siteNav = document.getElementById('site-nav');
|
|
const mainHeader = document.getElementById('main-header');
|
|
const menuButton = document.getElementById('menu-button');
|
|
|
|
jtd.addEvent(menuButton, 'click', function(e){
|
|
e.preventDefault();
|
|
|
|
if (menuButton.classList.toggle('nav-open')) {
|
|
siteNav.classList.add('nav-open');
|
|
mainHeader.classList.add('nav-open');
|
|
} else {
|
|
siteNav.classList.remove('nav-open');
|
|
mainHeader.classList.remove('nav-open');
|
|
}
|
|
});
|
|
|
|
{%- if site.search_enabled != false and site.search.button %}
|
|
const searchInput = document.getElementById('search-input');
|
|
const searchButton = document.getElementById('search-button');
|
|
|
|
jtd.addEvent(searchButton, 'click', function(e){
|
|
e.preventDefault();
|
|
|
|
mainHeader.classList.add('nav-open');
|
|
searchInput.focus();
|
|
});
|
|
{%- endif %}
|
|
}
|
|
|
|
{%- if site.search_enabled != false %}
|
|
// Site search
|
|
|
|
function initSearch() {
|
|
var request = new XMLHttpRequest();
|
|
request.open('GET', '{{ "assets/js/search-data.json" | relative_url }}', true);
|
|
|
|
request.onload = function(){
|
|
if (request.status >= 200 && request.status < 400) {
|
|
var docs = JSON.parse(request.responseText);
|
|
|
|
lunr.tokenizer.separator = {{ site.search.tokenizer_separator | default: site.search_tokenizer_separator | default: "/[\s\-/]+/" }}
|
|
|
|
var index = lunr(function(){
|
|
this.ref('id');
|
|
this.field('title', { boost: 200 });
|
|
this.field('content', { boost: 2 });
|
|
{%- if site.search.rel_url != false %}
|
|
this.field('relUrl');
|
|
{%- endif %}
|
|
this.metadataWhitelist = ['position']
|
|
|
|
for (var i in docs) {
|
|
this.add({
|
|
id: i,
|
|
title: docs[i].title,
|
|
content: docs[i].content,
|
|
{%- if site.search.rel_url != false %}
|
|
relUrl: docs[i].relUrl
|
|
{%- endif %}
|
|
});
|
|
}
|
|
});
|
|
|
|
searchLoaded(index, docs);
|
|
} else {
|
|
console.log('Error loading ajax request. Request status:' + request.status);
|
|
}
|
|
};
|
|
|
|
request.onerror = function(){
|
|
console.log('There was a connection error');
|
|
};
|
|
|
|
request.send();
|
|
}
|
|
|
|
function searchLoaded(index, docs) {
|
|
var index = index;
|
|
var docs = docs;
|
|
var searchInput = document.getElementById('search-input');
|
|
var searchResults = document.getElementById('search-results');
|
|
var mainHeader = document.getElementById('main-header');
|
|
var currentInput;
|
|
var currentSearchIndex = 0;
|
|
|
|
function showSearch() {
|
|
document.documentElement.classList.add('search-active');
|
|
}
|
|
|
|
function hideSearch() {
|
|
document.documentElement.classList.remove('search-active');
|
|
}
|
|
|
|
function update() {
|
|
currentSearchIndex++;
|
|
|
|
var input = searchInput.value;
|
|
if (input === '') {
|
|
hideSearch();
|
|
} else {
|
|
showSearch();
|
|
// scroll search input into view, workaround for iOS Safari
|
|
window.scroll(0, -1);
|
|
setTimeout(function(){ window.scroll(0, 0); }, 0);
|
|
}
|
|
if (input === currentInput) {
|
|
return;
|
|
}
|
|
currentInput = input;
|
|
searchResults.innerHTML = '';
|
|
if (input === '') {
|
|
return;
|
|
}
|
|
|
|
var results = index.query(function (query) {
|
|
var tokens = lunr.tokenizer(input)
|
|
query.term(tokens, {
|
|
boost: 10
|
|
});
|
|
query.term(tokens, {
|
|
wildcard: lunr.Query.wildcard.TRAILING
|
|
});
|
|
});
|
|
|
|
if ((results.length == 0) && (input.length > 2)) {
|
|
var tokens = lunr.tokenizer(input).filter(function(token, i) {
|
|
return token.str.length < 20;
|
|
})
|
|
if (tokens.length > 0) {
|
|
results = index.query(function (query) {
|
|
query.term(tokens, {
|
|
editDistance: Math.round(Math.sqrt(input.length / 2 - 1))
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
if (results.length == 0) {
|
|
var noResultsDiv = document.createElement('div');
|
|
noResultsDiv.classList.add('search-no-result');
|
|
noResultsDiv.innerText = 'No results found';
|
|
searchResults.appendChild(noResultsDiv);
|
|
|
|
} else {
|
|
var resultsList = document.createElement('ul');
|
|
resultsList.classList.add('search-results-list');
|
|
searchResults.appendChild(resultsList);
|
|
|
|
addResults(resultsList, results, 0, 10, 100, currentSearchIndex);
|
|
}
|
|
|
|
function addResults(resultsList, results, start, batchSize, batchMillis, searchIndex) {
|
|
if (searchIndex != currentSearchIndex) {
|
|
return;
|
|
}
|
|
for (var i = start; i < (start + batchSize); i++) {
|
|
if (i == results.length) {
|
|
return;
|
|
}
|
|
addResult(resultsList, results[i]);
|
|
}
|
|
setTimeout(function() {
|
|
addResults(resultsList, results, start + batchSize, batchSize, batchMillis, searchIndex);
|
|
}, batchMillis);
|
|
}
|
|
|
|
function addResult(resultsList, result) {
|
|
var doc = docs[result.ref];
|
|
|
|
var resultsListItem = document.createElement('li');
|
|
resultsListItem.classList.add('search-results-list-item');
|
|
resultsList.appendChild(resultsListItem);
|
|
|
|
var resultLink = document.createElement('a');
|
|
resultLink.classList.add('search-result');
|
|
resultLink.setAttribute('href', doc.url);
|
|
resultsListItem.appendChild(resultLink);
|
|
|
|
var resultTitle = document.createElement('div');
|
|
resultTitle.classList.add('search-result-title');
|
|
resultLink.appendChild(resultTitle);
|
|
|
|
var resultDoc = document.createElement('div');
|
|
resultDoc.classList.add('search-result-doc');
|
|
resultDoc.innerHTML = '<svg viewBox="0 0 24 24" class="search-result-icon"><use xlink:href="#svg-doc"></use></svg>';
|
|
resultTitle.appendChild(resultDoc);
|
|
|
|
var resultDocTitle = document.createElement('div');
|
|
resultDocTitle.classList.add('search-result-doc-title');
|
|
resultDocTitle.innerHTML = doc.doc;
|
|
resultDoc.appendChild(resultDocTitle);
|
|
var resultDocOrSection = resultDocTitle;
|
|
|
|
if (doc.doc != doc.title) {
|
|
resultDoc.classList.add('search-result-doc-parent');
|
|
var resultSection = document.createElement('div');
|
|
resultSection.classList.add('search-result-section');
|
|
resultSection.innerHTML = doc.title;
|
|
resultTitle.appendChild(resultSection);
|
|
resultDocOrSection = resultSection;
|
|
}
|
|
|
|
var metadata = result.matchData.metadata;
|
|
var titlePositions = [];
|
|
var contentPositions = [];
|
|
for (var j in metadata) {
|
|
var meta = metadata[j];
|
|
if (meta.title) {
|
|
var positions = meta.title.position;
|
|
for (var k in positions) {
|
|
titlePositions.push(positions[k]);
|
|
}
|
|
}
|
|
if (meta.content) {
|
|
var positions = meta.content.position;
|
|
for (var k in positions) {
|
|
var position = positions[k];
|
|
var previewStart = position[0];
|
|
var previewEnd = position[0] + position[1];
|
|
var ellipsesBefore = true;
|
|
var ellipsesAfter = true;
|
|
for (var k = 0; k < {{ site.search.preview_words_before | default: 5 }}; k++) {
|
|
var nextSpace = doc.content.lastIndexOf(' ', previewStart - 2);
|
|
var nextDot = doc.content.lastIndexOf('. ', previewStart - 2);
|
|
if ((nextDot >= 0) && (nextDot > nextSpace)) {
|
|
previewStart = nextDot + 1;
|
|
ellipsesBefore = false;
|
|
break;
|
|
}
|
|
if (nextSpace < 0) {
|
|
previewStart = 0;
|
|
ellipsesBefore = false;
|
|
break;
|
|
}
|
|
previewStart = nextSpace + 1;
|
|
}
|
|
for (var k = 0; k < {{ site.search.preview_words_after | default: 10 }}; k++) {
|
|
var nextSpace = doc.content.indexOf(' ', previewEnd + 1);
|
|
var nextDot = doc.content.indexOf('. ', previewEnd + 1);
|
|
if ((nextDot >= 0) && (nextDot < nextSpace)) {
|
|
previewEnd = nextDot;
|
|
ellipsesAfter = false;
|
|
break;
|
|
}
|
|
if (nextSpace < 0) {
|
|
previewEnd = doc.content.length;
|
|
ellipsesAfter = false;
|
|
break;
|
|
}
|
|
previewEnd = nextSpace;
|
|
}
|
|
contentPositions.push({
|
|
highlight: position,
|
|
previewStart: previewStart, previewEnd: previewEnd,
|
|
ellipsesBefore: ellipsesBefore, ellipsesAfter: ellipsesAfter
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
if (titlePositions.length > 0) {
|
|
titlePositions.sort(function(p1, p2){ return p1[0] - p2[0] });
|
|
resultDocOrSection.innerHTML = '';
|
|
addHighlightedText(resultDocOrSection, doc.title, 0, doc.title.length, titlePositions);
|
|
}
|
|
|
|
if (contentPositions.length > 0) {
|
|
contentPositions.sort(function(p1, p2){ return p1.highlight[0] - p2.highlight[0] });
|
|
var contentPosition = contentPositions[0];
|
|
var previewPosition = {
|
|
highlight: [contentPosition.highlight],
|
|
previewStart: contentPosition.previewStart, previewEnd: contentPosition.previewEnd,
|
|
ellipsesBefore: contentPosition.ellipsesBefore, ellipsesAfter: contentPosition.ellipsesAfter
|
|
};
|
|
var previewPositions = [previewPosition];
|
|
for (var j = 1; j < contentPositions.length; j++) {
|
|
contentPosition = contentPositions[j];
|
|
if (previewPosition.previewEnd < contentPosition.previewStart) {
|
|
previewPosition = {
|
|
highlight: [contentPosition.highlight],
|
|
previewStart: contentPosition.previewStart, previewEnd: contentPosition.previewEnd,
|
|
ellipsesBefore: contentPosition.ellipsesBefore, ellipsesAfter: contentPosition.ellipsesAfter
|
|
}
|
|
previewPositions.push(previewPosition);
|
|
} else {
|
|
previewPosition.highlight.push(contentPosition.highlight);
|
|
previewPosition.previewEnd = contentPosition.previewEnd;
|
|
previewPosition.ellipsesAfter = contentPosition.ellipsesAfter;
|
|
}
|
|
}
|
|
|
|
var resultPreviews = document.createElement('div');
|
|
resultPreviews.classList.add('search-result-previews');
|
|
resultLink.appendChild(resultPreviews);
|
|
|
|
var content = doc.content;
|
|
for (var j = 0; j < Math.min(previewPositions.length, {{ site.search.previews | default: 3 }}); j++) {
|
|
var position = previewPositions[j];
|
|
|
|
var resultPreview = document.createElement('div');
|
|
resultPreview.classList.add('search-result-preview');
|
|
resultPreviews.appendChild(resultPreview);
|
|
|
|
if (position.ellipsesBefore) {
|
|
resultPreview.appendChild(document.createTextNode('... '));
|
|
}
|
|
addHighlightedText(resultPreview, content, position.previewStart, position.previewEnd, position.highlight);
|
|
if (position.ellipsesAfter) {
|
|
resultPreview.appendChild(document.createTextNode(' ...'));
|
|
}
|
|
}
|
|
}
|
|
|
|
{%- if site.search.rel_url != false %}
|
|
var resultRelUrl = document.createElement('span');
|
|
resultRelUrl.classList.add('search-result-rel-url');
|
|
resultRelUrl.innerText = doc.relUrl;
|
|
resultTitle.appendChild(resultRelUrl);
|
|
{%- endif %}
|
|
}
|
|
|
|
function addHighlightedText(parent, text, start, end, positions) {
|
|
var index = start;
|
|
for (var i in positions) {
|
|
var position = positions[i];
|
|
var span = document.createElement('span');
|
|
span.innerHTML = text.substring(index, position[0]);
|
|
parent.appendChild(span);
|
|
index = position[0] + position[1];
|
|
var highlight = document.createElement('span');
|
|
highlight.classList.add('search-result-highlight');
|
|
highlight.innerHTML = text.substring(position[0], index);
|
|
parent.appendChild(highlight);
|
|
}
|
|
var span = document.createElement('span');
|
|
span.innerHTML = text.substring(index, end);
|
|
parent.appendChild(span);
|
|
}
|
|
}
|
|
|
|
jtd.addEvent(searchInput, 'focus', function(){
|
|
setTimeout(update, 0);
|
|
});
|
|
|
|
jtd.addEvent(searchInput, 'keyup', function(e){
|
|
switch (e.keyCode) {
|
|
case 27: // When esc key is pressed, hide the results and clear the field
|
|
searchInput.value = '';
|
|
break;
|
|
case 38: // arrow up
|
|
case 40: // arrow down
|
|
case 13: // enter
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
update();
|
|
});
|
|
|
|
jtd.addEvent(searchInput, 'keydown', function(e){
|
|
switch (e.keyCode) {
|
|
case 38: // arrow up
|
|
e.preventDefault();
|
|
var active = document.querySelector('.search-result.active');
|
|
if (active) {
|
|
active.classList.remove('active');
|
|
if (active.parentElement.previousSibling) {
|
|
var previous = active.parentElement.previousSibling.querySelector('.search-result');
|
|
previous.classList.add('active');
|
|
}
|
|
}
|
|
return;
|
|
case 40: // arrow down
|
|
e.preventDefault();
|
|
var active = document.querySelector('.search-result.active');
|
|
if (active) {
|
|
if (active.parentElement.nextSibling) {
|
|
var next = active.parentElement.nextSibling.querySelector('.search-result');
|
|
active.classList.remove('active');
|
|
next.classList.add('active');
|
|
}
|
|
} else {
|
|
var next = document.querySelector('.search-result');
|
|
if (next) {
|
|
next.classList.add('active');
|
|
}
|
|
}
|
|
return;
|
|
case 13: // enter
|
|
e.preventDefault();
|
|
var active = document.querySelector('.search-result.active');
|
|
if (active) {
|
|
active.click();
|
|
} else {
|
|
var first = document.querySelector('.search-result');
|
|
if (first) {
|
|
first.click();
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
});
|
|
|
|
jtd.addEvent(document, 'click', function(e){
|
|
if (e.target != searchInput) {
|
|
hideSearch();
|
|
}
|
|
});
|
|
}
|
|
{%- endif %}
|
|
|
|
// Switch theme
|
|
|
|
jtd.getTheme = function() {
|
|
var cssFileHref = document.querySelector('[rel="stylesheet"]').getAttribute('href');
|
|
return cssFileHref.substring(cssFileHref.lastIndexOf('-') + 1, cssFileHref.length - 4);
|
|
}
|
|
|
|
jtd.setTheme = function(theme) {
|
|
var cssFile = document.querySelector('[rel="stylesheet"]');
|
|
cssFile.setAttribute('href', '{{ "assets/css/just-the-docs-" | relative_url }}' + theme + '.css');
|
|
}
|
|
|
|
// Scroll site-nav to ensure the link to the current page is visible
|
|
|
|
function scrollNav() {
|
|
const href = document.location.pathname;
|
|
const siteNav = document.getElementById('site-nav');
|
|
const targetLink = siteNav.querySelector('a[href="' + href + '"], a[href="' + href + '/"]');
|
|
if(targetLink){
|
|
const rect = targetLink.getBoundingClientRect();
|
|
siteNav.scrollBy(0, rect.top - 3*rect.height);
|
|
}
|
|
}
|
|
|
|
// Document ready
|
|
|
|
jtd.onReady(function(){
|
|
initNav();
|
|
{%- if site.search_enabled != false %}
|
|
initSearch();
|
|
{%- endif %}
|
|
scrollNav();
|
|
});
|
|
|
|
// Copy button on code
|
|
|
|
|
|
{%- if site.enable_copy_code_button != false %}
|
|
|
|
jtd.onReady(function(){
|
|
|
|
var codeBlocks = document.querySelectorAll('div.highlighter-rouge, div.listingblock, figure.highlight');
|
|
|
|
var svgCopied = '<svg viewBox="0 0 24 24" class="copy-icon"><use xlink:href="#svg-copied"></use></svg>';
|
|
var svgCopy = '<svg viewBox="0 0 24 24" class="copy-icon"><use xlink:href="#svg-copy"></use></svg>';
|
|
|
|
codeBlocks.forEach(codeBlock => {
|
|
var copyButton = document.createElement('button');
|
|
var timeout = null;
|
|
copyButton.type = 'button';
|
|
copyButton.ariaLabel = 'Copy code to clipboard';
|
|
copyButton.innerHTML = svgCopy;
|
|
codeBlock.append(copyButton);
|
|
|
|
copyButton.addEventListener('click', function () {
|
|
if(timeout === null) {
|
|
var code = codeBlock.querySelector('code').innerText.trim();
|
|
window.navigator.clipboard.writeText(code);
|
|
|
|
copyButton.innerHTML = svgCopied;
|
|
|
|
var timeoutSetting = 4000;
|
|
|
|
timeout = setTimeout(function () {
|
|
copyButton.innerHTML = svgCopy;
|
|
timeout = null;
|
|
}, timeoutSetting);
|
|
}
|
|
});
|
|
});
|
|
|
|
});
|
|
|
|
{%- endif %}
|
|
|
|
})(window.jtd = window.jtd || {});
|
|
|
|
{% include js/custom.js %}
|