aboutsummaryrefslogtreecommitdiffstats
path: root/web_src/js/features/repo-code.ts
diff options
context:
space:
mode:
Diffstat (limited to 'web_src/js/features/repo-code.ts')
-rw-r--r--web_src/js/features/repo-code.ts162
1 files changed, 59 insertions, 103 deletions
diff --git a/web_src/js/features/repo-code.ts b/web_src/js/features/repo-code.ts
index a8d6e8f97d..207022ca42 100644
--- a/web_src/js/features/repo-code.ts
+++ b/web_src/js/features/repo-code.ts
@@ -1,12 +1,8 @@
-import $ from 'jquery';
import {svg} from '../svg.ts';
-import {invertFileFolding} from './file-fold.ts';
import {createTippy} from '../modules/tippy.ts';
import {clippie} from 'clippie';
import {toAbsoluteUrl} from '../utils.ts';
-
-export const singleAnchorRegex = /^#(L|n)([1-9][0-9]*)$/;
-export const rangeAnchorRegex = /^#(L[1-9][0-9]*)-(L[1-9][0-9]*)$/;
+import {addDelegatedEventListener} from '../utils/dom.ts';
function changeHash(hash: string) {
if (window.history.pushState) {
@@ -16,20 +12,11 @@ function changeHash(hash: string) {
}
}
-function isBlame() {
- return Boolean(document.querySelector('div.blame'));
-}
+// it selects the code lines defined by range: `L1-L3` (3 lines) or `L2` (singe line)
+function selectRange(range: string): Element {
+ for (const el of document.querySelectorAll('.code-view tr.active')) el.classList.remove('active');
+ const elLineNums = document.querySelectorAll(`.code-view td.lines-num span[data-line-number]`);
-function getLineEls() {
- return document.querySelectorAll(`.code-view td.lines-code${isBlame() ? '.blame-code' : ''}`);
-}
-
-function selectRange($linesEls, $selectionEndEl, $selectionStartEls?) {
- for (const el of $linesEls) {
- el.closest('tr').classList.remove('active');
- }
-
- // add hashchange to permalink
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');
@@ -59,37 +46,30 @@ function selectRange($linesEls, $selectionEndEl, $selectionStartEls?) {
copyPermalink.setAttribute('data-url', link);
};
- if ($selectionStartEls) {
- let a = parseInt($selectionEndEl[0].getAttribute('rel').slice(1));
- let b = parseInt($selectionStartEls[0].getAttribute('rel').slice(1));
- 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}]`);
- }
- $linesEls.filter(classes.join(',')).each(function () {
- this.closest('tr').classList.add('active');
- });
- changeHash(`#L${a}-L${b}`);
-
- updateIssueHref(`L${a}-L${b}`);
- updateViewGitBlameFragment(`L${a}-L${b}`);
- updateCopyPermalinkUrl(`L${a}-L${b}`);
- return;
- }
+ const rangeFields = range ? range.split('-') : [];
+ const start = rangeFields[0] ?? '';
+ if (!start) return null;
+ const stop = rangeFields[1] || start;
+
+ // format is i.e. 'L14-L26'
+ let startLineNum = parseInt(start.substring(1));
+ let stopLineNum = parseInt(stop.substring(1));
+ if (startLineNum > stopLineNum) {
+ const tmp = startLineNum;
+ startLineNum = stopLineNum;
+ stopLineNum = tmp;
+ range = `${stop}-${start}`;
}
- $selectionEndEl[0].closest('tr').classList.add('active');
- changeHash(`#${$selectionEndEl[0].getAttribute('rel')}`);
- updateIssueHref($selectionEndEl[0].getAttribute('rel'));
- updateViewGitBlameFragment($selectionEndEl[0].getAttribute('rel'));
- updateCopyPermalinkUrl($selectionEndEl[0].getAttribute('rel'));
+ const first = elLineNums[startLineNum - 1] ?? null;
+ for (let i = startLineNum - 1; i <= stopLineNum - 1 && i < elLineNums.length; i++) {
+ elLineNums[i].closest('tr').classList.add('active');
+ }
+ changeHash(`#${range}`);
+ updateIssueHref(range);
+ updateViewGitBlameFragment(range);
+ updateCopyPermalinkUrl(range);
+ return first;
}
function showLineButton() {
@@ -103,6 +83,8 @@ function showLineButton() {
// find active row and add button
const tr = document.querySelector('.code-view tr.active');
+ if (!tr) return;
+
const td = tr.querySelector('td.lines-num');
const btn = document.createElement('button');
btn.classList.add('code-line-button', 'ui', 'basic', 'button');
@@ -128,62 +110,36 @@ function showLineButton() {
}
export function initRepoCodeView() {
- if ($('.code-view .lines-num').length > 0) {
- $(document).on('click', '.lines-num span', function (e) {
- 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');
- });
- }
- selectRange($(linesEls), $(selectedEls), from ? $(from) : null);
- window.getSelection().removeAllRanges();
- showLineButton();
- });
-
- $(window).on('hashchange', () => {
- let m = rangeAnchorRegex.exec(window.location.hash);
- const $linesEls = $(getLineEls());
- let $first;
- if (m) {
- $first = $linesEls.filter(`[rel=${m[1]}]`);
- if ($first.length) {
- selectRange($linesEls, $first, $linesEls.filter(`[rel=${m[2]}]`));
-
- // show code view menu marker (don't show in blame page)
- if (!isBlame()) {
- showLineButton();
- }
-
- $('html, body').scrollTop($first.offset().top - 200);
- return;
- }
- }
- m = singleAnchorRegex.exec(window.location.hash);
- if (m) {
- $first = $linesEls.filter(`[rel=L${m[2]}]`);
- if ($first.length) {
- selectRange($linesEls, $first);
-
- // show code view menu marker (don't show in blame page)
- if (!isBlame()) {
- showLineButton();
- }
-
- $('html, body').scrollTop($first.offset().top - 200);
- }
- }
- }).trigger('hashchange');
- }
- $(document).on('click', '.fold-file', ({currentTarget}) => {
- invertFileFolding(currentTarget.closest('.file-content'), currentTarget);
+ if (!document.querySelector('.code-view .lines-num')) return;
+
+ let selRangeStart: string;
+ addDelegatedEventListener(document, 'click', '.lines-num span', (el: HTMLElement, e: KeyboardEvent) => {
+ if (!selRangeStart || !e.shiftKey) {
+ selRangeStart = el.getAttribute('id');
+ selectRange(selRangeStart);
+ } else {
+ const selRangeStop = el.getAttribute('id');
+ selectRange(`${selRangeStart}-${selRangeStop}`);
+ }
+ window.getSelection().removeAllRanges();
+ showLineButton();
});
- $(document).on('click', '.copy-line-permalink', async ({currentTarget}) => {
- await clippie(toAbsoluteUrl(currentTarget.getAttribute('data-url')));
+
+ const onHashChange = () => {
+ if (!window.location.hash) return;
+ const range = window.location.hash.substring(1);
+ const first = selectRange(range);
+ if (first) {
+ // set scrollRestoration to 'manual' when there is a hash in url, so that the scroll position will not be remembered after refreshing
+ if (window.history.scrollRestoration !== 'manual') window.history.scrollRestoration = 'manual';
+ first.scrollIntoView({block: 'start'});
+ showLineButton();
+ }
+ };
+ onHashChange();
+ window.addEventListener('hashchange', onHashChange);
+
+ addDelegatedEventListener(document, 'click', '.copy-line-permalink', (el) => {
+ clippie(toAbsoluteUrl(el.getAttribute('data-url')));
});
}