import debounce from "lodash/debounce"
import max from "lodash/max"
import ajax from "./utils/ajax"
import ImageLoader from "./image_loader"
const UPDATE_DELAY = 125
const IMAGE_PLACEHOLDER = ''
const EMPTY_IMAGE = ''
const THUMBNAIL_SIZE = "160x120"
class PictureEditor {
constructor(container) {
this.container = container
this.cropFromField = container.querySelector("[data-crop-from]")
this.cropSizeField = container.querySelector("[data-crop-size]")
this.pictureIdField = container.querySelector("[data-picture-id]")
this.targetSizeField = container.querySelector("[data-target-size]")
this.imageCropperField = container.querySelector("[data-image-cropper]")
this.image = container.querySelector("img")
this.thumbnailBackground = container.querySelector(".thumbnail_background")
this.deleteButton = container.querySelector(".picture_tool.delete")
this.cropLink = container.querySelector(".crop_link")
this.targetSize = this.targetSizeField.dataset.targetSize
this.pictureId = this.pictureIdField.value
if (this.image) {
this.imageLoader = new ImageLoader(this.image)
}
this.update = debounce(() => {
this.updateImage()
this.updateCropLink()
}, UPDATE_DELAY)
this.deleteButton.addEventListener("click", this.removeImage.bind(this))
}
observe() {
const observer = new MutationObserver(this.mutationCallback.bind(this))
observer.observe(this.cropFromField, { attributes: true })
observer.observe(this.cropSizeField, { attributes: true })
observer.observe(this.pictureIdField, { attributes: true })
}
mutationCallback(mutationsList) {
for (const mutation of mutationsList) {
if ("pictureId" in mutation.target.dataset) {
this.cropFromField.value = ""
this.cropSizeField.value = ""
this.pictureId = mutation.target.value
}
this.update()
}
}
updateImage() {
if (!this.pictureId) return
this.ensureImage()
this.image.removeAttribute("alt")
this.image.removeAttribute("src")
this.imageLoader.load(true)
ajax("GET", `/admin/pictures/${this.pictureId}/url`, {
crop: this.imageCropperEnabled,
crop_from: this.cropFrom,
crop_size: this.cropSize,
flatten: true,
size: THUMBNAIL_SIZE
})
.then(({ data }) => {
this.image.src = data.url
this.image.alt = data.alt
this.image.title = data.title
})
.catch((error) => {
console.error(error.message || error)
Alchemy.growl(error.message || error, "error")
})
}
ensureImage() {
if (this.image) return
this.thumbnailBackground.innerHTML = EMPTY_IMAGE
this.image = this.container.querySelector("img")
this.imageLoader = new ImageLoader(this.image)
}
removeImage() {
this.thumbnailBackground.innerHTML = IMAGE_PLACEHOLDER
this.pictureIdField.value = ""
this.image = null
this.cropLink.classList.add("disabled")
Alchemy.setElementDirty(this.container.closest(".element-editor"))
}
updateCropLink() {
if (!this.pictureId || !this.imageCropperEnabled) return
this.cropLink.classList.remove("disabled")
if (this.cropLink.href.match(/(picture_id=)\d+/)) {
this.cropLink.href = this.cropLink.href.replace(
/(picture_id=)\d+/,
"$1" + this.pictureId
)
} else {
this.cropLink.href = this.cropLink.href + `&picture_id=${this.pictureId}`
}
}
get cropFrom() {
if (this.cropFromField.value === "") {
return this.defaultCropFrom.join("x")
}
return this.cropFromField.value
}
get cropSize() {
if (this.cropSizeField.value === "") {
return this.defaultCropSize.join("x")
}
return this.cropSizeField.value
}
get defaultCropSize() {
if (!this.imageCropperEnabled) return []
const mask = this.targetSize.split("x").map((n) => parseInt(n))
const zoom = max([
mask[0] / this.imageFileWidth,
mask[1] / this.imageFileHeight
])
return [Math.round(mask[0] / zoom), Math.round(mask[1] / zoom)]
}
get defaultCropFrom() {
if (!this.imageCropperEnabled) return []
const dimensions = this.defaultCropSize
return [
Math.round((this.imageFileWidth - dimensions[0]) / 2),
Math.round((this.imageFileHeight - dimensions[1]) / 2)
]
}
get imageFileWidth() {
return parseInt(this.pictureIdField.dataset.imageFileWidth)
}
get imageFileHeight() {
return parseInt(this.pictureIdField.dataset.imageFileHeight)
}
get imageCropperEnabled() {
return this.targetSizeField.dataset.imageCropper === "true"
}
}
export default function init(selector) {
document.querySelectorAll(selector).forEach((node) => {
const thumbnail = new PictureEditor(node)
thumbnail.observe()
})
}