2022-01-29 05:00:11 +08:00
|
|
|
import $ from 'jquery';
|
2024-07-07 23:32:30 +08:00
|
|
|
import {svg} from '../svg.ts';
|
|
|
|
import {invertFileFolding} from './file-fold.ts';
|
|
|
|
import {createTippy} from '../modules/tippy.ts';
|
2023-04-02 17:25:36 +08:00
|
|
|
import {clippie} from 'clippie';
|
2024-07-07 23:32:30 +08:00
|
|
|
import {toAbsoluteUrl} from '../utils.ts';
|
2021-10-17 01:28:04 +08:00
|
|
|
|
2022-11-11 18:22:36 +08:00
|
|
|
export const singleAnchorRegex = /^#(L|n)([1-9][0-9]*)$/;
|
|
|
|
export const rangeAnchorRegex = /^#(L[1-9][0-9]*)-(L[1-9][0-9]*)$/;
|
2022-11-05 03:33:50 +08:00
|
|
|
|
2024-12-11 16:29:04 +08:00
|
|
|
function changeHash(hash: string) {
|
2021-10-17 01:28:04 +08:00
|
|
|
if (window.history.pushState) {
|
|
|
|
window.history.pushState(null, null, hash);
|
|
|
|
} else {
|
|
|
|
window.location.hash = hash;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-24 20:14:03 +08:00
|
|
|
function isBlame() {
|
|
|
|
return Boolean(document.querySelector('div.blame'));
|
|
|
|
}
|
|
|
|
|
|
|
|
function getLineEls() {
|
|
|
|
return document.querySelectorAll(`.code-view td.lines-code${isBlame() ? '.blame-code' : ''}`);
|
|
|
|
}
|
|
|
|
|
2024-12-11 16:29:04 +08:00
|
|
|
function selectRange($linesEls, $selectionEndEl, $selectionStartEls?) {
|
2024-03-30 01:08:54 +08:00
|
|
|
for (const el of $linesEls) {
|
|
|
|
el.closest('tr').classList.remove('active');
|
|
|
|
}
|
2021-10-17 01:28:04 +08:00
|
|
|
|
|
|
|
// add hashchange to permalink
|
2024-03-26 07:03:12 +08:00
|
|
|
const refInNewIssue = document.querySelector('a.ref-in-new-issue');
|
|
|
|
const copyPermalink = document.querySelector('a.copy-line-permalink');
|
|
|
|
const viewGitBlame = document.querySelector('a.view_git_blame');
|
2021-10-17 01:28:04 +08:00
|
|
|
|
2024-12-11 16:29:04 +08:00
|
|
|
const updateIssueHref = function (anchor: string) {
|
2024-03-26 07:03:12 +08:00
|
|
|
if (!refInNewIssue) return;
|
|
|
|
const urlIssueNew = refInNewIssue.getAttribute('data-url-issue-new');
|
|
|
|
const urlParamBodyLink = refInNewIssue.getAttribute('data-url-param-body-link');
|
2023-02-08 00:08:44 +08:00
|
|
|
const issueContent = `${toAbsoluteUrl(urlParamBodyLink)}#${anchor}`; // the default content for issue body
|
2024-03-26 07:03:12 +08:00
|
|
|
refInNewIssue.setAttribute('href', `${urlIssueNew}?body=${encodeURIComponent(issueContent)}`);
|
2021-10-17 01:28:04 +08:00
|
|
|
};
|
|
|
|
|
2024-12-11 16:29:04 +08:00
|
|
|
const updateViewGitBlameFragment = function (anchor: string) {
|
2024-03-26 07:03:12 +08:00
|
|
|
if (!viewGitBlame) return;
|
|
|
|
let href = viewGitBlame.getAttribute('href');
|
2022-04-26 18:54:40 +08:00
|
|
|
href = `${href.replace(/#L\d+$|#L\d+-L\d+$/, '')}`;
|
|
|
|
if (anchor.length !== 0) {
|
|
|
|
href = `${href}#${anchor}`;
|
|
|
|
}
|
2024-03-26 07:03:12 +08:00
|
|
|
viewGitBlame.setAttribute('href', href);
|
2022-04-26 18:54:40 +08:00
|
|
|
};
|
|
|
|
|
2024-12-11 16:29:04 +08:00
|
|
|
const updateCopyPermalinkUrl = function (anchor: string) {
|
2024-03-26 07:03:12 +08:00
|
|
|
if (!copyPermalink) return;
|
|
|
|
let link = copyPermalink.getAttribute('data-url');
|
2021-10-17 01:28:04 +08:00
|
|
|
link = `${link.replace(/#L\d+$|#L\d+-L\d+$/, '')}#${anchor}`;
|
2024-03-26 07:03:12 +08:00
|
|
|
copyPermalink.setAttribute('data-url', link);
|
2021-10-17 01:28:04 +08:00
|
|
|
};
|
|
|
|
|
2024-03-24 20:14:03 +08:00
|
|
|
if ($selectionStartEls) {
|
2024-03-26 07:03:12 +08:00
|
|
|
let a = parseInt($selectionEndEl[0].getAttribute('rel').slice(1));
|
|
|
|
let b = parseInt($selectionStartEls[0].getAttribute('rel').slice(1));
|
2021-10-17 01:28:04 +08:00
|
|
|
let c;
|
|
|
|
if (a !== b) {
|
|
|
|
if (a > b) {
|
|
|
|
c = a;
|
|
|
|
a = b;
|
|
|
|
b = c;
|
|
|
|
}
|
|
|
|
const classes = [];
|
|
|
|
for (let i = a; i <= b; i++) {
|
|
|
|
classes.push(`[rel=L${i}]`);
|
|
|
|
}
|
2024-03-24 20:14:03 +08:00
|
|
|
$linesEls.filter(classes.join(',')).each(function () {
|
2024-03-30 01:08:54 +08:00
|
|
|
this.closest('tr').classList.add('active');
|
2024-03-24 20:14:03 +08:00
|
|
|
});
|
2021-10-17 01:28:04 +08:00
|
|
|
changeHash(`#L${a}-L${b}`);
|
|
|
|
|
|
|
|
updateIssueHref(`L${a}-L${b}`);
|
2022-04-26 18:54:40 +08:00
|
|
|
updateViewGitBlameFragment(`L${a}-L${b}`);
|
2022-08-09 20:37:34 +08:00
|
|
|
updateCopyPermalinkUrl(`L${a}-L${b}`);
|
2021-10-17 01:28:04 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2024-03-30 01:08:54 +08:00
|
|
|
$selectionEndEl[0].closest('tr').classList.add('active');
|
2024-03-26 07:03:12 +08:00
|
|
|
changeHash(`#${$selectionEndEl[0].getAttribute('rel')}`);
|
2021-10-17 01:28:04 +08:00
|
|
|
|
2024-03-26 07:03:12 +08:00
|
|
|
updateIssueHref($selectionEndEl[0].getAttribute('rel'));
|
|
|
|
updateViewGitBlameFragment($selectionEndEl[0].getAttribute('rel'));
|
|
|
|
updateCopyPermalinkUrl($selectionEndEl[0].getAttribute('rel'));
|
2021-10-17 01:28:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function showLineButton() {
|
2022-08-09 20:37:34 +08:00
|
|
|
const menu = document.querySelector('.code-line-menu');
|
|
|
|
if (!menu) return;
|
|
|
|
|
|
|
|
// remove all other line buttons
|
|
|
|
for (const el of document.querySelectorAll('.code-line-button')) {
|
|
|
|
el.remove();
|
|
|
|
}
|
|
|
|
|
|
|
|
// find active row and add button
|
2024-03-24 20:14:03 +08:00
|
|
|
const tr = document.querySelector('.code-view tr.active');
|
|
|
|
const td = tr.querySelector('td.lines-num');
|
2022-08-09 20:37:34 +08:00
|
|
|
const btn = document.createElement('button');
|
2024-03-24 20:14:03 +08:00
|
|
|
btn.classList.add('code-line-button', 'ui', 'basic', 'button');
|
2022-08-09 20:37:34 +08:00
|
|
|
btn.innerHTML = svg('octicon-kebab-horizontal');
|
|
|
|
td.prepend(btn);
|
|
|
|
|
|
|
|
// put a copy of the menu back into DOM for the next click
|
2023-05-09 10:35:49 +08:00
|
|
|
btn.closest('.code-view').append(menu.cloneNode(true));
|
2022-08-09 20:37:34 +08:00
|
|
|
|
|
|
|
createTippy(btn, {
|
2024-04-30 22:52:46 +08:00
|
|
|
theme: 'menu',
|
2022-08-09 20:37:34 +08:00
|
|
|
trigger: 'click',
|
2023-06-09 17:10:51 +08:00
|
|
|
hideOnClick: true,
|
2022-08-09 20:37:34 +08:00
|
|
|
content: menu,
|
|
|
|
placement: 'right-start',
|
2023-06-14 16:01:37 +08:00
|
|
|
interactive: true,
|
2023-06-09 17:10:51 +08:00
|
|
|
onShow: (tippy) => {
|
|
|
|
tippy.popper.addEventListener('click', () => {
|
|
|
|
tippy.hide();
|
|
|
|
}, {once: true});
|
2024-03-22 22:06:53 +08:00
|
|
|
},
|
2022-08-09 20:37:34 +08:00
|
|
|
});
|
2021-10-17 01:28:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
export function initRepoCodeView() {
|
|
|
|
if ($('.code-view .lines-num').length > 0) {
|
|
|
|
$(document).on('click', '.lines-num span', function (e) {
|
2024-03-24 20:14:03 +08:00
|
|
|
const linesEls = getLineEls();
|
|
|
|
const selectedEls = Array.from(linesEls).filter((el) => {
|
|
|
|
return el.matches(`[rel=${this.getAttribute('id')}]`);
|
|
|
|
});
|
|
|
|
|
|
|
|
let from;
|
|
|
|
if (e.shiftKey) {
|
|
|
|
from = Array.from(linesEls).filter((el) => {
|
|
|
|
return el.closest('tr').classList.contains('active');
|
|
|
|
});
|
2021-10-17 01:28:04 +08:00
|
|
|
}
|
2024-03-24 20:14:03 +08:00
|
|
|
selectRange($(linesEls), $(selectedEls), from ? $(from) : null);
|
2024-12-11 16:29:04 +08:00
|
|
|
window.getSelection().removeAllRanges();
|
2024-03-24 20:14:03 +08:00
|
|
|
showLineButton();
|
2021-10-17 01:28:04 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
$(window).on('hashchange', () => {
|
2024-07-03 23:48:14 +08:00
|
|
|
let m = rangeAnchorRegex.exec(window.location.hash);
|
2024-03-24 20:14:03 +08:00
|
|
|
const $linesEls = $(getLineEls());
|
2021-10-17 01:28:04 +08:00
|
|
|
let $first;
|
|
|
|
if (m) {
|
2024-03-24 20:14:03 +08:00
|
|
|
$first = $linesEls.filter(`[rel=${m[1]}]`);
|
2022-11-11 18:22:36 +08:00
|
|
|
if ($first.length) {
|
2024-03-24 20:14:03 +08:00
|
|
|
selectRange($linesEls, $first, $linesEls.filter(`[rel=${m[2]}]`));
|
2021-10-17 01:28:04 +08:00
|
|
|
|
2022-11-11 18:22:36 +08:00
|
|
|
// show code view menu marker (don't show in blame page)
|
2024-03-24 20:14:03 +08:00
|
|
|
if (!isBlame()) {
|
2022-11-11 18:22:36 +08:00
|
|
|
showLineButton();
|
|
|
|
}
|
2021-10-17 01:28:04 +08:00
|
|
|
|
2022-11-11 18:22:36 +08:00
|
|
|
$('html, body').scrollTop($first.offset().top - 200);
|
|
|
|
return;
|
|
|
|
}
|
2021-10-17 01:28:04 +08:00
|
|
|
}
|
2024-07-03 23:48:14 +08:00
|
|
|
m = singleAnchorRegex.exec(window.location.hash);
|
2021-10-17 01:28:04 +08:00
|
|
|
if (m) {
|
2024-03-24 20:14:03 +08:00
|
|
|
$first = $linesEls.filter(`[rel=L${m[2]}]`);
|
2022-11-11 18:22:36 +08:00
|
|
|
if ($first.length) {
|
2024-03-24 20:14:03 +08:00
|
|
|
selectRange($linesEls, $first);
|
2021-10-17 01:28:04 +08:00
|
|
|
|
2022-11-11 18:22:36 +08:00
|
|
|
// show code view menu marker (don't show in blame page)
|
2024-03-24 20:14:03 +08:00
|
|
|
if (!isBlame()) {
|
2022-11-11 18:22:36 +08:00
|
|
|
showLineButton();
|
|
|
|
}
|
2021-10-17 01:28:04 +08:00
|
|
|
|
2022-11-11 18:22:36 +08:00
|
|
|
$('html, body').scrollTop($first.offset().top - 200);
|
|
|
|
}
|
2021-10-17 01:28:04 +08:00
|
|
|
}
|
|
|
|
}).trigger('hashchange');
|
|
|
|
}
|
|
|
|
$(document).on('click', '.fold-file', ({currentTarget}) => {
|
2022-05-08 02:28:10 +08:00
|
|
|
invertFileFolding(currentTarget.closest('.file-content'), currentTarget);
|
2021-10-17 01:28:04 +08:00
|
|
|
});
|
2024-02-17 05:41:23 +08:00
|
|
|
$(document).on('click', '.copy-line-permalink', async ({currentTarget}) => {
|
|
|
|
await clippie(toAbsoluteUrl(currentTarget.getAttribute('data-url')));
|
2022-08-09 20:37:34 +08:00
|
|
|
});
|
2021-10-17 01:28:04 +08:00
|
|
|
}
|