const PAGE_SIZE = 36 async function GetImagesList(page = 0, query = "") { const response = await fetch(`/api/v1/images?thumbnails&pageSize=${PAGE_SIZE}&page=${page}&query=${query}`);// const formData = await response.formData(); const result = { thumbnails: {} } for (const key of formData.keys()) { if (key === "image_list.json") { const buffer = await formData.get(key).arrayBuffer() result.images = JSON.parse(new TextDecoder().decode(buffer)) } else { let buffer = await formData.get(key).arrayBuffer() let b64 = await bytesToBase64DataUrl(buffer, key.endsWith('.svg') ? "image/svg+xml" : "image/png") result.thumbnails[key] = b64 } } return result } async function RenderImageListPage(mountPoint, options = {}) { mountPoint.innerHTML = `
` const container = mountPoint.querySelector('.images_page__container') new ImagesPage(container, options) document.addEventListener('imageclicked', (e) => { const url = `/images/${e.detail.imageId}` if (e.detail.ctrlKey) { window.open(url, '_blank') } else { window.location.href = url } }) } async function fullScreenImageSelect() { const div = document.createElement('div') div.classList.add('image_selector__back') document.body.appendChild(div) const page = new ImagesPage(div, { headless: true }) return new Promise((resolve, reject) => { const onClick = (e) => { document.removeEventListener('imageclicked', onClick) page.destroy() div.remove() resolve(e.detail.imageId) } document.addEventListener('imageclicked', onClick) }) } class ImagesPage { constructor(mountPoint, options) { this.page = 0 const head = options.headless ? "" : `
Images:
` const urlParams = new URLSearchParams(window.location.search); mountPoint.innerHTML = `
${head}
<
0
>
` this.div = mountPoint.querySelector('.images') this.container = this.div.querySelector('.images_container') this.buttonLeft = this.div.querySelector('.images_page-controls__button.left') this.buttonRight = this.div.querySelector('.images_page-controls__button.right') this.pageDisplay = this.div.querySelector('.images_page-controls__page') this.searchText = this.div.querySelector('.images_search__text') this.searchButton = this.div.querySelector('.images_search__button') this.searchText.addEventListener("keypress", (event) => { if (event.key === "Enter") { event.preventDefault(); this.searchButton.click(); } }) this.buttonLeft.addEventListener('click', async () => { this.page = this.page == 0 ? 0 : this.page - 1 await this.reloadPage() }) this.searchButton.addEventListener('click', async () => { window.history.pushState({}, "", `/images?query=${this.searchText.innerText.trim()}`) this.page = 0 await this.reloadPage() }) this.buttonRight.addEventListener('click', async () => { this.page += 1 await this.reloadPage() }) setTimeout(async () => await this.reloadPage(), 0) } async reloadPage() { const imageList = await GetImagesList(this.page, this.searchText.innerText); const images = imageList.images for (const block of this.imageBlocks || []) { block.destroy() } this.imageBlocks = [] for (const imageId of images.images || []) { this.imageBlocks.push(new ImageBlock(this.container, imageId, imageSize, imageList.thumbnails[imageId])) } this.pageDisplay.innerText = `${this.page}` } destroy() { for (const block of this.imageBlocks) { block.destroy() } this.div.remove() } } class ImageBlock { constructor(mountPoint, imageId, imageSize, thumbnail) { const blockStyle = imageSize ? `style="width: ${imageSize}px; height: ${imageSize}px"` : "" const src = thumbnail || `"/api/v1/images/${imageId}"` this.div = document.createElement('div') this.div.innerHTML = `
` this.onClick = (e) => { const event = new CustomEvent('imageclicked', { detail: { imageId: imageId, ctrlKey: e.ctrlKey } }) document.dispatchEvent(event) } this.div.addEventListener('click', this.onClick) mountPoint.appendChild(this.div) } destroy() { this.div.removeEventListener('click', this.onClick) this.div.remove() } }