mirror of https://github.com/go-gitea/gitea.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
76 lines
3.3 KiB
76 lines
3.3 KiB
import type {FileRenderPlugin} from '../render/plugin.ts'; |
|
import {newRenderPlugin3DViewer} from '../render/plugins/3d-viewer.ts'; |
|
import {newRenderPluginPdfViewer} from '../render/plugins/pdf-viewer.ts'; |
|
import {registerGlobalInitFunc} from '../modules/observer.ts'; |
|
import {createElementFromHTML, showElem, toggleElemClass} from '../utils/dom.ts'; |
|
import {html} from '../utils/html.ts'; |
|
import {basename} from '../utils.ts'; |
|
|
|
const plugins: FileRenderPlugin[] = []; |
|
|
|
function initPluginsOnce(): void { |
|
if (plugins.length) return; |
|
plugins.push(newRenderPlugin3DViewer(), newRenderPluginPdfViewer()); |
|
} |
|
|
|
function findFileRenderPlugin(filename: string, mimeType: string): FileRenderPlugin | null { |
|
return plugins.find((plugin) => plugin.canHandle(filename, mimeType)) || null; |
|
} |
|
|
|
function showRenderRawFileButton(elFileView: HTMLElement, renderContainer: HTMLElement | null): void { |
|
const toggleButtons = elFileView.querySelector('.file-view-toggle-buttons')!; |
|
showElem(toggleButtons); |
|
const displayingRendered = Boolean(renderContainer); |
|
toggleElemClass(toggleButtons.querySelectorAll('.file-view-toggle-source'), 'active', !displayingRendered); // it may not exist |
|
toggleElemClass(toggleButtons.querySelector('.file-view-toggle-rendered')!, 'active', displayingRendered); |
|
// TODO: if there is only one button, hide it? |
|
} |
|
|
|
async function renderRawFileToContainer(container: HTMLElement, rawFileLink: string, mimeType: string) { |
|
const elViewRawPrompt = container.querySelector('.file-view-raw-prompt'); |
|
if (!rawFileLink || !elViewRawPrompt) throw new Error('unexpected file view container'); |
|
|
|
let rendered = false, errorMsg = ''; |
|
try { |
|
const plugin = findFileRenderPlugin(basename(rawFileLink), mimeType); |
|
if (plugin) { |
|
container.classList.add('is-loading'); |
|
container.setAttribute('data-render-name', plugin.name); // not used yet |
|
await plugin.render(container, rawFileLink); |
|
rendered = true; |
|
} |
|
} catch (e) { |
|
errorMsg = `${e}`; |
|
} finally { |
|
container.classList.remove('is-loading'); |
|
} |
|
|
|
if (rendered) { |
|
elViewRawPrompt.remove(); |
|
return; |
|
} |
|
|
|
// remove all children from the container, and only show the raw file link |
|
container.replaceChildren(elViewRawPrompt); |
|
|
|
if (errorMsg) { |
|
const elErrorMessage = createElementFromHTML(html`<div class="ui error message">${errorMsg}</div>`); |
|
elViewRawPrompt.insertAdjacentElement('afterbegin', elErrorMessage); |
|
} |
|
} |
|
|
|
export function initRepoFileView(): void { |
|
registerGlobalInitFunc('initRepoFileView', async (elFileView: HTMLElement) => { |
|
initPluginsOnce(); |
|
const rawFileLink = elFileView.getAttribute('data-raw-file-link')!; |
|
const mimeType = elFileView.getAttribute('data-mime-type') || ''; // not used yet |
|
// TODO: we should also provide the prefetched file head bytes to let the plugin decide whether to render or not |
|
const plugin = findFileRenderPlugin(basename(rawFileLink), mimeType); |
|
if (!plugin) return; |
|
|
|
const renderContainer = elFileView.querySelector<HTMLElement>('.file-view-render-container'); |
|
showRenderRawFileButton(elFileView, renderContainer); |
|
// maybe in the future multiple plugins can render the same file, so we should not assume only one plugin will render it |
|
if (renderContainer) await renderRawFileToContainer(renderContainer, rawFileLink, mimeType); |
|
}); |
|
}
|
|
|