···1616 }
1717}
18181919-function selectRange($list, $select, $from) {
2020- $list.removeClass('active');
1919+function isBlame() {
2020+ return Boolean(document.querySelector('div.blame'));
2121+}
2222+2323+function getLineEls() {
2424+ return document.querySelectorAll(`.code-view td.lines-code${isBlame() ? '.blame-code' : ''}`);
2525+}
2626+2727+function selectRange($linesEls, $selectionEndEl, $selectionStartEls) {
2828+ $linesEls.closest('tr').removeClass('active');
21292230 // add hashchange to permalink
2331 const $refInNewIssue = $('a.ref-in-new-issue');
···2533 const $viewGitBlame = $('a.view_git_blame');
26342735 const updateIssueHref = function (anchor) {
2828- if ($refInNewIssue.length === 0) {
3636+ if (!$refInNewIssue.length) {
2937 return;
3038 }
3139 const urlIssueNew = $refInNewIssue.attr('data-url-issue-new');
···3543 };
36443745 const updateViewGitBlameFragment = function (anchor) {
3838- if ($viewGitBlame.length === 0) {
3939- return;
4040- }
4646+ if (!$viewGitBlame.length) return;
4147 let href = $viewGitBlame.attr('href');
4248 href = `${href.replace(/#L\d+$|#L\d+-L\d+$/, '')}`;
4349 if (anchor.length !== 0) {
···4753 };
48544955 const updateCopyPermalinkUrl = function(anchor) {
5050- if ($copyPermalink.length === 0) {
5151- return;
5252- }
5656+ if (!$copyPermalink.length) return;
5357 let link = $copyPermalink.attr('data-url');
5458 link = `${link.replace(/#L\d+$|#L\d+-L\d+$/, '')}#${anchor}`;
5559 $copyPermalink.attr('data-url', link);
5660 };
57615858- if ($from) {
5959- let a = parseInt($select.attr('rel').slice(1));
6060- let b = parseInt($from.attr('rel').slice(1));
6262+ if ($selectionStartEls) {
6363+ let a = parseInt($selectionEndEl.attr('rel').slice(1));
6464+ let b = parseInt($selectionStartEls.attr('rel').slice(1));
6165 let c;
6266 if (a !== b) {
6367 if (a > b) {
···6973 for (let i = a; i <= b; i++) {
7074 classes.push(`[rel=L${i}]`);
7175 }
7272- $list.filter(classes.join(',')).addClass('active');
7676+ $linesEls.filter(classes.join(',')).each(function () {
7777+ $(this).closest('tr').addClass('active');
7878+ });
7379 changeHash(`#L${a}-L${b}`);
74807581 updateIssueHref(`L${a}-L${b}`);
···7884 return;
7985 }
8086 }
8181- $select.addClass('active');
8282- changeHash(`#${$select.attr('rel')}`);
8787+ $selectionEndEl.closest('tr').addClass('active');
8888+ changeHash(`#${$selectionEndEl.attr('rel')}`);
83898484- updateIssueHref($select.attr('rel'));
8585- updateViewGitBlameFragment($select.attr('rel'));
8686- updateCopyPermalinkUrl($select.attr('rel'));
9090+ updateIssueHref($selectionEndEl.attr('rel'));
9191+ updateViewGitBlameFragment($selectionEndEl.attr('rel'));
9292+ updateCopyPermalinkUrl($selectionEndEl.attr('rel'));
8793}
88948995function showLineButton() {
···96102 }
9710398104 // find active row and add button
9999- const tr = document.querySelector('.code-view td.lines-code.active').closest('tr');
100100- const td = tr.querySelector('td');
105105+ const tr = document.querySelector('.code-view tr.active');
106106+ const td = tr.querySelector('td.lines-num');
101107 const btn = document.createElement('button');
102102- btn.classList.add('code-line-button');
108108+ btn.classList.add('code-line-button', 'ui', 'basic', 'button');
103109 btn.innerHTML = svg('octicon-kebab-horizontal');
104110 td.prepend(btn);
105111···123129export function initRepoCodeView() {
124130 if ($('.code-view .lines-num').length > 0) {
125131 $(document).on('click', '.lines-num span', function (e) {
126126- const $select = $(this);
127127- let $list;
128128- if ($('div.blame').length) {
129129- $list = $('.code-view td.lines-code.blame-code');
130130- } else {
131131- $list = $('.code-view td.lines-code');
132132+ const linesEls = getLineEls();
133133+ const selectedEls = Array.from(linesEls).filter((el) => {
134134+ return el.matches(`[rel=${this.getAttribute('id')}]`);
135135+ });
136136+137137+ let from;
138138+ if (e.shiftKey) {
139139+ from = Array.from(linesEls).filter((el) => {
140140+ return el.closest('tr').classList.contains('active');
141141+ });
132142 }
133133- selectRange($list, $list.filter(`[rel=${$select.attr('id')}]`), (e.shiftKey ? $list.filter('.active').eq(0) : null));
143143+ selectRange($(linesEls), $(selectedEls), from ? $(from) : null);
134144135145 if (window.getSelection) {
136146 window.getSelection().removeAllRanges();
···138148 document.selection.empty();
139149 }
140150141141- // show code view menu marker (don't show in blame page)
142142- if ($('div.blame').length === 0) {
143143- showLineButton();
144144- }
151151+ showLineButton();
145152 });
146153147154 $(window).on('hashchange', () => {
148155 let m = window.location.hash.match(rangeAnchorRegex);
149149- let $list;
150150- if ($('div.blame').length) {
151151- $list = $('.code-view td.lines-code.blame-code');
152152- } else {
153153- $list = $('.code-view td.lines-code');
154154- }
156156+ const $linesEls = $(getLineEls());
155157 let $first;
156158 if (m) {
157157- $first = $list.filter(`[rel=${m[1]}]`);
159159+ $first = $linesEls.filter(`[rel=${m[1]}]`);
158160 if ($first.length) {
159159- selectRange($list, $first, $list.filter(`[rel=${m[2]}]`));
161161+ selectRange($linesEls, $first, $linesEls.filter(`[rel=${m[2]}]`));
160162161163 // show code view menu marker (don't show in blame page)
162162- if ($('div.blame').length === 0) {
164164+ if (!isBlame()) {
163165 showLineButton();
164166 }
165167···169171 }
170172 m = window.location.hash.match(singleAnchorRegex);
171173 if (m) {
172172- $first = $list.filter(`[rel=L${m[2]}]`);
174174+ $first = $linesEls.filter(`[rel=L${m[2]}]`);
173175 if ($first.length) {
174174- selectRange($list, $first);
176176+ selectRange($linesEls, $first);
175177176178 // show code view menu marker (don't show in blame page)
177177- if ($('div.blame').length === 0) {
179179+ if (!isBlame()) {
178180 showLineButton();
179181 }
180182
+19
web_src/js/webcomponents/overflow-menu.js
···127127 });
128128129129 init() {
130130+ // for horizontal menus where fomantic boldens active items, prevent this bold text from
131131+ // enlarging the menu's active item replacing the text node with a div that renders a
132132+ // invisible pseudo-element that enlarges the box.
133133+ if (this.matches('.ui.secondary.pointing.menu, .ui.tabular.menu')) {
134134+ for (const item of this.querySelectorAll('.item')) {
135135+ for (const child of item.childNodes) {
136136+ if (child.nodeType === Node.TEXT_NODE) {
137137+ const text = child.textContent.trim(); // whitespace is insignificant inside flexbox
138138+ if (!text) continue;
139139+ const span = document.createElement('span');
140140+ span.classList.add('resize-for-semibold');
141141+ span.setAttribute('data-text', text);
142142+ span.textContent = text;
143143+ child.replaceWith(span);
144144+ }
145145+ }
146146+ }
147147+ }
148148+130149 // ResizeObserver triggers on initial render, so we don't manually call `updateItems` here which
131150 // also avoids a full-page FOUC in Firefox that happens when `updateItems` is called too soon.
132151 this.resizeObserver = new ResizeObserver((entries) => {