���� ������������������������������������ let currentRow; let undoStack = []; let hasUndone = false; function triggerFileInput(dropArea) { const fileInput = $(dropArea).siblings('input[type="file"]')[0]; fileInput.click(); } function addRow() { const row = `
Drag & Drop Image Here
Image Preview `; $('#imageTableBody').append(row); } function removeRow(button) { const rows = $('#imageTableBody tr'); if (rows.length > 1) { $(button).closest('tr').remove(); } else { toastr.warning("The last row cannot be removed!"); } } function moveRow(button, direction) { const row = $(button).closest('tr'); if (direction === 'up' && row.index() > 0) { row.insertBefore(row.prev()); } else if (direction === 'down') { row.insertAfter(row.next()); } } function previewImage(event, input) { const imgPreview = $(input).siblings('.image-preview'); if (input.files && input.files[0]) { const reader = new FileReader(); reader.onload = function (e) { imgPreview.attr('src', e.target.result).show(); } reader.readAsDataURL(input.files[0]); } } function highlightDropArea(dropArea) { $(dropArea).addClass('highlight'); } function removeHighlight(dropArea) { $(dropArea).removeClass('highlight'); } function saveImages() { const saveButton = $('#saveImagesButton'); saveButton.prop('disabled', true); saveButton.text('Submitting...'); let formData = new FormData(); let pid = $('#pid').val(); let allImagesUploaded = true; $('#imageTableBody tr').each(function () { const fileInput = $(this).find('.image-file')[0].files[0]; const caption = $(this).find('input[type="text"]').val(); const imageSize = $(this).find('.image-size').val(); // Get image size const pins = $(this).data('pins') || []; const id = $(this).data('image-id'); const imageOrder = $(this).index(); if (!fileInput) { allImagesUploaded = false; return; } formData.append('images[]', fileInput); formData.append('captions[]', caption); formData.append('pins[]', JSON.stringify(pins)); formData.append('ids[]', id); formData.append('pid', pid); formData.append('imageOrder[]', imageOrder); formData.append('imageSizes[]', imageSize); // Append image size }); if (!allImagesUploaded) { saveButton.prop('disabled', false); saveButton.text('Submit'); toastr.error('Please upload all images.'); return; } $.ajax({ url: 'save_images.php', method: 'POST', data: formData, processData: false, contentType: false, success: function (response) { saveButton.text('Images Saved'); toastr.success('Images saved successfully! Please submit the form to view processed images.'); window.parent.postMessage({ action: 'submitForm' }, '*'); }, error: function (xhr, status, error) { saveButton.prop('disabled', false); saveButton.text('Save All Images'); toastr.error('Error saving images: ' + error); } }); } $(document).ready(function () { $(document).on('dragover', '.drag-drop-area', function (event) { event.preventDefault(); highlightDropArea(this); }); $(document).on('dragleave', '.drag-drop-area', function () { removeHighlight(this); }); $(document).on('drop', '.drag-drop-area', function (event) { handleDrop(event, this); }); $(document).on('paste', '.drag-drop-area', function (event) { handlePaste(event, this); }); }); function highlightDropArea(dropArea) { // console.log('hit'); $(dropArea).addClass('highlight'); } function removeHighlight(dropArea) { $(dropArea).removeClass('highlight'); } function handleDrop(event, dropArea) { event.preventDefault(); removeHighlight(dropArea); const files = event.originalEvent.dataTransfer.files; if (files.length > 0) { const input = $(dropArea).siblings('input[type="file"]')[0]; const dataTransfer = new DataTransfer(); dataTransfer.items.add(files[0]); // Simulate file input input.files = dataTransfer.files; previewImage(event, input); } } function handlePaste(event, dropArea) { const items = event.originalEvent.clipboardData.items; for (let i = 0; i < items.length; i++) { const item = items[i]; if (item.type.startsWith('image/')) { event.preventDefault(); highlightDropArea(dropArea); // Highlight the area const file = item.getAsFile(); const input = $(dropArea).siblings('input[type="file"]')[0]; const dataTransfer = new DataTransfer(); dataTransfer.items.add(file); input.files = dataTransfer.files; // Simulate file input previewImage(event, input); } } } function previewImage(event, input) { const imgPreview = $(input).siblings('.image-preview'); if (input.files && input.files[0]) { const reader = new FileReader(); reader.onload = function (e) { imgPreview.attr('src', e.target.result).show(); }; reader.readAsDataURL(input.files[0]); } } let canvas = new fabric.Canvas('canvas'); let fabricImage = null; let originalImageSrc = null; function editImage(icon) { currentRow = $(icon).closest('tr'); const imageSrc = currentRow.find('.image-preview').attr('src'); if (imageSrc) { originalImageSrc = imageSrc; $('#editImageModal').modal({ backdrop: 'static', keyboard: false }); loadFabricImage(imageSrc); } } function loadFabricImage(src) { fabric.Image.fromURL(src, function(img) { // Get the original dimensions of the image const originalWidth = img.width; const originalHeight = img.height; const maxWidth = 1000; if (originalWidth > maxWidth) { const aspectRatio = originalHeight / originalWidth; canvas.setWidth(maxWidth); canvas.setHeight(maxWidth * aspectRatio); } else { canvas.setWidth(originalWidth); canvas.setHeight(originalHeight); } canvas.clear(); fabricImage = img; // Set the properties of the image img.set({ left: 0, top: 0, scaleX: canvas.width / originalWidth, // Scale to fit the canvas scaleY: canvas.height / originalHeight, // Scale to fit the canvas selectable: false, evented: false }); // Add the image to the canvas and send it to the back canvas.add(fabricImage); fabricImage.sendToBack(); // Render the canvas canvas.renderAll(); }); } function updateModalImage(event) { const input = event.target; if (input.files && input.files[0]) { const reader = new FileReader(); reader.onload = function(e) { loadFabricImage(e.target.result); }; reader.readAsDataURL(input.files[0]); } } let cropMode = false; // Tracks whether crop mode is active let cropRect; function cropImage() { if (!cropMode) { // First click - initialize crop mode and create crop rectangle pushUndoState(); cropRect = new fabric.Rect({ left: 50, // Adjust as needed top: 50, width: 200, height: 200, fill: 'transparent', stroke: 'red', strokeWidth: 2, selectable: false, }); canvas.add(cropRect); canvas.setActiveObject(cropRect); canvas.renderAll(); cropMode = true; // Set crop mode active } else { // Second click - perform the crop if (fabricImage && cropRect) { const rect = cropRect.getBoundingRect(); const cropX = (rect.left - fabricImage.left) / fabricImage.scaleX; const cropY = (rect.top - fabricImage.top) / fabricImage.scaleY; const cropWidth = rect.width / fabricImage.scaleX; const cropHeight = rect.height / fabricImage.scaleY; const croppedCanvas = document.createElement('canvas'); croppedCanvas.width = cropWidth; croppedCanvas.height = cropHeight; const ctx = croppedCanvas.getContext('2d'); ctx.drawImage( fabricImage.getElement(), cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight ); const croppedImageDataURL = croppedCanvas.toDataURL(); fabric.Image.fromURL(croppedImageDataURL, function (croppedImage) { // Clear the canvas before adding the cropped image canvas.clear(); // Ensure the cropped image width does not exceed 1000 let finalWidth = croppedImage.width; let finalHeight = croppedImage.height; const maxWidth = 1000; if (finalWidth > maxWidth) { const scaleFactor = maxWidth / finalWidth; finalWidth = maxWidth; finalHeight *= scaleFactor; } // Set the canvas dimensions to match the cropped image canvas.setWidth(finalWidth); canvas.setHeight(finalHeight); // Set the cropped image properties croppedImage.set({ left: 0, top: 0, scaleX: canvas.width / croppedImage.width, scaleY: canvas.height / croppedImage.height, selectable: false, evented: false }); // Add the cropped image to the canvas and render canvas.add(croppedImage); canvas.renderAll(); // Clean up: remove crop rectangle canvas.remove(cropRect); // Push the state to undo stack and update the fabric image pushUndoState(); fabricImage = croppedImage; // Reset crop mode cropMode = false; cropRect = null; }); } } } function flipImage() { if (fabricImage) { // console.log("Before flip: ", fabricImage); // console.log("Before flip: ", fabricImage.flipX); pushUndoState(); fabricImage.flipX = !fabricImage.flipX; // console.log("After flip: ", fabricImage); // console.log("After flip: ", fabricImage.flipX); fabricImage.dirty = true; canvas.renderAll(); } } function rotateImage() { if (fabricImage) { pushUndoState(); fabricImage.rotate((fabricImage.angle + 90) % 360); canvas.renderAll(); } } function saveImageChanges() { // Clear the pins from the canvas (temporarily) const pins = []; canvas.getObjects('image').forEach(function (obj) { if (obj.pinData) { pins.push(obj); canvas.remove(obj); } }); const dataURL = canvas.toDataURL(); currentRow.find('.image-preview').attr('src', dataURL); const fileInput = currentRow.find('input[type="file"]')[0]; const blob = dataURLtoBlob(dataURL); const newFile = new File([blob], 'edited-image.png', { type: 'image/png' }); const dataTransfer = new DataTransfer(); dataTransfer.items.add(newFile); fileInput.files = dataTransfer.files; pins.forEach(pin => canvas.add(pin)); currentRow.data('pinsData', pinsData); $('#editImageModal').modal('hide'); } // Helper function to convert base64 to Blob function dataURLtoBlob(dataURL) { const byteString = atob(dataURL.split(',')[1]); const mimeString = dataURL.split(',')[0].split(':')[1].split(';')[0]; const ab = new ArrayBuffer(byteString.length); const ia = new Uint8Array(ab); for (let i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); } return new Blob([ab], { type: mimeString }); } function toggleFreeDrawing() { disableAllDrawingModes(); canvas.isDrawingMode = !canvas.isDrawingMode; // Toggle drawing mode pushUndoState(); if (canvas.isDrawingMode) { const selectedColor = $('#colorPicker').val(); canvas.freeDrawingBrush.color = selectedColor || 'red'; canvas.freeDrawingBrush.width = 5; // Set brush width toastr.success('Free Drawing Mode Enabled Successfully!.') } else { toastr.success('Free Drawing Mode Disabled Successfully!.') } } canvas.on('path:created', function () { }); function addRectangle() { pushUndoState(); disableAllDrawingModes(); const selectedColor = $('#colorPicker').val(); let rect = new fabric.Rect({ left: 100, top: 100, fill: 'transparent', width: 100, height: 100, stroke: selectedColor, strokeWidth: 2, selectable: true }); canvas.add(rect); canvas.renderAll(); } // Add circle to the canvas function addCircle() { pushUndoState(); disableAllDrawingModes(); const selectedColor = $('#colorPicker').val(); let circle = new fabric.Circle({ left: 200, top: 150, radius: 50, fill: 'transparent', stroke: selectedColor, strokeWidth: 2, selectable: true }); canvas.add(circle); canvas.renderAll(); // Save the state after adding the circle } function addText() { // console.log(undoStack); pushUndoState(); // console.log(undoStack); const text = prompt('Enter the text you want to add:'); if (text) { const selectedColor = $('#colorPicker').val(); const textObj = new fabric.Text(text, { left: canvas.width / 2 - 50, // Center the text horizontally top: canvas.height / 2 - 10, // Center the text vertically fill: selectedColor || 'red', // Default text color fontSize: 24, // Default font size fontFamily: 'Arial', // Default font family }); // Add the text object to the canvas canvas.add(textObj); // Make the text selectable and draggable canvas.setActiveObject(textObj); canvas.renderAll(); } } let isLineDrawing = false; let isDrawing = false; let line = null; // Toggle the drawing mode on or off function toggleLineDrawing() { isLineDrawing = !isLineDrawing; if (isLineDrawing) { canvas.isDrawingMode = false; canvas.selection = false; canvas.defaultCursor = 'crosshair'; // Reset the line and drawing flag line = null; isDrawing = false; // Attach mouse event listeners canvas.on('mouse:down', startDrawingLine); canvas.on('mouse:move', continueDrawingLine); canvas.on('mouse:up', finishDrawingLine); } else { // Detach mouse event listeners canvas.off('mouse:down', startDrawingLine); canvas.off('mouse:move', continueDrawingLine); canvas.off('mouse:up', finishDrawingLine); // Enable selection and reset cursor canvas.selection = true; canvas.defaultCursor = 'default'; } } // Start drawing the line function startDrawingLine(o) { if (!isLineDrawing || isDrawing) return; // Prevent drawing if already drawing isDrawing = true; const pointer = canvas.getPointer(o.e); const points = [pointer.x, pointer.y, pointer.x, pointer.y]; // Get color from the color picker (or default to red) const selectedColor = $('#colorPicker').val(); line = new fabric.Line(points, { strokeWidth: 2, stroke: selectedColor || 'red', // Default line color selectable: false, // Line should not be selectable while drawing evented: false, // Disable event handling while drawing }); canvas.add(line); } // Continue drawing the line as the mouse moves function continueDrawingLine(o) { if (!isDrawing || !line) return; // Ensure line exists before continuing const pointer = canvas.getPointer(o.e); line.set({ x2: pointer.x, y2: pointer.y }); canvas.renderAll(); } // Finish drawing the line when mouse button is released function finishDrawingLine() { if (!isDrawing || !line) return; // Ensure line exists before finishing isDrawing = false; line.setCoords(); // Set final coordinates of the line pushUndoState(); // Optional, push to undo stack // Finalize the line's drawing canvas.renderAll(); // Reset the line object for the next drawing line = null; } let pinsData = []; function addPin() { disableAllDrawingModes(); const url = prompt('Enter the URL for the pin:'); fabric.Image.fromURL('location-pin.png', function(pinIcon) { pinIcon.scale(0.06); pinIcon.set({ left: canvas.width / 2, top: canvas.height / 2, selectable: true, hasControls: false, hasBorders: false }); pinIcon.on('mousedblclick', function() { window.open(url, '_blank'); }); canvas.add(pinIcon); canvas.renderAll(); let relativeLeft = (pinIcon.left / canvas.width) * 100; let relativeTop = (pinIcon.top / canvas.height) * 100; const pinData = { url: url, left: relativeLeft, // Store as percentage top: relativeTop // Store as percentage }; // Store pinData in the current row const row = $(currentRow); let pins = row.data('pins') || []; pins.push(pinData); row.data('pins', pins); // Update the row's pin data // Update pin's position when it is moved pinIcon.on('moving', function() { // Convert new position to percentage relativeLeft = (pinIcon.left / canvas.width) * 100; relativeTop = (pinIcon.top / canvas.height) * 100; pinData.left = relativeLeft; pinData.top = relativeTop; row.data('pins', pins); }); pushUndoState(); }); } function disableAllDrawingModes() { // Disable free drawing mode canvas.isDrawingMode = false; // Disable line drawing mode isLineDrawing = false; canvas.off('mouse:down', startDrawingLine); canvas.off('mouse:move', continueDrawingLine); canvas.off('mouse:up', finishDrawingLine); // Reset the canvas cursor and selection properties canvas.selection = false; // Disable selection canvas.defaultCursor = 'default'; // Set default cursor // Ensure changes are reflected immediately canvas.renderAll(); } let isDropAreaSelected = false; // Track the selection state $(document).on('click', '.select-area-btn', function() { var currentRow = $(this).closest('tr'); var dropArea = currentRow.find('.drag-drop-area')[0]; var instructions = currentRow.find('.paste-instructions'); highlightDropArea(dropArea); instructions.show(); $(document).off('paste'); $(document).on('paste', function(event) { handlePaste(event, dropArea); }); }); function pushUndoState() { if (hasUndone) { undoStack = undoStack.slice(0, undoStack.length); hasUndone = false; } const canvasState = JSON.stringify(canvas.toJSON()); // Save canvas objects as JSON const canvasWidth = canvas.getWidth(); // Get canvas width const canvasHeight = canvas.getHeight(); // Get canvas height undoStack.push({ state: canvasState, // Save the canvas state width: canvasWidth, // Save the canvas width height: canvasHeight // Save the canvas height }); } function undo() { if (undoStack.length > 0) { const lastState = undoStack.pop(); // console.log("Restoring canvas with width:", lastState.width); // console.log("Restoring canvas with height:", lastState.height); canvas.setWidth(lastState.width); canvas.setHeight(lastState.height); canvas.clear(); canvas.loadFromJSON(lastState.state, function() { canvas.getObjects().forEach(function(obj) { obj.set({ selectable: false, hasControls: false, evented: false, }); fabricImage = obj; }); canvas.renderAll(); }); hasUndone = true; } else { toastr.warning('No more actions to undo.'); } }