/******************************************* - Title : Munjanara Image Editor ¥± - Date : 2025.04.25 - Author : KKH - Email : vsharp12@naver.com *******************************************/ var version = "1.50.26.3.19.11"; var imageMax = 3; var canvasWidth = 400; var canvasHeight = 600; var limitWidth = 1500; var limitHeight = 1440; var minWidth = 40; var minHeight = 40; var alpha = 0.3; var backImageWidth, backImageHeight; var scaleAll = 1; var imageWidth, imageHeight, fileName; var jobIndex = 0; var textAlignStat = "left"; var canvas, activeObj; var img, text, cropper; var imageInfo = []; var parentImg = []; var isBootLoading = false; var isApplyingParentImage = false; function initEditor() { if (imageInfo.length > 0) { if (!confirm("¸ðµç ¿ä¼Ò¸¦ »èÁ¦Çϰí ÃʱâÈ­ ÇÕ´Ï´Ù.")) return; document.location.reload(); } } function initPage() { if (!imageInfo[jobIndex] || !imageInfo[jobIndex].src) return; deleteCropper(); addJsonToCanvas(imageInfo[jobIndex].src); } function getPrimaryImageObject() { return canvas.getActiveObject() || canvas._objects[0] || null; } function deleteObject() { if (cropper) return; var objects = canvas.getObjects(); if (objects.length == 1) { Alert("±âº» À̹ÌÁö´Â »èÁ¦ÇÒ ¼ö ¾ø½À´Ï´Ù."); return; } activeObj = canvas.getActiveObject(); if (!activeObj) { Alert("»èÁ¦ÇÒ ¿ä¼Ò¸¦ ¼±ÅÃÇØÁÖ¼¼¿ä."); return; } if (activeObj && !activeObj.isEditing) { canvas.remove(activeObj); canvas.discardActiveObject(); canvas.requestRenderAll(); var remaining = canvas.getObjects().filter(obj => obj.selectable); if (remaining.length > 1) { canvas.setActiveObject(remaining[remaining.length - 1]); canvas.requestRenderAll(); } } } function deleteThumbnail(idx) { if (idx == "null") idx = jobIndex; var thumbnails = document.querySelectorAll(".thumbnail"); imageInfo.splice(idx, 1); for (var i = idx; i < thumbnails.length - 1; i++) { thumbnails[i].innerHTML = thumbnails[i + 1].innerHTML; thumbnails[i].title = thumbnails[i + 1].title; var currentBtn = thumbnails[i].querySelector(".close-button"); if (currentBtn) { currentBtn.onclick = function (e) { e.stopPropagation(); deleteThumbnail($(".close-button").index(this)); }; } } thumbnails[thumbnails.length - 1].innerHTML = ""; canvas.clear(); document.querySelector("#image-overlay").style.display = "flex"; document.querySelector("#btn-layer-editor").style.display = "none"; thumbnails[thumbnails.length - 1].title = "À§Ä¡ ¼±ÅÃÀº ¸¶¿ì½º ¿À¸¥ÂÊ ¹öưÀ» ´©¸£¼¼¿ä."; jobIndex = imageInfo.length; if (jobIndex > imageMax) jobIndex = imageMax - 1; console.log("deleteThumbnail() job index : ", jobIndex, idx); } function highlightAppliedFilters(obj) { document.querySelectorAll(".filter-buttons button").forEach(btn => btn.classList.remove("active-filter")); if (!obj || obj.type !== "image" || !obj.filters) return; obj.filters.forEach(filter => { var filterType = filter && filter.type; if (!filterType) return; var btn = document.querySelector('.filter-buttons button[data-filter="' + filterType + '"]'); if (btn) btn.classList.add("active-filter"); }); } function toggleFilter(filterType) { if (cropper) return; if (!img) { Alert("±âº» À̹ÌÁö°¡ ¼±ÅõÇÁö ¾Ê¾Ò½À´Ï´Ù."); return; } activeObj = canvas.getActiveObject(); if (!activeObj || activeObj.type !== "image") { activeObj = canvas.getObjects()[0]; if (!activeObj) return; } var obj = activeObj; if (filterType === "FlipX") { obj.flipX = !obj.flipX; canvas.requestRenderAll(); return; } var existing = obj.filters.find(f => f.type === filterType); if (existing) { obj.filters = obj.filters.filter(f => f.type !== filterType); } else { var filter; switch (filterType) { case "Grayscale": filter = new fabric.Image.filters.Grayscale(); break; case "Sepia": filter = new fabric.Image.filters.Sepia(); break; case "Invert": filter = new fabric.Image.filters.Invert(); break; case "Brightness": filter = new fabric.Image.filters.Brightness({ brightness: 0.2 }); break; case "Contrast": filter = new fabric.Image.filters.Contrast({ contrast: 0.3 }); break; case "Noise": filter = new fabric.Image.filters.Noise({ noise: 180 }); break; case "Pixelate": filter = new fabric.Image.filters.Pixelate({ blocksize: 4 }); break; case "Blur": filter = new fabric.Image.filters.Blur({ blur: 0.07 }); break; case "Saturation": filter = new fabric.Image.filters.Saturation({ saturation: 0.5 }); break; case "Vignette": filter = new fabric.Image.filters.Vignette({ darkness: 0.5, offset: 0.5 }); break; default: return; } if (filter) obj.filters.push(filter); } obj.applyFilters(); highlightAppliedFilters(obj); canvas.requestRenderAll(); } function removeFilters() { if (cropper) return; activeObj = getPrimaryImageObject(); if (!activeObj) return; var objs = (activeObj.type === "activeSelection" || activeObj.type === "group") ? activeObj.getObjects() : [activeObj]; objs.forEach(obj => { if (obj.type === "image" && obj.filters) { obj.filters = []; if (obj.applyFilters) obj.applyFilters(); } highlightAppliedFilters(obj); }); canvas.renderAll(); } function rotateObject() { if (cropper) return; if (!img) { Alert("´ë»óÀÌ ¼±ÅõÇÁö ¾Ê¾Ò½À´Ï´Ù."); return; } var activeObjects = canvas.getActiveObjects(); var rotateBy90 = function (obj) { var angle = ((Math.floor(obj.angle / 90) * 90) + 90) % 360; obj.rotate(angle); obj.setCoords(); }; if (activeObjects.length > 0) { activeObjects.forEach(rotateBy90); } else { var allObjects = canvas.getObjects().slice(); if (allObjects.length === 0) return; var tempGroup = new fabric.Group(allObjects); canvas.clear(); canvas.add(tempGroup); rotateBy90(tempGroup); var items = tempGroup._objects; tempGroup._restoreObjectsState(); canvas.remove(tempGroup); items.forEach(obj => canvas.add(obj)); canvas.renderAll(); } canvas.requestRenderAll(); } function startCrop() { var objects = canvas.getObjects(); if (!img || (!cropper && objects.length > 1)) { Alert("ÀÚ¸£±â´Â ±âº»À̹ÌÁö¸¸ Á¸ÀçÇÒ¶§ °¡´ÉÇÕ´Ï´Ù."); return; } var center = canvas.getCenter(); if (cropper) { applyCrop(); } else { cropper = new fabric.Rect({ left: center.left - 50, top: center.top - 50, width: 100, height: 100, fill: "rgba(0, 0, 125, 0.2)", stroke: "gray", strokeWidth: 0.1, lockRotation: true, selectable: true }); canvas.add(cropper); canvas.setActiveObject(cropper); } } function applyCrop() { var rect = canvas.getActiveObject(); if (!rect || rect.type !== "rect") { startCrop(); return; } if (!img) { Alert("À߸± À̹ÌÁö°¡ ¾ø½À´Ï´Ù."); return; } var bgImg = canvas.getObjects("image")[0]; var scaleX = bgImg.scaleX || 1; var scaleY = bgImg.scaleY || 1; var rectBounds = rect.getBoundingRect(); var imgBounds = bgImg.getBoundingRect(); var cropX = (rectBounds.left - imgBounds.left) / scaleX; var cropY = (rectBounds.top - imgBounds.top) / scaleY; var cropW = rect.getScaledWidth() / scaleX; var cropH = rect.getScaledHeight() / scaleY; var tempCanvas = document.createElement("canvas"); tempCanvas.width = cropW; tempCanvas.height = cropH; var ctx = tempCanvas.getContext("2d"); ctx.drawImage( bgImg.getElement(), cropX, cropY, cropW, cropH, 0, 0, cropW, cropH ); var dataURL = tempCanvas.toDataURL("image/jpeg"); canvas.clear(); addImageToCanvasFromDataURL(dataURL); } function deleteCropper() { if (cropper) { canvas.remove(cropper); cropper = null; } } function getImageBounds(canvasInstance) { canvasInstance.renderAll(); var imageObjects = canvasInstance.getObjects().filter(obj => obj.type === "image"); if (imageObjects.length === 0) { return { left: 0, top: 0, right: 0, bottom: 0 }; } if (scaleAll) { return imageObjects.reduce(function (acc, obj) { var rect = obj.getBoundingRect(true); acc.left = Math.min(acc.left, rect.left); acc.top = Math.min(acc.top, rect.top); acc.right = Math.max(acc.right, rect.left + rect.width); acc.bottom = Math.max(acc.bottom, rect.top + rect.height); return acc; }, { left: Infinity, top: Infinity, right: -Infinity, bottom: -Infinity }); } else { var obj = imageObjects[0]; var rect = obj.getBoundingRect(true); return { left: rect.left, top: rect.top, right: rect.left + rect.width, bottom: rect.top + rect.height }; } } function setInfoArea(idx) { var infoArea = document.getElementById("info-area"); var str = ""; str += "Ver : " + version + "\n"; infoArea.innerHTML = str; } function saveImagesSequentially(idx, delay) { if (checkExampleTextInCanvas(true)) return; if (cropper) canvas.remove(cropper); if (!idx) idx = 0; if (!delay) delay = 0; if (idx == 0 && imageInfo.length > 0) { for (var i = 1; i <= imageMax; i++) { var tail = (i == 1) ? "" : i; opener.PutInitImage(tail); } var objects = canvas.getObjects(); if (objects.length > 0) { imageInfo[jobIndex].wrk = canvas.toJSON(); imageInfo[jobIndex].durl = makeDataURL(jobIndex); } Alert("ó¸®Áß ÀÔ´Ï´Ù. Àá½Ã¸¸ ±â´Ù·ÁÁÖ¼¼¿ä.."); } if (idx >= imageInfo.length) { self.close(); return; } var durl = imageInfo[idx].durl; if (durl != null) { setTimeout(function () { saveImage(idx, function () { saveImagesSequentially(idx + 1, delay); }); }, delay); delay += 400; } } function saveImage(idx, callback) { if (imageInfo[idx].filename == null) { imageInfo[idx].filename = "m20260317171759_" + idx + ".jpg"; } var formData = new FormData(); formData.append("data", imageInfo[idx].durl); formData.append("image_width", imageInfo[idx].width); formData.append("image_height", imageInfo[idx].height); formData.append("image_size", imageInfo[idx].durl.length); formData.append("image_name", imageInfo[idx].filename); formData.append("image_cate", imageInfo[idx].cate); $.ajax({ type: "POST", url: "/image_editor/save.htm", data: formData, processData: false, contentType: false, async: false, success: function (data) { var parts = data.split("|"); var num = parts[0]; var name = parts[1]; var edate = parts[2]; opener.PutImage(opener.document.send_msg, num, name, edate); opener.MoveScrollObject("hp_top", -80); } }); if (callback) callback(); } function saveCanvasToServer() { if (!img) { Alert("±âº» À̹ÌÁö°¡ ¼±ÅõÇÁö ¾Ê¾Ò½À´Ï´Ù."); return; } var jsonData = canvas.toJSON(); var jsonString = JSON.stringify(jsonData); var sampleImg = makeDataURL(); var postData = { json: jsonString, image: sampleImg }; fetch("index.html", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(postData) }) .then(response => response.blob()) .then(blob => { var reader = new FileReader(); reader.onload = function () { var text = reader.result; var rslt = text.split("|"); if (rslt[0] != 1) { console.error("¿¡·¯:", rslt[1]); Alert(rslt[1], 1); } else { console.log("ÀúÀå °á°ú:", text); SetCate("my_smart"); } }; reader.readAsText(blob, "euc-kr"); }) .catch(error => { console.error("Åë½Å ¿À·ù:", error); }); return; } function loadCanvasFromServer(fname, edate) { if (!fname) return; var path = "/smart_image/" + edate; var jsonName = path + "/" + fname + ".json"; var imgName = path + "/" + fname + ".jpg"; $("#btn-layer-editor").show(); fetch(jsonName) .then(response => { if (!response.ok) throw new Error("ÆÄÀÏÀ» ãÀ» ¼ö ¾ø½À´Ï´Ù."); return response.json(); }) .then(jsonData => { imageCate = 6; makeType = "smart_image"; setGuestCount(makeType); imageInfo[jobIndex] = { src: jsonData, wrk: jsonData, durl: null, width: null, height: null, filename: imgName, cate: imageCate }; setTimeout(function () { $("#image-overlay").hide(); addJsonToCanvas(jsonData, imgName); console.log("make type :", makeType, ", cate :", imageInfo[jobIndex].cate); }, 300); }) .catch(err => { console.error("·Îµù ½ÇÆÐ:", err); }); } function makeDataURL(idx) { var objects = canvas.getObjects(); if (objects.length === 0) return; var bounds = getImageBounds(canvas); var userImageWidth = bounds.right - bounds.left; var userImageHeight = bounds.bottom - bounds.top; var exportWidth = backImageWidth; var exportHeight = backImageHeight; var baseImage = objects.find(obj => obj.type === "image"); var isRotated = baseImage && baseImage.angle % 180 !== 0; if (isRotated) { var temp = exportWidth; exportWidth = exportHeight; exportHeight = temp; } if (exportWidth > limitWidth || exportHeight > limitHeight) { var scale = Math.min(limitWidth / exportWidth, limitHeight / exportHeight); exportWidth *= scale; exportHeight *= scale; } var scaleX = exportWidth / userImageWidth; var scaleY = exportHeight / userImageHeight; var tempCanvasEl = document.createElement("canvas"); tempCanvasEl.width = exportWidth; tempCanvasEl.height = exportHeight; var tempCanvas = new fabric.Canvas(tempCanvasEl, { backgroundColor: "white" }); objects.forEach(obj => { if (obj !== null) { var clone = fabric.util.object.clone(obj); clone.originX = "center"; clone.originY = "center"; clone.scaleX *= scaleX; clone.scaleY *= scaleY; var center = obj.getCenterPoint(); clone.left = (center.x - bounds.left) * scaleX; clone.top = (center.y - bounds.top) * scaleY; clone.setCoords(); tempCanvas.add(clone); } }); tempCanvas.renderAll(); var dataURL = tempCanvas.toDataURL({ format: "jpeg", quality: 0.7 }); tempCanvas.dispose(); if (typeof idx !== "undefined" && imageInfo[idx]) { imageInfo[idx].width = exportWidth; imageInfo[idx].height = exportHeight; } return dataURL; } function addText(txt) { if (cropper) return; if (!img) { Alert("±âº» À̹ÌÁö°¡ ¼±ÅõÇÁö ¾Ê¾Ò½À´Ï´Ù."); return; } var defaultText = txt || "³»¿ëÃß°¡"; var textCount = canvas.getObjects().filter(obj => obj.type === "textbox").length; var bgImage = canvas._objects[0]; text = new fabric.Textbox(defaultText, { left: canvas.getWidth() / 2 + (textCount * 7) + 8, top: canvas.getHeight() / 2 - (textCount * 7), width: bgImage.width * bgImage.scaleX - 25, fontSize: document.getElementById("font-size-select").value, fill: document.getElementById("text-color").value, fontFamily: document.getElementById("font-family-select").value, fontWeight: "bold", backgroundColor: Hex2Rgba(document.getElementById("text-bgcolor").value, alpha), selectable: true, editable: true, padding: 0, lineHeight: 1.4, originX: "center", originY: "top", textAlign: textAlignStat, strokeWidth: 16, splitByGrapheme: true, objectCaching: false, breakWords: true, noScaleCache: false }); text.setControlsVisibility({ mt: false, mb: false, tl: false, tr: false, bl: false, br: false }); document.fonts.ready.then(() => { canvas.add(text); text.setCoords(); text.set({ dirty: true, objectCaching: false }); canvas.setActiveObject(text); var count = canvas.getObjects().filter(obj => obj.type === "textbox").length; if (count == 1) { text.enterEditing(); text.selectAll(); } canvas.requestRenderAll(); }); } function insertText(val) { if (cropper) return; var obj = canvas.getActiveObject(); if (!obj || obj.type !== "textbox") { var textboxes = canvas.getObjects().filter(o => o.type === "textbox"); obj = textboxes.length > 0 ? textboxes[textboxes.length - 1] : null; if (!obj || obj.type !== "textbox") { addText(val); return; } } var splitter = new GraphemeSplitter(); var textUnits = splitter.splitGraphemes(obj.text); var start = obj.selectionStart != null ? obj.selectionStart : textUnits.length; var end = obj.selectionEnd != null ? obj.selectionEnd : textUnits.length; var newText = [].concat(textUnits.slice(0, start), [val], textUnits.slice(end)).join(""); obj.text = newText; var beforeText = newText.slice(0, [].concat(textUnits.slice(0, start), [val]).join("").length); var realCursorOffset = beforeText.length; obj.selectionStart = obj.selectionEnd = realCursorOffset; if (obj.hiddenTextarea) { obj.hiddenTextarea.value = newText; obj.hiddenTextarea.selectionStart = obj.hiddenTextarea.selectionEnd = realCursorOffset; setTimeout(() => obj.hiddenTextarea.focus(), 0); } obj.setCoords(); canvas.requestRenderAll(); } function applyToTextbox(callback) { if (cropper) return; activeObj = canvas.getActiveObject(); var applyTo = function (obj) { if (obj.styles && typeof obj.styles === "object") { obj.styles = {}; } callback(obj); }; if (activeObj && activeObj.type === "activeSelection") { activeObj.getObjects().forEach(obj => { if (obj.type === "textbox") applyTo(obj); }); } else if (activeObj && activeObj.type === "textbox") { applyTo(activeObj); } else { var allObjects = canvas.getObjects(); var recentTextbox = allObjects.slice().reverse().find(obj => obj.type === "textbox"); if (recentTextbox) { canvas.setActiveObject(recentTextbox); applyTo(recentTextbox); } else { addText(); } } canvas.renderAll(); } function setImageAttr() { if (!img) { Alert("±âº» À̹ÌÁö°¡ ¼±ÅõÇÁö ¾Ê¾Ò½À´Ï´Ù."); return; } document.querySelector("#image-overlay").style.display = "none"; var center = canvas.getCenter(); var images = canvas.getObjects("image"); img.set({ originX: "center", originY: "center", left: center.left + images.length * 8, top: center.top + images.length * 8, lockRotation: false, lockScalingX: false, lockScalingY: false, hasControls: true, lockScalingFlip: true, hoverCursor: "move", selectable: true, evented: true }); var jsonData = canvas.toJSON(); if (images.length == 1) { imageInfo[jobIndex] = { src: jsonData, wrk: null, durl: null, width: null, height: null, filename: null, cate: imageCate }; console.log("base image : ", images.length); console.log("make type :", makeType, ", cate :", imageInfo[jobIndex].cate); img.set({ lockMovementX: true, lockMovementY: true, hasControls: false, selectable: false, evented: false }); backImageWidth = img.width; backImageHeight = img.height; imageWidth = img.width * img.scaleX; imageHeight = img.height * img.scaleY; canvas.discardActiveObject(); addImageToThumbnail(img.toDataURL(), jobIndex); imageInfo[jobIndex].wrk = jsonData; } else { var baseImg = canvas._objects[0]; if (baseImg) { var scaleRatioX = (baseImg.scaleX || 1) * 0.5; var scaleRatioY = (baseImg.scaleY || 1) * 0.5; img.scaleX = scaleRatioX; img.scaleY = scaleRatioY; } canvas.setActiveObject(img); } img.setCoords(); if (cropper) cropper = null; document.querySelector("#btn-layer-editor").style.display = "inline"; } function addImageToCanvasFromDataURL(dataURL) { return new Promise((resolve, reject) => { try { var textboxes = canvas.getObjects().filter(obj => obj.type === "textbox"); textboxes.forEach(obj => canvas.remove(obj)); fabric.Image.fromURL(dataURL, function (image) { try { image._originalElement = image.getElement(); img = image; var widthRatio = canvasWidth / image.width; var heightRatio = canvasHeight / image.height; var scaleRatio = Math.min(widthRatio, heightRatio, 1); image.scale(scaleRatio); image.setCoords(); imageWidth = image.width * image.scaleX; imageHeight = image.height * image.scaleY; canvas.add(image); setImageAttr(); textboxes.forEach(obj => { canvas.add(obj); obj.setCoords(); }); canvas.renderAll(); if (imageInfo[jobIndex]) { imageInfo[jobIndex].durl = makeDataURL(jobIndex); } resolve(image); } catch (err) { reject(err); } }, { crossOrigin: "anonymous" }); } catch (err) { reject(err); } }); } function addImageToCanvas(file, sticker) { return new Promise((resolve, reject) => { deleteCropper(); if (sticker === 1 && !imageInfo[jobIndex]) { Alert("±âº»À̹ÌÁö¸¦ ¸ÕÀú ¼±ÅÃÇØÁÖ¼¼¿ä"); reject(new Error("base image missing")); return; } if (sticker !== 1 && !document.getElementById("multi-image").checked) { var textObj = canvas.getObjects().find(obj => obj.type === "textbox"); var overlay = document.getElementById("aiOverlay2"); var isOverlayActive = overlay && overlay.style.display !== "none"; if (!isOverlayActive && textObj && !confirm("»õ·Î¿î ÀÛ¾÷À» ÁøÇàÇϽðڽÀ´Ï±î?")) { reject(new Error("user cancelled")); return; } canvas.clear(); } var reader = new FileReader(); reader.onload = function (event) { addImageToCanvasFromDataURL(event.target.result) .then(resolve) .catch(reject); }; reader.onerror = function (err) { reject(err); }; reader.readAsDataURL(file); document.querySelector("#image-overlay").style.display = "none"; document.querySelector("#btn-layer-editor").style.display = "inline"; }); } function addJsonToCanvasLast(jdata, filePath) { canvas.clear(); canvas.loadFromJSON(jdata, function () { img = canvas.getObjects().find(obj => obj.type === "image"); if (img) { img.set({ lockMovementX: true, lockMovementY: true, hasControls: false, selectable: false, evented: false }); img.setCoords(); canvas.sendToBack(img); backImageWidth = img.width; backImageHeight = img.height; } canvas.getObjects().forEach(obj => { if (obj.type === "textbox") { obj.set({ objectCaching: false }); } }); if (filePath) { var fimg = new Image(); fimg.onload = function () { var tempCanvas = document.createElement("canvas"); tempCanvas.width = fimg.width; tempCanvas.height = fimg.height; var ctx = tempCanvas.getContext("2d"); ctx.drawImage(fimg, 0, 0); var dataURL = tempCanvas.toDataURL("image/jpeg"); addImageToThumbnail(dataURL, jobIndex); }; fimg.onerror = function () { console.warn("À̹ÌÁö ·Îµå ½ÇÆÐ:", filePath); }; fimg.src = filePath; } document.fonts.ready.then(() => { var texts = canvas.getObjects().filter(obj => obj.type === "textbox"); texts.forEach(textObj => { textObj.setControlsVisibility({ mt: false, mb: false, tl: false, tr: false, bl: false, br: false }); textObj.set({ splitByGrapheme: true, breakWords: true, noScaleCache: false, objectCaching: false }); textObj.initDimensions(); textObj.setCoords(); }); canvas.renderAll(); }); }); } function addJsonToCanvas(jdata, filePath) { var usedFonts = Array.from(new Set( (jdata.objects || []) .filter(obj => obj.type === "textbox" && obj.fontFamily) .map(obj => obj.fontFamily) )); if (usedFonts.length === 0) { addJsonToCanvasLast(jdata, filePath); return; } Promise.all( usedFonts.map(font => document.fonts.load("16px '" + font + "'")) ) .then(() => { addJsonToCanvasLast(jdata, filePath); }) .catch(() => { addJsonToCanvasLast(jdata, filePath); }); } function addImageToThumbnail(src, idx) { var div = document.getElementById("thumbnail" + idx); if (!div) { document.location.reload(); return; } div.style.position = "relative"; var im = document.createElement("img"); im.src = src; im.style.width = "100%"; im.style.height = "auto"; im.style.display = "block"; im.title = ""; im.draggable = false; var closeBtn = document.createElement("button"); closeBtn.className = "close-button"; closeBtn.innerText = "¥¹"; closeBtn.title = ""; closeBtn.style.fontSize = "12px"; closeBtn.style.position = "absolute"; closeBtn.style.top = "1px"; closeBtn.style.right = "0px"; closeBtn.style.backgroundColor = "rgba(0, 0, 0, 0.4)"; closeBtn.style.color = "white"; closeBtn.style.border = "none"; closeBtn.style.borderRadius = "50%"; closeBtn.style.width = "20px"; closeBtn.style.height = "20px"; closeBtn.style.cursor = "pointer"; $(closeBtn).click(function (e) { e.stopPropagation(); deleteThumbnail($(".close-button").index(this)); }); div.replaceChildren(im, closeBtn); } async function callImageFromServer(url, sticker) { if (!url) return; url = url.replace("_1.jpg", ".jpg"); try { var response = await fetch(url); var blob = await response.blob(); var localFileName = url.substring(url.lastIndexOf("/") + 1); var file = new File([blob], localFileName, { type: blob.type }); await addImageToCanvas(file, sticker); } catch (err) { console.error("À̹ÌÁö ºÒ·¯¿À±â ½ÇÆÐ:", err); Alert("À̹ÌÁö¸¦ ºÒ·¯¿ÀÁö ¸øÇß½À´Ï´Ù."); throw err; } } function getParentImageUrls() { if (!opener || !opener.document) return []; var nodes = opener.document.querySelectorAll(".photo_list_image"); var urls = Array.prototype.slice.call(nodes) .map(node => node.src) .filter(Boolean) .map(src => src.replace("_1.jpg", ".jpg")); return Array.from(new Set(urls)).slice(0, imageMax); } async function loadImagesSequentially() { var urls = getParentImageUrls(); if (!urls.length) { $("#image-overlay").show(); return; } isBootLoading = true; isApplyingParentImage = true; try { for (var idx = 0; idx < urls.length && idx < imageMax; idx++) { jobIndex = idx; await callImageFromServer(urls[idx], 0); await new Promise(resolve => setTimeout(resolve, 120)); } if (imageInfo[0] && imageInfo[0].wrk) { jobIndex = 0; addJsonToCanvas(imageInfo[0].wrk); $("#image-overlay").hide(); $("#btn-layer-editor").show(); } else { $("#image-overlay").show(); $("#btn-layer-editor").hide(); } SetCate("sticker_sample"); } catch (err) { console.error("ºÎ¸ðâ À̹ÌÁö ¼øÂ÷ ·Îµå ½ÇÆÐ:", err); } finally { isApplyingParentImage = false; isBootLoading = false; } } function textAlign(value) { $(".text-align").css("opacity", 0.4); $("#text-align-" + value).css("opacity", 1.0); textAlignStat = value; applyToTextbox(obj => obj.set("textAlign", value)); } function bringToFront() { if (!canvas) return; var activeObjects = canvas.getActiveObjects(); if (activeObjects && activeObjects.length > 0) { activeObjects.forEach(obj => { obj.bringToFront(); highlightObject(obj); }); } else { var objects = canvas.getObjects(); if (objects.length > 0) { var lastObj = objects[objects.length - 1]; lastObj.bringToFront(); highlightObject(lastObj); } } canvas.requestRenderAll(); } function normalizeExampleText(textValue) { return String(textValue || "") .toLowerCase() .replace(/\s+/g, "") .replace(/[\-_.(),]/g, ""); } function findExampleText(textValue) { var normalized = normalizeExampleText(textValue); var exampleWords = [ "È«±æµ¿", "01012345678", "01000000000", "¡Û¡Û¡Û", "³»¿ëÃß°¡" ]; for (var i = 0; i < exampleWords.length; i++) { var word = exampleWords[i]; var normalizedWord = normalizeExampleText(word); if (normalized.includes(normalizedWord)) { return word; } } return null; } function checkExampleTextInCanvas(showAlert) { var textboxes = canvas.getObjects().filter(obj => obj.type === "textbox"); for (var i = 0; i < textboxes.length; i++) { var obj = textboxes[i]; var txt = (obj.text || "").trim(); var matchedWord = findExampleText(txt); if (matchedWord) { console.log("matched:", matchedWord, " / full text:", txt); canvas.setActiveObject(obj); obj.setCoords(); canvas.requestRenderAll(); if (typeof obj.enterEditing === "function") { obj.enterEditing(); obj.selectAll(); } if (showAlert !== false) { Alert( "³»¿ë¿¡ ¾Æ·¡¿Í °°ÀÌ ¿¹½Ã·Î »ç¿ëµÇ´Â ¹®±¸°¡\n" + "Æ÷ÇԵǾî ÀÖ½À´Ï´Ù. ¼öÁ¤ ÈÄ ÁøÇàÇØÁÖ¼¼¿ä.\n\n" + matchedWord ); } return true; } } return false; } function highlightObject(obj) { if (!obj) return; var originalStroke = obj.stroke; var originalStrokeWidth = obj.strokeWidth; obj.set({ opacity: 0.3 }); canvas.requestRenderAll(); setTimeout(function () { obj.set({ stroke: originalStroke || null, strokeWidth: originalStrokeWidth || 0, opacity: 1 }); canvas.requestRenderAll(); }, 200); } window.onload = function () { if (!opener) { Alert("¸ÞÀÎâ°ú Åë½ÅÀÌ ²÷°å½À´Ï´Ù. âÀ» ´Ù½Ã ¿©¼¼¿ä."); return; } document.addEventListener("keydown", function (e) { if (isBootLoading) return; var el = e.target; var isTyping = el && ( el.tagName === "TEXTAREA" || (el.tagName === "INPUT" && !["checkbox", "radio", "button", "submit", "reset", "file"].includes((el.type || "").toLowerCase())) || el.isContentEditable ); if (isTyping) return; if (e.key === "Delete" || e.key === "Backspace") { deleteObject(); } if (e.key === "Enter" || e.key === "+") { if (cropper) { applyCrop(); return; } if (img && !canvas.getActiveObject()) { e.preventDefault(); addText(); } } if (e.key === "i") { console.log(imageInfo); } }); document.addEventListener("click", function () { if (isBootLoading) return; activeObj = canvas.getActiveObject(); if (activeObj && activeObj.type === "textbox") { document.getElementById("font-family-select").value = activeObj.fontFamily; document.getElementById("font-size-select").value = activeObj.fontSize; document.getElementById("font-lineheight-select").value = activeObj.lineHeight; textAlign(activeObj.textAlign); } if (!activeObj && canvas._objects[0]) { highlightAppliedFilters(canvas._objects[0]); } }); document.getElementById("upload").addEventListener("change", function (e) { if (isBootLoading) return; var file = e.target.files[0]; if (file && file.type.startsWith("image/")) { imageCate = 0; addImageToCanvas(file).catch(err => console.error(err)); } $("#upload").val(""); SetCate("sticker_sample"); }); document.getElementById("text-color").addEventListener("input", function () { applyToTextbox(obj => obj.set("fill", this.value)); }); document.getElementById("text-bgcolor").addEventListener("input", function () { var bgColor = Hex2Rgba(this.value, alpha || 0.3); applyToTextbox(obj => obj.set("backgroundColor", bgColor)); }); document.getElementById("text-alpha").addEventListener("click", function () { applyToTextbox(obj => { var bgColor = obj.backgroundColor; if (!bgColor) return; var rgbaMatch = bgColor.match(/rgba?\(([^)]+)\)/); if (!rgbaMatch) return; var parts = rgbaMatch[1].split(",").map(p => p.trim()); if (parts.length < 3) return; var r = parts[0]; var g = parts[1]; var b = parts[2]; var a = parts.length === 4 ? parseFloat(parts[3]) : alpha; alpha = a > 0 ? 0 : 0.3; obj.set("backgroundColor", "rgba(" + r + ", " + g + ", " + b + ", " + alpha + ")"); }); }); document.getElementById("font-family-select").addEventListener("change", function () { applyToTextbox(obj => obj.set("fontFamily", this.value)); }); document.getElementById("font-size-select").addEventListener("input", function () { applyToTextbox(obj => obj.set("fontSize", parseFloat(this.value))); }); document.getElementById("font-lineheight-select").addEventListener("input", function () { applyToTextbox(obj => obj.set("lineHeight", parseFloat(this.value))); }); requestAnimationFrame(function () { var container = document.getElementById("canvas-container"); var width = container.offsetWidth; var height = container.offsetHeight; console.log("Canvas container size:", width, height); width = width || 700; height = height || 700; canvas = new fabric.Canvas("canvas", { width: width, height: height }); canvas.on("object:moving", function (e) { var obj = e.target; obj.setCoords(); if (obj.type == "textbox" || obj.type == "rect") { var bounds = getImageBounds(canvas); var rect = obj.getBoundingRect(true); var newLeft = obj.left; var newTop = obj.top; if (rect.left < bounds.left) { newLeft += bounds.left - rect.left; } if (rect.left + rect.width > bounds.right) { newLeft -= (rect.left + rect.width) - bounds.right; } if (rect.top < bounds.top) { newTop += bounds.top - rect.top; } if (rect.top + rect.height > bounds.bottom) { newTop -= (rect.top + rect.height) - bounds.bottom; } obj.set({ left: newLeft, top: newTop }); obj.setCoords(); } else { var boundingBox = obj.getBoundingRect(true); if (boundingBox.left < 0) { obj.left -= boundingBox.left; } else if (boundingBox.left + boundingBox.width > canvas.getWidth()) { obj.left -= (boundingBox.left + boundingBox.width - canvas.getWidth()); } if (boundingBox.top < 0) { obj.top -= boundingBox.top; } else if (boundingBox.top + boundingBox.height > canvas.getHeight()) { obj.top -= (boundingBox.top + boundingBox.height - canvas.getHeight()); } } }); canvas.on("object:scaling", function (e) { var obj = e.target; if (obj.type !== "textbox" && obj.type !== "rect") return; obj.set({ originX: "left", originY: "top" }); var bounds = getImageBounds(canvas); var rect = obj.getBoundingRect(true); var newScaleX = obj.scaleX; var newScaleY = obj.scaleY; var newLeft = obj.left; var newTop = obj.top; var objWidth = obj.width * newScaleX; var objHeight = obj.height * newScaleY; if (rect.left < bounds.left) { var diffL = bounds.left - rect.left; newLeft = bounds.left; var maxWidthL = objWidth - diffL; newScaleX = Math.max(1, maxWidthL / obj.width); } if (rect.top < bounds.top) { var diffT = bounds.top - rect.top; newTop = bounds.top; var maxHeightT = objHeight - diffT; newScaleY = Math.max(1, maxHeightT / obj.height); } if (rect.left + rect.width > bounds.right) { var diffR = (rect.left + rect.width) - bounds.right; var maxWidthR = objWidth - diffR; newScaleX = Math.max(1, maxWidthR / obj.width); } if (rect.top + rect.height > bounds.bottom) { var diffB = (rect.top + rect.height) - bounds.bottom; var maxHeightB = objHeight - diffB; newScaleY = Math.max(1, maxHeightB / obj.height); } obj.set({ scaleX: newScaleX, scaleY: newScaleY, left: newLeft, top: newTop }); obj.setCoords(); }); canvas.on("mouse:down", function (e) { if (isBootLoading) return; var obj = e.target; if (!obj) { deleteCropper(); } }); document.getElementById("image-overlay").addEventListener("dragover", function (e) { e.preventDefault(); }); document.getElementById("image-overlay").addEventListener("drop", function (e) { if (isBootLoading) return; e.preventDefault(); canvas.upperCanvasEl.style.border = "none"; var file = e.dataTransfer.files[0]; if (file && file.type.startsWith("image/")) { addImageToCanvas(file).catch(err => console.error(err)); } }); canvas.upperCanvasEl.addEventListener("dragover", function (e) { e.preventDefault(); }); canvas.upperCanvasEl.addEventListener("drop", function (e) { if (isBootLoading) return; e.preventDefault(); canvas.upperCanvasEl.style.border = "none"; var file = e.dataTransfer.files[0]; if (file && file.type.startsWith("image/")) { if (imageInfo[jobIndex]) imageInfo[jobIndex].filename = file.name; addImageToCanvas(file, 1).catch(err => console.error(err)); } }); canvas.upperCanvasEl.addEventListener("click", function (e) { e.preventDefault(); }); canvas.on("selection:created", function (e) { var obj = e.selected[0]; highlightAppliedFilters(obj); }); canvas.on("selection:updated", function (e) { var obj = e.selected[0]; highlightAppliedFilters(obj); }); canvas.on("selection:cleared", function () { document.querySelectorAll(".filter-buttons button").forEach(btn => btn.classList.remove("active-filter")); }); }, 0); setInfoArea(); parentImg = opener.document.querySelectorAll(".photo_list_image"); var tnList = document.getElementById("thumbnail-container"); for (var i = 0; i < imageMax; i++) { (function (idx) { var div = document.createElement("div"); div.className = "thumbnail"; div.id = "thumbnail" + idx; div.title = "À§Ä¡ ¼±ÅÃÀº ¿À¸¥ÂÊ ¹öưÀ» ´©¸£¼¼¿ä."; div.onclick = function () { if (isBootLoading || isApplyingParentImage) return; var objects = canvas.getObjects(); if (objects.length) { imageInfo[jobIndex].wrk = canvas.toJSON(); imageInfo[jobIndex].durl = makeDataURL(jobIndex); } if (idx >= imageInfo.length) { jobIndex = idx; if (jobIndex >= imageMax) jobIndex = imageMax - 1; document.getElementById("upload").click(); $("#image-overlay").show(); $("#btn-layer-editor").hide(); } else { if (idx == jobIndex) { if (!confirm("À̹ÌÁö¸¦ ºÒ·¯¿À°Ú½À´Ï±î?")) return; document.getElementById("upload").click(); return; } jobIndex = idx; var jsonData = imageInfo[jobIndex].wrk; addJsonToCanvas(jsonData); $("#image-overlay").hide(); $("#btn-layer-editor").show(); } console.log("job index : ", jobIndex, ", click index : ", idx); }; $(div).on("contextmenu", function (e) { if (isBootLoading || isApplyingParentImage) return; e.preventDefault(); var objects = canvas.getObjects(); if (objects.length && imageInfo[jobIndex]) { imageInfo[jobIndex].wrk = canvas.toJSON(); imageInfo[jobIndex].durl = makeDataURL(jobIndex); } jobIndex = $(".thumbnail").index(this); if (imageInfo[jobIndex] == null) { canvas.clear(); jobIndex = imageInfo.length; if (jobIndex >= imageMax) jobIndex = imageMax - 1; $("#image-overlay").show(); $("#btn-layer-editor").hide(); this.title = "À§Ä¡ ¼±ÅÃÀº ¿À¸¥ÂÊ ¹öưÀ» ´©¸£¼¼¿ä."; } else { $("#image-overlay").hide(); $("#btn-layer-editor").show(); addJsonToCanvas(imageInfo[jobIndex].wrk); } }); tnList.appendChild(div); })(i); } if (!startInfo[0] || !startInfo[1]) { loadImagesSequentially(); } else { loadCanvasFromServer(startInfo[0], startInfo[1]); } };