const devicePixelRatio = window.devicePixelRatio || 1; class ImageCropper { constructor(canvas) { if (!(canvas instanceof HTMLCanvasElement)) { throw TypeError('canvas must be a HTMLCanvasElement'); } this._preview = canvas; this._preview_ctx = canvas.getContext('2d'); this._preview_ctx.reset(); const dimensions = this._preview.getBoundingClientRect(); console.log(dimensions); console.log(this._preview.width, this._preview.height); this._preview.width = dimensions.width * devicePixelRatio; this._preview.height = dimensions.height * devicePixelRatio; this._preview_ctx.setTransform(devicePixelRatio, 0, 0, devicePixelRatio, 0, 0); } showImage(image_src) { this._image = new Image(); this._image.src = image_src; this._image.addEventListener('load', (e) => { const dimensions = this._preview.getBoundingClientRect(); const scale = { x: dimensions.width / this._image.naturalWidth, y: dimensions.height / this._image.naturalHeight } const ratio = Math.min(scale.x, scale.y); const position = { x: (dimensions.width - this._image.naturalWidth * ratio) / 2, y: (dimensions.height - this._image.naturalHeight * ratio) / 2, }; this._preview_ctx.drawImage(this._image, position.x, position.y, this._image.naturalWidth * ratio, this._image.naturalHeight * ratio); }) } doCrop() { this._shadow = document.createElement('canvas'); this._shadow.style.display = 'none'; this._shadow_ctx = canvas.getContext('2d'); } } /*jQuery(document).ready( ($) => { let wp_file_selector_frame; let image_cropper; $('#theatergf-select-image').on('click', (event) => { // don't open browser file select event.preventDefault(); if (wp_file_selector_frame) { wp_file_selector_frame.open(); } wp_file_selector_frame = wp.media({ title: 'Select Image', button: { text: 'Use Image' }, multiple: false }); wp_file_selector_frame.on('select', () => { const file = wp_file_selector_frame.state().get('selection').first().toJSON(); image_cropper = new ImageCropper(document.getElementById('theatergf-image-editor')); //image_cropper.showImage('http://localhost:8000/wp-content/uploads/2026/03/Kishon002.jpg'); image_cropper.showImage(file.url); //document.getElementById('theatergf-image-preview').src = file.url; }); wp_file_selector_frame.open(); }); });*/ class ResizableBox { constructor(element, aspectRatio = null) { this.element = element; this.aspectRatio = aspectRatio; this.mouseStart = { x: 0, y: 0 } this.elemStart = { left: 0, top: 0, width: element.style.width, height: element.style.height, corner_pos: { x: 0, y: 0 } }; this.direction = undefined; const dragMouseMove = (event) => { event.preventDefault(); const offset = { x: event.clientX - this.mouseStart.x, y: event.clientY - this.mouseStart.y }; const delta = {...offset}; if (this.direction && aspectRatio) { const {anchor, sign} = (() => { switch (this.direction) { case 'nw': return { anchor: { x: this.elemStart.left + this.elemStart.width, y: this.elemStart.top + this.elemStart.height }, sign: { x: -1, y: -1 } } case 'ne': return { anchor: { x: this.elemStart.left, y: this.elemStart.top + this.elemStart.height }, sign: { x: +1, y: -1 } } case 'sw': return { anchor: { x: this.elemStart.left + this.elemStart.width, y: this.elemStart.top }, sign: { x: -1, y: +1 } } case 'se': return { anchor: { x: this.elemStart.left, y: this.elemStart.top }, sign: { x: +1, y: +1 } } } })() const px = this.elemStart.corner_pos.x + offset.x const py = this.elemStart.corner_pos.y + offset.y const dx = sign.x * (px - anchor.x) const dy = sign.y * (py - anchor.y) const sh = dx/this.elemStart.width const sv = dy/this.elemStart.height const dv = Math.abs(dx - this.elemStart.width) const dh = Math.abs(dy - this.elemStart.height) const s = (dv > dh) ? sv : sh delta.x = sign.x * (s * this.elemStart.width - this.elemStart.width) delta.y = sign.y * (s * this.elemStart.height - this.elemStart.height) } switch(this.direction) { case 'nw': this.element.style.left = (this.elemStart.left + delta.x) + "px"; this.element.style.top = (this.elemStart.top + delta.y) + "px"; this.element.style.width = (this.elemStart.width - delta.x) + "px"; this.element.style.height = (this.elemStart.height - delta.y) + "px"; break; case 'ne': this.element.style.top = (this.elemStart.top + delta.y) + "px"; this.element.style.width = (this.elemStart.width + delta.x) + "px"; this.element.style.height = (this.elemStart.height - delta.y) + "px"; break; case 'sw': this.element.style.left = (this.elemStart.left + delta.x) + "px"; this.element.style.width = (this.elemStart.width - delta.x) + "px"; this.element.style.height = (this.elemStart.height + delta.y) + "px"; break; case 'se': this.element.style.width = (this.elemStart.width + delta.x) + "px"; this.element.style.height = (this.elemStart.height + delta.y) + "px"; break; // moving is the default default: this.element.style.left = (this.elemStart.left + offset.x) + "px"; this.element.style.top = (this.elemStart.top + offset.y) + "px"; } } const dragMouseUp = (_) => { this.element.classList.remove("moving"); this.direction = undefined document.removeEventListener('mousemove', dragMouseMove); document.removeEventListener('mouseup', dragMouseUp); }; const dragMouseDown = (event) => { event.preventDefault(); this.mouseStart.x = event.clientX; this.mouseStart.y = event.clientY; this.elemStart.left = this.element.offsetLeft; this.elemStart.top = this.element.offsetTop; this.elemStart.width = this.element.offsetWidth; this.elemStart.height = this.element.offsetHeight; this.direction = [...event.target.classList].find(c => ["nw","ne","sw","se"].includes(c)); switch (this.direction) { case 'nw': this.elemStart.corner_pos.x = this.element.offsetLeft; this.elemStart.corner_pos.y = this.element.offsetTop; break; case 'ne': this.elemStart.corner_pos.x = this.element.offsetLeft + this.element.offsetWidth; this.elemStart.corner_pos.y = this.element.offsetTop; break; case 'sw': this.elemStart.corner_pos.x = this.element.offsetLeft; this.elemStart.corner_pos.y = this.element.offsetTop + this.element.offsetHeight; break; case 'se': this.elemStart.corner_pos.x = this.element.offsetLeft + this.element.offsetWidth; this.elemStart.corner_pos.y = this.element.offsetTop + this.element.offsetHeight; break; } this.element.classList.add('moving'); document.addEventListener('mousemove', dragMouseMove); document.addEventListener('mouseup', dragMouseUp); }; this.element.addEventListener('mousedown', dragMouseDown); this.createHandles(); } createHandles() { const handles = this.element.querySelectorAll('.handle'); for (const handle of handles) { this.element.removeChild(handle); } const neHandle = document.createElement('div'); neHandle.classList.add('handle', 'ne'); const nwHandle = document.createElement('div'); nwHandle.classList.add('handle', 'nw'); const seHandle = document.createElement('div'); seHandle.classList.add('handle', 'se'); const swHandle = document.createElement('div'); swHandle.classList.add('handle', 'sw'); this.element.appendChild(neHandle); this.element.appendChild(nwHandle); this.element.appendChild(seHandle); this.element.appendChild(swHandle); } getRect() { return new DOMRect(this.element.offsetLeft, this.element.offsetTop, this.element.offsetWidth, this.element.offsetHeight); } } class Editor { constructor(editorElement, aspectRatio) { this._editor = editorElement; this.aspectRatio = aspectRatio; } loadImage(url) { const imgElement = new Image() imgElement.classList.add("editor-image"); imgElement.src = url; this._editor.replaceChildren(imgElement); const that = this; imgElement.addEventListener('load', (_) => { const size = { width: imgElement.offsetWidth, height: imgElement.offsetHeight }; if (size.width > size.height * that.aspectRatio) { size.width = size.height * that.aspectRatio; } else { size.height = size.width / that.aspectRatio; } that._createSelectionBox(size.width, size.height); }); } _createSelectionBox(width, height) { const selector = document.createElement('div'); selector.classList.add('selector'); selector.style.width = (width - 1) + "px"; selector.style.height = (height - 1) + "px"; this._cutSelector = selector; this._editor.appendChild(selector); this._cutArea = new ResizableBox(selector, width/height); } } jQuery(document).ready( ($) => { let wp_file_selector_frame = null; let editor = new Editor(document.querySelector("#theatergf-editor"), 4/3); $("#theatergf-select-image").on("click", (event) => { event.preventDefault(); if (wp_file_selector_frame) { wp_file_selector_frame.open(); return; } wp_file_selector_frame = wp.media({ title: 'Select Image', button: { text: 'Use Image' }, multiple: false }); wp_file_selector_frame.on('select', () => { const file = wp_file_selector_frame.state().get('selection').first().toJSON(); editor.loadImage(file.url); }); wp_file_selector_frame.open(); }) })