gitea源码

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. import type {FileRenderPlugin} from '../render/plugin.ts';
  2. import {newRenderPlugin3DViewer} from '../render/plugins/3d-viewer.ts';
  3. import {newRenderPluginPdfViewer} from '../render/plugins/pdf-viewer.ts';
  4. import {registerGlobalInitFunc} from '../modules/observer.ts';
  5. import {createElementFromHTML, showElem, toggleElemClass} from '../utils/dom.ts';
  6. import {html} from '../utils/html.ts';
  7. import {basename} from '../utils.ts';
  8. const plugins: FileRenderPlugin[] = [];
  9. function initPluginsOnce(): void {
  10. if (plugins.length) return;
  11. plugins.push(newRenderPlugin3DViewer(), newRenderPluginPdfViewer());
  12. }
  13. function findFileRenderPlugin(filename: string, mimeType: string): FileRenderPlugin | null {
  14. return plugins.find((plugin) => plugin.canHandle(filename, mimeType)) || null;
  15. }
  16. function showRenderRawFileButton(elFileView: HTMLElement, renderContainer: HTMLElement | null): void {
  17. const toggleButtons = elFileView.querySelector('.file-view-toggle-buttons');
  18. showElem(toggleButtons);
  19. const displayingRendered = Boolean(renderContainer);
  20. toggleElemClass(toggleButtons.querySelectorAll('.file-view-toggle-source'), 'active', !displayingRendered); // it may not exist
  21. toggleElemClass(toggleButtons.querySelector('.file-view-toggle-rendered'), 'active', displayingRendered);
  22. // TODO: if there is only one button, hide it?
  23. }
  24. async function renderRawFileToContainer(container: HTMLElement, rawFileLink: string, mimeType: string) {
  25. const elViewRawPrompt = container.querySelector('.file-view-raw-prompt');
  26. if (!rawFileLink || !elViewRawPrompt) throw new Error('unexpected file view container');
  27. let rendered = false, errorMsg = '';
  28. try {
  29. const plugin = findFileRenderPlugin(basename(rawFileLink), mimeType);
  30. if (plugin) {
  31. container.classList.add('is-loading');
  32. container.setAttribute('data-render-name', plugin.name); // not used yet
  33. await plugin.render(container, rawFileLink);
  34. rendered = true;
  35. }
  36. } catch (e) {
  37. errorMsg = `${e}`;
  38. } finally {
  39. container.classList.remove('is-loading');
  40. }
  41. if (rendered) {
  42. elViewRawPrompt.remove();
  43. return;
  44. }
  45. // remove all children from the container, and only show the raw file link
  46. container.replaceChildren(elViewRawPrompt);
  47. if (errorMsg) {
  48. const elErrorMessage = createElementFromHTML(html`<div class="ui error message">${errorMsg}</div>`);
  49. elViewRawPrompt.insertAdjacentElement('afterbegin', elErrorMessage);
  50. }
  51. }
  52. export function initRepoFileView(): void {
  53. registerGlobalInitFunc('initRepoFileView', async (elFileView: HTMLElement) => {
  54. initPluginsOnce();
  55. const rawFileLink = elFileView.getAttribute('data-raw-file-link');
  56. const mimeType = elFileView.getAttribute('data-mime-type') || ''; // not used yet
  57. // TODO: we should also provide the prefetched file head bytes to let the plugin decide whether to render or not
  58. const plugin = findFileRenderPlugin(basename(rawFileLink), mimeType);
  59. if (!plugin) return;
  60. const renderContainer = elFileView.querySelector<HTMLElement>('.file-view-render-container');
  61. showRenderRawFileButton(elFileView, renderContainer);
  62. // maybe in the future multiple plugins can render the same file, so we should not assume only one plugin will render it
  63. if (renderContainer) await renderRawFileToContainer(renderContainer, rawFileLink, mimeType);
  64. });
  65. }