2018-03-09 15:18:59 -05:00
|
|
|
import $ from 'jquery';
|
2022-02-22 10:18:06 -05:00
|
|
|
import { merge } from 'lodash';
|
2021-06-17 20:10:29 -04:00
|
|
|
import createFlash from '~/flash';
|
2021-02-14 13:09:20 -05:00
|
|
|
import axios from '~/lib/utils/axios_utils';
|
2018-02-08 03:47:31 -05:00
|
|
|
import { __ } from '~/locale';
|
2017-06-12 14:43:21 -04:00
|
|
|
import FilesCommentButton from './files_comment_button';
|
2019-09-18 10:02:45 -04:00
|
|
|
import initImageDiffHelper from './image_diff/helpers/init_image_diff';
|
2021-02-14 13:09:20 -05:00
|
|
|
import { getLocationHash } from './lib/utils/url_utility';
|
|
|
|
import SingleFileDiff from './single_file_diff';
|
2017-01-25 15:34:49 -05:00
|
|
|
|
2017-03-11 02:30:44 -05:00
|
|
|
const UNFOLD_COUNT = 20;
|
|
|
|
let isBound = false;
|
2016-07-24 16:45:11 -04:00
|
|
|
|
2017-10-19 05:46:43 -04:00
|
|
|
export default class Diff {
|
2017-03-11 02:30:44 -05:00
|
|
|
constructor() {
|
|
|
|
const $diffFile = $('.files .diff-file');
|
2017-06-12 14:43:21 -04:00
|
|
|
|
2017-07-06 14:39:28 -04:00
|
|
|
$diffFile.each((index, file) => {
|
|
|
|
if (!$.data(file, 'singleFileDiff')) {
|
|
|
|
$.data(file, 'singleFileDiff', new SingleFileDiff(file));
|
|
|
|
}
|
|
|
|
});
|
2017-06-12 14:43:21 -04:00
|
|
|
|
2017-09-14 09:35:57 -04:00
|
|
|
const tab = document.getElementById('diffs');
|
2018-10-24 15:17:03 -04:00
|
|
|
if (!tab || (tab && tab.dataset && tab.dataset.isLocked !== ''))
|
|
|
|
FilesCommentButton.init($diffFile);
|
2016-12-04 06:12:23 -05:00
|
|
|
|
2020-12-23 07:10:26 -05:00
|
|
|
const firstFile = $('.files').first().get(0);
|
2022-05-12 05:08:08 -04:00
|
|
|
const canCreateNote = firstFile && firstFile.hasAttribute('data-can-create-note');
|
2019-09-18 10:02:45 -04:00
|
|
|
$diffFile.each((index, file) => initImageDiffHelper.initImageDiff(file, canCreateNote));
|
2016-10-25 15:25:46 -04:00
|
|
|
|
2017-03-11 02:30:44 -05:00
|
|
|
if (!isBound) {
|
|
|
|
$(document)
|
|
|
|
.on('click', '.js-unfold', this.handleClickUnfold.bind(this))
|
2017-09-28 10:55:25 -04:00
|
|
|
.on('click', '.diff-line-num a', this.handleClickLineNum.bind(this))
|
|
|
|
.on('mousedown', 'td.line_content.parallel', this.handleParallelLineDown.bind(this));
|
2017-03-11 02:30:44 -05:00
|
|
|
isBound = true;
|
|
|
|
}
|
2017-03-02 08:43:52 -05:00
|
|
|
|
2017-12-07 07:30:53 -05:00
|
|
|
if (getLocationHash()) {
|
2017-03-11 02:30:44 -05:00
|
|
|
this.highlightSelectedLine();
|
2016-07-24 16:45:11 -04:00
|
|
|
}
|
|
|
|
|
2017-03-11 02:30:44 -05:00
|
|
|
this.openAnchoredDiff();
|
2022-02-22 10:18:06 -05:00
|
|
|
|
|
|
|
this.prepareRenderedDiff();
|
2017-03-11 02:30:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
handleClickUnfold(e) {
|
|
|
|
const $target = $(e.target);
|
2017-03-21 13:37:25 -04:00
|
|
|
const [oldLineNumber, newLineNumber] = this.lineNumbers($target.parent());
|
2017-03-11 02:30:44 -05:00
|
|
|
const offset = newLineNumber - oldLineNumber;
|
|
|
|
const bottom = $target.hasClass('js-unfold-bottom');
|
|
|
|
let since;
|
|
|
|
let to;
|
|
|
|
let unfold = true;
|
|
|
|
|
|
|
|
if (bottom) {
|
|
|
|
const lineNumber = newLineNumber + 1;
|
|
|
|
since = lineNumber;
|
|
|
|
to = lineNumber + UNFOLD_COUNT;
|
|
|
|
} else {
|
|
|
|
const lineNumber = newLineNumber - 1;
|
|
|
|
since = lineNumber - UNFOLD_COUNT;
|
|
|
|
to = lineNumber;
|
|
|
|
|
|
|
|
// make sure we aren't loading more than we need
|
|
|
|
const prevNewLine = this.lineNumbers($target.parent().prev())[1];
|
|
|
|
if (since <= prevNewLine + 1) {
|
|
|
|
since = prevNewLine + 1;
|
|
|
|
unfold = false;
|
2016-10-25 21:22:09 -04:00
|
|
|
}
|
2017-03-11 02:30:44 -05:00
|
|
|
}
|
2016-10-25 21:22:09 -04:00
|
|
|
|
2017-03-11 02:30:44 -05:00
|
|
|
const file = $target.parents('.diff-file');
|
2018-02-20 17:20:48 -05:00
|
|
|
const link = file.data('blobDiffPath');
|
2017-03-11 02:30:44 -05:00
|
|
|
const view = file.data('view');
|
2016-10-25 21:22:09 -04:00
|
|
|
|
2017-03-11 02:30:44 -05:00
|
|
|
const params = { since, to, bottom, offset, unfold, view };
|
2018-10-24 15:17:03 -04:00
|
|
|
axios
|
|
|
|
.get(link, { params })
|
|
|
|
.then(({ data }) => $target.parent().replaceWith(data))
|
2021-06-17 20:10:29 -04:00
|
|
|
.catch(() =>
|
|
|
|
createFlash({
|
|
|
|
message: __('An error occurred while loading diff'),
|
|
|
|
}),
|
|
|
|
);
|
2017-03-11 02:30:44 -05:00
|
|
|
}
|
2016-10-25 21:22:09 -04:00
|
|
|
|
2017-03-11 02:30:44 -05:00
|
|
|
openAnchoredDiff(cb) {
|
2017-12-07 07:30:53 -05:00
|
|
|
const locationHash = getLocationHash();
|
2017-03-11 02:30:44 -05:00
|
|
|
const anchoredDiff = locationHash && locationHash.split('_')[0];
|
2016-09-09 11:47:43 -04:00
|
|
|
|
2017-03-11 02:30:44 -05:00
|
|
|
if (!anchoredDiff) return;
|
|
|
|
|
|
|
|
const diffTitle = $(`#${anchoredDiff}`);
|
|
|
|
const diffFile = diffTitle.closest('.diff-file');
|
|
|
|
const nothingHereBlock = $('.nothing-here-block:visible', diffFile);
|
|
|
|
if (nothingHereBlock.length) {
|
|
|
|
const clickTarget = $('.js-file-title, .click-to-expand', diffFile);
|
|
|
|
diffFile.data('singleFileDiff').toggleDiff(clickTarget, () => {
|
|
|
|
this.highlightSelectedLine();
|
|
|
|
if (cb) cb();
|
|
|
|
});
|
|
|
|
} else if (cb) {
|
|
|
|
cb();
|
2016-10-27 16:32:30 -04:00
|
|
|
}
|
2017-03-11 02:30:44 -05:00
|
|
|
}
|
2016-10-25 21:22:09 -04:00
|
|
|
|
2017-03-11 02:30:44 -05:00
|
|
|
handleClickLineNum(e) {
|
|
|
|
const hash = $(e.currentTarget).attr('href');
|
|
|
|
e.preventDefault();
|
|
|
|
if (window.history.pushState) {
|
|
|
|
window.history.pushState(null, null, hash);
|
|
|
|
} else {
|
|
|
|
window.location.hash = hash;
|
2016-09-19 19:02:52 -04:00
|
|
|
}
|
2017-03-11 02:30:44 -05:00
|
|
|
this.highlightSelectedLine();
|
|
|
|
}
|
2017-10-19 05:46:43 -04:00
|
|
|
// eslint-disable-next-line class-methods-use-this
|
2017-09-28 10:55:25 -04:00
|
|
|
handleParallelLineDown(e) {
|
|
|
|
const line = $(e.currentTarget);
|
|
|
|
const table = line.closest('table');
|
|
|
|
|
|
|
|
table.removeClass('left-side-selected right-side-selected');
|
|
|
|
|
2020-12-23 16:10:24 -05:00
|
|
|
const lineClass = ['left-side', 'right-side'].filter((name) => line.hasClass(name))[0];
|
2017-09-28 10:55:25 -04:00
|
|
|
if (lineClass) {
|
|
|
|
table.addClass(`${lineClass}-selected`);
|
|
|
|
}
|
|
|
|
}
|
2017-10-19 05:46:43 -04:00
|
|
|
// eslint-disable-next-line class-methods-use-this
|
2017-03-11 02:30:44 -05:00
|
|
|
diffViewType() {
|
2018-02-20 17:20:48 -05:00
|
|
|
return $('.inline-parallel-buttons a.active').data('viewType');
|
2017-03-11 02:30:44 -05:00
|
|
|
}
|
2017-10-19 05:46:43 -04:00
|
|
|
// eslint-disable-next-line class-methods-use-this
|
2017-03-11 02:30:44 -05:00
|
|
|
lineNumbers(line) {
|
2017-03-22 03:57:31 -04:00
|
|
|
const children = line.find('.diff-line-num').toArray();
|
|
|
|
if (children.length !== 2) {
|
2017-03-11 02:30:44 -05:00
|
|
|
return [0, 0];
|
2016-10-25 15:25:46 -04:00
|
|
|
}
|
2020-12-23 16:10:24 -05:00
|
|
|
return children.map((elm) => parseInt($(elm).data('linenumber'), 10) || 0);
|
2017-03-11 02:30:44 -05:00
|
|
|
}
|
2017-10-19 05:46:43 -04:00
|
|
|
// eslint-disable-next-line class-methods-use-this
|
2017-03-11 02:30:44 -05:00
|
|
|
highlightSelectedLine() {
|
2017-12-07 07:30:53 -05:00
|
|
|
const hash = getLocationHash();
|
2017-03-11 02:30:44 -05:00
|
|
|
const $diffFiles = $('.diff-file');
|
|
|
|
$diffFiles.find('.hll').removeClass('hll');
|
2016-10-25 02:27:21 -04:00
|
|
|
|
2017-03-11 02:30:44 -05:00
|
|
|
if (hash) {
|
|
|
|
$diffFiles
|
|
|
|
.find(`tr#${hash}:not(.match) td, td#${hash}, td[data-line-code="${hash}"]`)
|
|
|
|
.addClass('hll');
|
2016-10-25 15:25:46 -04:00
|
|
|
}
|
|
|
|
}
|
2022-02-22 10:18:06 -05:00
|
|
|
|
|
|
|
prepareRenderedDiff() {
|
|
|
|
const $elements = $('[data-diff-toggle-entity]');
|
|
|
|
|
|
|
|
if ($elements.length === 0) return;
|
|
|
|
|
|
|
|
const diff = this;
|
|
|
|
|
|
|
|
const elements = $elements.toArray().map(this.formatElementToObject).reduce(merge);
|
|
|
|
|
|
|
|
Object.values(elements).forEach((e) => {
|
|
|
|
e.toShowBtn.onclick = () => diff.showOneHideAnother('rendered', e); // eslint-disable-line no-param-reassign
|
|
|
|
e.toHideBtn.onclick = () => diff.showOneHideAnother('raw', e); // eslint-disable-line no-param-reassign
|
|
|
|
|
2022-02-23 04:12:16 -05:00
|
|
|
diff.showOneHideAnother('rendered', e);
|
2022-02-22 10:18:06 -05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
formatElementToObject = (element) => {
|
|
|
|
const key = element.attributes['data-file-hash'].value;
|
|
|
|
const name = element.attributes['data-diff-toggle-entity'].value;
|
|
|
|
|
|
|
|
return { [key]: { [name]: element } };
|
|
|
|
};
|
|
|
|
|
|
|
|
showOneHideAnother = (mode, elements) => {
|
|
|
|
let { toShowBtn, toHideBtn, toShow, toHide } = elements;
|
|
|
|
|
|
|
|
if (mode === 'raw') {
|
|
|
|
[toShowBtn, toHideBtn] = [toHideBtn, toShowBtn];
|
|
|
|
[toShow, toHide] = [toHide, toShow];
|
|
|
|
}
|
|
|
|
|
|
|
|
toShowBtn.classList.add('selected');
|
|
|
|
toHideBtn.classList.remove('selected');
|
|
|
|
|
|
|
|
toHide.classList.add('hidden');
|
|
|
|
toShow.classList.remove('hidden');
|
|
|
|
};
|
2017-03-11 02:30:44 -05:00
|
|
|
}
|