mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-29 02:24:57 +01:00 
			
		
		
		
	- Switched from jQuery `attr` to plain javascript `getAttribute` and `setAttribute` - Tested the code line range selection and it works as before --------- Signed-off-by: Yarden Shoham <git@yardenshoham.com> Co-authored-by: silverwind <me@silverwind.io>
		
			
				
	
	
		
			193 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			193 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import $ from 'jquery';
 | |
| import {svg} from '../svg.js';
 | |
| import {invertFileFolding} from './file-fold.js';
 | |
| import {createTippy} from '../modules/tippy.js';
 | |
| import {clippie} from 'clippie';
 | |
| import {toAbsoluteUrl} from '../utils.js';
 | |
| 
 | |
| export const singleAnchorRegex = /^#(L|n)([1-9][0-9]*)$/;
 | |
| export const rangeAnchorRegex = /^#(L[1-9][0-9]*)-(L[1-9][0-9]*)$/;
 | |
| 
 | |
| function changeHash(hash) {
 | |
|   if (window.history.pushState) {
 | |
|     window.history.pushState(null, null, hash);
 | |
|   } else {
 | |
|     window.location.hash = hash;
 | |
|   }
 | |
| }
 | |
| 
 | |
| function isBlame() {
 | |
|   return Boolean(document.querySelector('div.blame'));
 | |
| }
 | |
| 
 | |
| function getLineEls() {
 | |
|   return document.querySelectorAll(`.code-view td.lines-code${isBlame() ? '.blame-code' : ''}`);
 | |
| }
 | |
| 
 | |
| function selectRange($linesEls, $selectionEndEl, $selectionStartEls) {
 | |
|   $linesEls.closest('tr').removeClass('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');
 | |
| 
 | |
|   const updateIssueHref = function (anchor) {
 | |
|     if (!refInNewIssue) return;
 | |
|     const urlIssueNew = refInNewIssue.getAttribute('data-url-issue-new');
 | |
|     const urlParamBodyLink = refInNewIssue.getAttribute('data-url-param-body-link');
 | |
|     const issueContent = `${toAbsoluteUrl(urlParamBodyLink)}#${anchor}`; // the default content for issue body
 | |
|     refInNewIssue.setAttribute('href', `${urlIssueNew}?body=${encodeURIComponent(issueContent)}`);
 | |
|   };
 | |
| 
 | |
|   const updateViewGitBlameFragment = function (anchor) {
 | |
|     if (!viewGitBlame) return;
 | |
|     let href = viewGitBlame.getAttribute('href');
 | |
|     href = `${href.replace(/#L\d+$|#L\d+-L\d+$/, '')}`;
 | |
|     if (anchor.length !== 0) {
 | |
|       href = `${href}#${anchor}`;
 | |
|     }
 | |
|     viewGitBlame.setAttribute('href', href);
 | |
|   };
 | |
| 
 | |
|   const updateCopyPermalinkUrl = function (anchor) {
 | |
|     if (!copyPermalink) return;
 | |
|     let link = copyPermalink.getAttribute('data-url');
 | |
|     link = `${link.replace(/#L\d+$|#L\d+-L\d+$/, '')}#${anchor}`;
 | |
|     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').addClass('active');
 | |
|       });
 | |
|       changeHash(`#L${a}-L${b}`);
 | |
| 
 | |
|       updateIssueHref(`L${a}-L${b}`);
 | |
|       updateViewGitBlameFragment(`L${a}-L${b}`);
 | |
|       updateCopyPermalinkUrl(`L${a}-L${b}`);
 | |
|       return;
 | |
|     }
 | |
|   }
 | |
|   $selectionEndEl.closest('tr').addClass('active');
 | |
|   changeHash(`#${$selectionEndEl[0].getAttribute('rel')}`);
 | |
| 
 | |
|   updateIssueHref($selectionEndEl[0].getAttribute('rel'));
 | |
|   updateViewGitBlameFragment($selectionEndEl[0].getAttribute('rel'));
 | |
|   updateCopyPermalinkUrl($selectionEndEl[0].getAttribute('rel'));
 | |
| }
 | |
| 
 | |
| function showLineButton() {
 | |
|   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
 | |
|   const tr = document.querySelector('.code-view tr.active');
 | |
|   const td = tr.querySelector('td.lines-num');
 | |
|   const btn = document.createElement('button');
 | |
|   btn.classList.add('code-line-button', 'ui', 'basic', 'button');
 | |
|   btn.innerHTML = svg('octicon-kebab-horizontal');
 | |
|   td.prepend(btn);
 | |
| 
 | |
|   // put a copy of the menu back into DOM for the next click
 | |
|   btn.closest('.code-view').append(menu.cloneNode(true));
 | |
| 
 | |
|   createTippy(btn, {
 | |
|     trigger: 'click',
 | |
|     hideOnClick: true,
 | |
|     content: menu,
 | |
|     placement: 'right-start',
 | |
|     interactive: true,
 | |
|     onShow: (tippy) => {
 | |
|       tippy.popper.addEventListener('click', () => {
 | |
|         tippy.hide();
 | |
|       }, {once: true});
 | |
|     },
 | |
|   });
 | |
| }
 | |
| 
 | |
| 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);
 | |
| 
 | |
|       if (window.getSelection) {
 | |
|         window.getSelection().removeAllRanges();
 | |
|       } else {
 | |
|         document.selection.empty();
 | |
|       }
 | |
| 
 | |
|       showLineButton();
 | |
|     });
 | |
| 
 | |
|     $(window).on('hashchange', () => {
 | |
|       let m = window.location.hash.match(rangeAnchorRegex);
 | |
|       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 = window.location.hash.match(singleAnchorRegex);
 | |
|       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);
 | |
|   });
 | |
|   $(document).on('click', '.copy-line-permalink', async ({currentTarget}) => {
 | |
|     await clippie(toAbsoluteUrl(currentTarget.getAttribute('data-url')));
 | |
|   });
 | |
| }
 |