gitlab-org--gitlab-foss/app/assets/javascripts/blob/viewer/index.js

229 lines
6.6 KiB
JavaScript

import $ from 'jquery';
import '~/behaviors/markdown/render_gfm';
import createFlash from '~/flash';
import { __ } from '~/locale';
import {
REPO_BLOB_LOAD_VIEWER_START,
REPO_BLOB_LOAD_VIEWER_FINISH,
REPO_BLOB_LOAD_VIEWER,
REPO_BLOB_SWITCH_TO_VIEWER_START,
REPO_BLOB_SWITCH_VIEWER,
} from '~/performance/constants';
import { performanceMarkAndMeasure } from '~/performance/utils';
import { fixTitle } from '~/tooltips';
import axios from '~/lib/utils/axios_utils';
import { handleLocationHash } from '~/lib/utils/common_utils';
import eventHub from '~/notes/event_hub';
const loadRichBlobViewer = (type) => {
switch (type) {
case 'notebook':
return import(/* webpackChunkName: 'notebook_viewer' */ '../notebook_viewer');
case 'openapi':
return import(/* webpackChunkName: 'openapi_viewer' */ '../openapi_viewer');
case 'csv':
return import(/* webpackChunkName: 'csv_viewer' */ '../csv_viewer');
case 'pdf':
return import(/* webpackChunkName: 'pdf_viewer' */ '../pdf_viewer');
case 'sketch':
return import(/* webpackChunkName: 'sketch_viewer' */ '../sketch_viewer');
case 'stl':
return import(/* webpackChunkName: 'stl_viewer' */ '../stl_viewer');
default:
return Promise.resolve();
}
};
const loadViewer = (viewerParam) => {
const viewer = viewerParam;
const { url } = viewer.dataset;
if (!url || viewer.dataset.loaded || viewer.dataset.loading) {
return Promise.resolve(viewer);
}
viewer.dataset.loading = 'true';
return axios.get(url).then(({ data }) => {
// eslint-disable-next-line no-unsanitized/property
viewer.innerHTML = data.html;
window.requestIdleCallback(() => {
delete viewer.dataset.loading;
});
return viewer;
});
};
export const initAuxiliaryViewer = () => {
const auxiliaryViewer = document.querySelector('.blob-viewer[data-type="auxiliary"]');
if (!auxiliaryViewer) return;
loadViewer(auxiliaryViewer);
};
export const handleBlobRichViewer = (viewer, type) => {
if (!viewer || !type) return;
loadRichBlobViewer(type)
.then((module) => module?.default(viewer))
.catch((error) => {
createFlash({
message: __('Error loading file viewer.'),
});
throw error;
});
};
export class BlobViewer {
constructor() {
performanceMarkAndMeasure({
mark: REPO_BLOB_LOAD_VIEWER_START,
});
const viewer = document.querySelector('.blob-viewer[data-type="rich"]');
const type = viewer?.dataset?.richType;
initAuxiliaryViewer();
handleBlobRichViewer(viewer, type);
this.initMainViewers();
}
initMainViewers() {
this.$fileHolder = $('.file-holder');
if (!this.$fileHolder.length) return;
this.switcher = document.querySelector('.js-blob-viewer-switcher');
this.switcherBtns = document.querySelectorAll('.js-blob-viewer-switch-btn');
this.copySourceBtn = document.querySelector('.js-copy-blob-source-btn');
this.copySourceBtnTooltip = document.querySelector('.js-copy-blob-source-btn-tooltip');
this.simpleViewer = this.$fileHolder[0].querySelector('.blob-viewer[data-type="simple"]');
this.richViewer = this.$fileHolder[0].querySelector('.blob-viewer[data-type="rich"]');
this.initBindings();
this.switchToInitialViewer();
}
switchToInitialViewer() {
const initialViewer = this.$fileHolder[0].querySelector('.blob-viewer:not(.hidden)');
let initialViewerName = initialViewer.dataset.type;
if (this.switcher && window.location.hash.indexOf('#L') === 0) {
initialViewerName = 'simple';
}
this.switchToViewer(initialViewerName);
}
initBindings() {
if (this.switcherBtns.length) {
Array.from(this.switcherBtns).forEach((el) => {
el.addEventListener('click', this.switchViewHandler.bind(this));
});
}
if (this.copySourceBtn) {
this.copySourceBtn.addEventListener('click', () => {
if (this.copySourceBtn.classList.contains('disabled')) return this.copySourceBtn.blur();
return this.switchToViewer('simple');
});
}
}
switchViewHandler(e) {
const target = e.currentTarget;
e.preventDefault();
this.switchToViewer(target.dataset.viewer);
}
toggleCopyButtonState() {
if (!this.copySourceBtn) return;
if (this.simpleViewer.dataset.loaded) {
this.copySourceBtnTooltip.setAttribute('title', __('Copy file contents'));
this.copySourceBtn.classList.remove('disabled');
} else if (this.activeViewer === this.simpleViewer) {
this.copySourceBtnTooltip.setAttribute(
'title',
__('Wait for the file to load to copy its contents'),
);
this.copySourceBtn.classList.add('disabled');
} else {
this.copySourceBtnTooltip.setAttribute(
'title',
__('Switch to the source to copy the file contents'),
);
this.copySourceBtn.classList.add('disabled');
}
fixTitle($(this.copySourceBtnTooltip));
}
switchToViewer(name) {
performanceMarkAndMeasure({
mark: REPO_BLOB_SWITCH_TO_VIEWER_START,
});
const newViewer = this.$fileHolder[0].querySelector(`.blob-viewer[data-type='${name}']`);
if (this.activeViewer === newViewer) return;
const oldButton = document.querySelector('.js-blob-viewer-switch-btn.selected');
const newButton = document.querySelector(`.js-blob-viewer-switch-btn[data-viewer='${name}']`);
const oldViewer = this.$fileHolder[0].querySelector(`.blob-viewer:not([data-type='${name}'])`);
if (oldButton) {
oldButton.classList.remove('selected');
}
if (newButton) {
newButton.classList.add('selected');
newButton.blur();
}
if (oldViewer) {
oldViewer.classList.add('hidden');
}
newViewer.classList.remove('hidden');
this.activeViewer = newViewer;
this.toggleCopyButtonState();
loadViewer(newViewer)
.then((viewer) => {
$(viewer).renderGFM();
window.requestIdleCallback(() => {
this.$fileHolder.trigger('highlight:line');
handleLocationHash();
// eslint-disable-next-line no-param-reassign
viewer.dataset.loaded = 'true';
this.toggleCopyButtonState();
eventHub.$emit('showBlobInteractionZones', viewer.dataset.path);
});
performanceMarkAndMeasure({
mark: REPO_BLOB_LOAD_VIEWER_FINISH,
measures: [
{
name: REPO_BLOB_LOAD_VIEWER,
start: REPO_BLOB_LOAD_VIEWER_START,
},
{
name: REPO_BLOB_SWITCH_VIEWER,
start: REPO_BLOB_SWITCH_TO_VIEWER_START,
},
],
});
})
.catch(() =>
createFlash({
message: __('Error loading viewer'),
}),
);
}
}