script.js

40.16 KB
20/06/2025 08:14
JS
script.js
// Global variables
let canvas = null;
let ctx = null;
let currentImage = null;
let selectedTemplate = null;

// Text Position Management
let textPositions = {
  top: {
    x: 'center', // left, center, right
    y: 15 // percentage from top
  },
  bottom: {
    x: 'center',
    y: 85 // percentage from top
  }
};

// ==============================================
// Advanced Draggable Text System
// ==============================================

// Text positions and settings
let textElements = {
  top: {
    x: 0.5,  // Relative position (0-1)
    y: 0.15,
    text: '',
    isDragging: false,
    fontSize: 40,
    fontFamily: 'Arial Black',
    color: 'white',
    strokeColor: 'black',
    strokeWidth: 3,
    align: 'center'
  },
  bottom: {
    x: 0.5,
    y: 0.85,
    text: '',
    isDragging: false,
    fontSize: 40,
    fontFamily: 'Arial Black',
    color: 'white',
    strokeColor: 'black',
    strokeWidth: 3,
    align: 'center'
  }
};

let dragState = {
  isDragging: false,
  currentElement: null,
  startX: 0,
  startY: 0,
  offsetX: 0,
  offsetY: 0
};

// Initialize the application
document.addEventListener('DOMContentLoaded', function() {
  initializeCanvas();
  loadTemplates();
  setupEventListeners();
  setDefaultMeme();
  // Initialize text position controls
  initializeTextPositionControls();
  initializeDraggableText();
  // Initialize modal system
  initializeModal();
});

// Initialize canvas
function initializeCanvas() {
  canvas = document.getElementById('meme-canvas');
  ctx = canvas.getContext('2d');
  canvas.width = 600;
  canvas.height = 400;

  // Set default canvas background
  ctx.fillStyle = '#f0f0f0';
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  // Draw placeholder text with better font
  ctx.fillStyle = '#666';
  ctx.font = '20px "Inter", Arial, sans-serif';
  ctx.textAlign = 'center';
  ctx.fillText('เลือกรูปภาพหรือเทมเพลตเพื่อเริ่มสร้างมีม', canvas.width / 2, canvas.height / 2);
}

// Setup event listeners
function setupEventListeners() {
  // Theme switcher
  document.getElementById('theme-switcher').addEventListener('click', toggleTheme);

  // Text inputs
  document.getElementById('top-text').addEventListener('input', updateTextElements);
  document.getElementById('bottom-text').addEventListener('input', updateTextElements);

  // File upload
  document.getElementById('image-upload').addEventListener('change', handleImageUpload);

  // Clear button
  document.getElementById('clear-btn').addEventListener('click', clearMeme);

  // Download button
  document.getElementById('download-btn').addEventListener('click', downloadMeme);

  // Update canvas size display
  updateCanvasSize();
}

// Select template
function selectTemplate(template, element) {
  // Remove previous selection
  document.querySelectorAll('.template-item').forEach(item => {
    item.classList.remove('selected');
  });

  // Add selection to current template
  element.classList.add('selected');

  // Haptic feedback for mobile
  if (navigator.vibrate) {
    navigator.vibrate(50);
  }

  selectedTemplate = template;

  // Load template image
  const img = new Image();
  img.crossOrigin = 'anonymous';
  img.onload = function() {
    currentImage = img;
    canvas.width = template.width;
    canvas.height = template.height;
    updateMeme();
    updateCanvasSize();

    // Show toast if available
    if (typeof showToast === 'function') {
      showToast(`เลือกเทมเพลต: ${template.name}`);
    }
  };

  img.onerror = function() {
    console.error('Failed to load template:', template.src);
    if (typeof showToast === 'function') {
      showToast('❌ ไม่สามารถโหลดเทมเพลตได้', 'error');
    }
  };

  img.src = template.src;
}

// Toggle theme
function toggleTheme() {
  const body = document.body;
  const currentTheme = body.getAttribute('data-theme');
  const newTheme = currentTheme === 'light' ? 'dark' : 'light';
  body.setAttribute('data-theme', newTheme);

  // Update theme icon
  const themeIcon = document.querySelector('.theme-icon');
  themeIcon.textContent = newTheme === 'light' ? '🌙' : '☀️';

  // Save theme preference
  localStorage.setItem('theme', newTheme);
}

// Handle image upload
function handleImageUpload(event) {
  const file = event.target.files[0];
  if (!file) return;

  if (!validateFile(file)) {
    event.target.value = ''; // Clear invalid file
    return;
  }

  // Clear template selection
  document.querySelectorAll('.template-item').forEach(item => {
    item.classList.remove('selected');
  });
  selectedTemplate = null;

  const reader = new FileReader();
  reader.onload = function(e) {
    const img = new Image();
    img.onload = function() {
      currentImage = img;

      // Resize canvas to fit image while maintaining aspect ratio
      const maxWidth = 600;
      const maxHeight = 600;
      let {width, height} = img;

      if (width > maxWidth || height > maxHeight) {
        const ratio = Math.min(maxWidth / width, maxHeight / height);
        width *= ratio;
        height *= ratio;
      }

      canvas.width = width;
      canvas.height = height;
      updateMeme();
    };
    img.onerror = () => handleImageError(img);
    img.src = e.target.result;
  };
  reader.readAsDataURL(file);
}

// Initialize text position controls
function initializeTextPositionControls() {
  // Position buttons
  document.querySelectorAll('.position-btn').forEach(btn => {
    btn.addEventListener('click', function() {
      const textType = this.dataset.text; // 'top' or 'bottom'
      const position = this.dataset.pos; // 'left', 'center', 'right'

      // Update active state
      document.querySelectorAll(`[data-text="${textType}"]`).forEach(b => b.classList.remove('active'));
      this.classList.add('active');

      // Update position
      textPositions[textType].x = position;
      updateMemeWithPositions();

      // Haptic feedback
      if (navigator.vibrate) {
        navigator.vibrate(30);
      }

      // Show toast
      const positionNames = {left: 'ซ้าย', center: 'กลาง', right: 'ขวา'};
      if (typeof showToast === 'function') {
        showToast(`📍 ย้ายข้อความ${textType === 'top' ? 'บน' : 'ล่าง'}ไป${positionNames[position]}`, 'success');
      }
    });
  });

  // Set initial active states
  document.querySelector('[data-text="top"][data-pos="center"]')?.classList.add('active');
  document.querySelector('[data-text="bottom"][data-pos="center"]')?.classList.add('active');
}

// Enhanced text drawing with position support
function drawTextWithPosition(text, textType) {
  if (!text || !currentImage) return;

  const position = textPositions[textType];
  const fontSize = Math.max(canvas.width / 15, 24);
  const strokeWidth = fontSize / 12;

  ctx.font = `bold ${fontSize}px "Impact", "Arial Black", sans-serif`;
  ctx.textAlign = position.x === 'left' ? 'left' : position.x === 'right' ? 'right' : 'center';
  ctx.fillStyle = 'white';
  ctx.strokeStyle = 'black';
  ctx.lineWidth = strokeWidth;
  ctx.lineJoin = 'round';
  ctx.miterLimit = 2;

  // Calculate X position
  let x;
  const margin = canvas.width * 0.05; // 5% margin
  switch (position.x) {
    case 'left':
      x = margin;
      break;
    case 'right':
      x = canvas.width - margin;
      break;
    default: // center
      x = canvas.width / 2;
      break;
  }

  // Calculate Y position
  const y = (canvas.height * position.y) / 100;

  // Word wrapping
  const words = text.split(' ');
  const lines = [];
  let currentLine = words[0] || '';

  for (let i = 1; i < words.length; i++) {
    const testLine = currentLine + ' ' + words[i];
    const metrics = ctx.measureText(testLine);
    const testWidth = metrics.width;

    if (testWidth > canvas.width - (margin * 2)) {
      lines.push(currentLine);
      currentLine = words[i];
    } else {
      currentLine = testLine;
    }
  }
  lines.push(currentLine);

  // Draw text lines
  const lineHeight = fontSize * 1.2;
  const totalHeight = lines.length * lineHeight;
  let startY = y;

  // Adjust Y position for multiple lines
  if (textType === 'top') {
    startY = y + fontSize;
  } else {
    startY = y - totalHeight + fontSize;
  }

  lines.forEach((line, index) => {
    const lineY = startY + (index * lineHeight);

    // Draw text stroke (outline)
    ctx.strokeText(line, x, lineY);
    // Draw text fill
    ctx.fillText(line, x, lineY);
  });
}

// Enhanced updateMeme function with position support
function updateMemeWithPositions() {
  if (!currentImage) return;

  // Clear canvas
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // Draw image
  ctx.drawImage(currentImage, 0, 0, canvas.width, canvas.height);

  // Draw texts with positions
  const topText = document.getElementById('top-text').value;
  const bottomText = document.getElementById('bottom-text').value;

  if (topText) {
    drawTextWithPosition(topText, 'top');
  }

  if (bottomText) {
    drawTextWithPosition(bottomText, 'bottom');
  }
}

// Compatibility functions
function updateMeme() {
  drawMemeWithDraggableText();
}

function updateMemeWithPositions() {
  drawMemeWithDraggableText();
}

// Reset text positions
function resetTextPositions() {
  if (!textElements) {
    console.error('textElements not defined');
    return;
  }

  // Reset to default positions
  textElements.top.x = 0.5;
  textElements.top.y = 0.15;
  textElements.bottom.x = 0.5;
  textElements.bottom.y = 0.85;

  // Redraw
  drawMemeWithDraggableText();

  // Show success message
  if (typeof showToast === 'function') {
    showToast('🔄 รีเซ็ตตำแหน่งแล้ว', 'success');
  }
}

// Add keyboard shortcuts for position control
function initializePositionKeyboardShortcuts() {
  document.addEventListener('keydown', function(e) {
    if (e.ctrlKey || e.metaKey) {
      switch (e.key) {
        case 'ArrowLeft':
          e.preventDefault();
          document.querySelector('[data-text="top"][data-pos="left"]')?.click();
          break;
        case 'ArrowRight':
          e.preventDefault();
          document.querySelector('[data-text="top"][data-pos="right"]')?.click();
          break;
        case 'ArrowUp':
          e.preventDefault();
          document.querySelector('[data-text="top"][data-pos="center"]')?.click();
          break;
        case 'ArrowDown':
          e.preventDefault();
          document.querySelector('[data-text="bottom"][data-pos="center"]')?.click();
          break;
      }
    }
  });
}

// Select template
function enhancedSelectTemplate(template, element) {
  // Remove previous selections
  document.querySelectorAll('.template-item').forEach(item => {
    item.classList.remove('selected');
  });

  // Add selection with animation
  element.classList.add('selected');
  addSuccessAnimation(element);

  // Haptic feedback
  if (navigator.vibrate) {
    navigator.vibrate([50, 30, 50]);
  }

  selectedTemplate = template;
  loadTemplateImage(template);

  if (typeof showToast === 'function') {
    showToast(`✨ เลือก: ${template.name}`, 'success');
  }
}

// Load template image
function loadTemplateImage(template) {
  showLoadingOverlay('กำลังโหลดเทมเพลต...');

  const img = new Image();
  img.crossOrigin = 'anonymous';

  img.onload = function() {
    currentImage = img;
    canvas.width = template.width;
    canvas.height = template.height;
    updateMeme();
    updateCanvasSize();
    hideLoadingOverlay();

    // Success animation on canvas
    addSuccessAnimation(canvas);
  };

  img.onerror = function() {
    hideLoadingOverlay();
    console.error('Failed to load template:', template.src);

    if (typeof showToast === 'function') {
      showToast('❌ ไม่สามารถโหลดเทมเพลตได้', 'error');
    }
  };

  img.src = template.src;
}

// Enhanced Download Function
async function enhancedDownloadMeme() {
  const downloadBtn = document.getElementById('download-btn');
  const originalText = downloadBtn.innerHTML;

  try {
    // Show loading state
    downloadBtn.innerHTML = '<span class="loading"></span> กำลังสร้าง...';
    downloadBtn.disabled = true;

    showLoadingOverlay('กำลังสร้างมีมคุณภาพสูง...');

    // Wait a bit for UI update
    await new Promise(resolve => setTimeout(resolve, 500));

    // Create download link
    const link = document.createElement('a');
    const timestamp = new Date().toISOString().slice(0, 19).replace(/[:-]/g, '');
    link.download = `meme-${timestamp}.png`;
    link.href = canvas.toDataURL('image/png', 1.0);

    // Trigger download
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);

    hideLoadingOverlay();

    // Success feedback
    addSuccessAnimation(downloadBtn);
    showToast('🎉 ดาวน์โหลดสำเร็จ!', 'success');

  } catch (error) {
    hideLoadingOverlay();
    console.error('Download failed:', error);
    addErrorState(downloadBtn);
    showToast('❌ เกิดข้อผิดพลาดในการดาวน์โหลด', 'error');
  } finally {
    // Restore button
    setTimeout(() => {
      downloadBtn.innerHTML = originalText;
      downloadBtn.disabled = false;
    }, 1000);
  }
}

// Enhanced Theme Switching
function enhancedToggleTheme() {
  const body = document.body;
  const themeBtn = document.getElementById('theme-switcher');
  const themeIcon = themeBtn.querySelector('.theme-icon');
  const themeText = themeBtn.querySelector('.theme-text');

  const currentTheme = body.getAttribute('data-theme');
  const newTheme = currentTheme === 'light' ? 'dark' : 'light';

  // Animate theme transition
  body.style.transition = 'all 0.3s ease';
  body.setAttribute('data-theme', newTheme);

  // Update button content
  if (newTheme === 'light') {
    themeIcon.textContent = '☀️';
    themeText.textContent = 'Light';
  } else {
    themeIcon.textContent = '🌙';
    themeText.textContent = 'Dark';
  }

  // Success animation
  addSuccessAnimation(themeBtn);

  // Save preference
  localStorage.setItem('theme', newTheme);

  // Remove transition after animation
  setTimeout(() => {
    body.style.transition = '';
  }, 300);
}

// ============================================
// Modal System Functions
// ============================================

// Open modal with content
function openModal(title, content) {
  const modal = document.getElementById('modal');
  const modalTitle = document.getElementById('modal-title');
  const modalBody = document.getElementById('modal-body');

  if (!modal || !modalTitle || !modalBody) {
    console.error('Modal elements not found');
    return;
  }

  modalTitle.textContent = title;
  modalBody.innerHTML = content;

  // Show modal with animation
  modal.classList.remove('hidden');
  setTimeout(() => {
    modal.classList.add('show');
  }, 10);

  // Add escape key listener
  document.addEventListener('keydown', handleModalEscape);

  // Prevent body scroll
  document.body.style.overflow = 'hidden';

  // Focus management for accessibility
  const closeButton = modal.querySelector('.modal-close');
  if (closeButton) {
    closeButton.focus();
  }
}

// Close modal
function closeModal() {
  const modal = document.getElementById('modal');

  if (!modal) {
    console.error('Modal element not found');
    return;
  }

  // Hide modal with animation
  modal.classList.remove('show');

  setTimeout(() => {
    modal.classList.add('hidden');

    // Clear content
    const modalTitle = document.getElementById('modal-title');
    const modalBody = document.getElementById('modal-body');

    if (modalTitle) modalTitle.textContent = '';
    if (modalBody) modalBody.innerHTML = '';

  }, 300); // Match CSS transition duration

  // Remove escape key listener
  document.removeEventListener('keydown', handleModalEscape);

  // Restore body scroll
  document.body.style.overflow = '';

  // Return focus to trigger element (if available)
  if (window.lastFocusedElement) {
    window.lastFocusedElement.focus();
    window.lastFocusedElement = null;
  }
}

// Handle escape key press
function handleModalEscape(event) {
  if (event.key === 'Escape') {
    closeModal();
  }
}

// Show About modal
function showAbout() {
  // Store current focused element
  window.lastFocusedElement = document.activeElement;

  const aboutContent = `
    <div class="about-content">
      <div class="about-hero">
        <div class="about-icon">🎨</div>
        <h3>Meme Forge Pro</h3>
        <p class="about-tagline">"เปลี่ยนบั๊กเป็นเสียงฮา โค้ดปัญหาให้เป็นมีม!"</p>
      </div>

      <div class="about-section">
        <h4>🚀 เกี่ยวกับโปรเจกต์</h4>
        <p>Meme Forge Pro เป็นเว็บแอปพลิเคชันสำหรับสร้างมีมที่เน้นไปที่กลุ่มนักพัฒนาซอฟต์แวร์ นักวิทยาศาสตร์ข้อมูล และผู้ที่สนใจเทคโนโลยี AI</p>
      </div>

      <div class="about-section">
        <h4>✨ ฟีเจอร์หลัก</h4>
        <ul class="feature-list">
          <li>🎯 <strong>Drag & Drop Text:</strong> ลากข้อความไปตำแหน่งที่ต้องการ</li>
          <li>🖼️ <strong>22+ Templates:</strong> เทมเพลตมีมยอดนิยม</li>
          <li>📱 <strong>Responsive:</strong> ใช้งานได้ทุกอุปกรณ์</li>
          <li>🌙 <strong>Dark/Light Mode:</strong> เลือกธีมตามใจ</li>
          <li>⚡ <strong>Real-time Preview:</strong> เห็นผลทันที</li>
          <li>💾 <strong>High Quality Export:</strong> ดาวน์โหลด PNG คุณภาพสูง</li>
        </ul>
      </div>

      <div class="about-section">
        <h4>🛠️ เทคโนโลยีที่ใช้</h4>
        <div class="tech-stack">
          <span class="tech-item">HTML5</span>
          <span class="tech-item">CSS3</span>
          <span class="tech-item">JavaScript</span>
          <span class="tech-item">Canvas API</span>
          <span class="tech-item">PWA</span>
        </div>
      </div>

      <div class="about-section">
        <h4>👨‍💻 ผู้พัฒนา</h4>
        <div class="developer-info">
          <p><strong>Goragod Wiriya</strong></p>
          <div class="developer-links">
            <a href="https://github.com/goragodwiriya" target="_blank" rel="noopener">
              🐙 GitHub Profile
            </a>
            <a href="https://github.com/goragodwiriya/meme-forge-pro" target="_blank" rel="noopener">
              📂 Repository
            </a>
          </div>
        </div>
      </div>

      <div class="about-footer">
        <p>Built with ❤️ for the developer community</p>
        <p class="version">Version 2.0 - มิถุนายน 2568</p>
      </div>
    </div>
  `;

  openModal('ℹ️ เกี่ยวกับ Meme Forge Pro', aboutContent);
}

// Initialize modal system
function initializeModal() {
  const modal = document.getElementById('modal');

  if (!modal) {
    console.error('Modal element not found');
    return;
  }

  // Close modal when clicking overlay
  modal.addEventListener('click', (event) => {
    if (event.target === modal) {
      closeModal();
    }
  });

  // Prevent modal content clicks from closing modal
  const modalContent = modal.querySelector('.modal-content');
  if (modalContent) {
    modalContent.addEventListener('click', (event) => {
      event.stopPropagation();
    });
  }
}

// Clear meme function
function clearMeme() {
  if (!canvas || !ctx) return;

  // Clear canvas
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // Reset current image
  currentImage = null;

  // Clear text inputs
  const topTextInput = document.getElementById('top-text');
  const bottomTextInput = document.getElementById('bottom-text');

  if (topTextInput) topTextInput.value = '';
  if (bottomTextInput) bottomTextInput.value = '';

  // Reset text elements
  textElements.top.text = '';
  textElements.bottom.text = '';

  // Reset positions to default
  textElements.top.x = 0.5;
  textElements.top.y = 0.15;
  textElements.bottom.x = 0.5;
  textElements.bottom.y = 0.85;

  // Show success message
  if (typeof showToast === 'function') {
    showToast('🧹 ล้างข้อมูลเรียบร้อยแล้ว', 'success');
  }

  // Add success animation
  if (typeof addSuccessAnimation === 'function') {
    addSuccessAnimation(canvas);
  }
}

// Download meme function
function downloadMeme() {
  if (!canvas || !currentImage) {
    if (typeof showToast === 'function') {
      showToast('❌ กรุณาเลือกรูปภาพก่อนดาวน์โหลด', 'error');
    }
    return;
  }

  try {
    // Show loading
    if (typeof showLoadingOverlay === 'function') {
      showLoadingOverlay('กำลังสร้างมีม...', 'pulse');
    }

    // Create download link
    const link = document.createElement('a');
    const timestamp = new Date().toISOString().slice(0, 19).replace(/[:-]/g, '');
    link.download = `meme-${timestamp}.png`;
    link.href = canvas.toDataURL('image/png', 1.0);

    // Trigger download
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);

    // Hide loading and show success
    setTimeout(() => {
      if (typeof hideLoadingOverlay === 'function') {
        hideLoadingOverlay();
      }
      if (typeof showToast === 'function') {
        showToast('🎉 ดาวน์โหลดสำเร็จ!', 'success');
      }
    }, 500);

  } catch (error) {
    console.error('Download failed:', error);

    if (typeof hideLoadingOverlay === 'function') {
      hideLoadingOverlay();
    }
    if (typeof showToast === 'function') {
      showToast('❌ เกิดข้อผิดพลาดในการดาวน์โหลด', 'error');
    }
  }
}

// Enhanced Draggable Text System
// Initialize draggable text system
function initializeDraggableText() {
  try {
    // Check requirements
    if (!canvas) {
      console.error('Canvas not found');
      return false;
    }

    if (!textElements) {
      console.error('textElements not defined');
      return false;
    }

    if (!dragState) {
      console.error('dragState not defined');
      return false;
    }

    // Remove existing listeners to prevent duplicates
    canvas.removeEventListener('mousedown', handleTextMouseDown);
    canvas.removeEventListener('mousemove', handleTextMouseMove);
    canvas.removeEventListener('mouseup', handleTextMouseUp);
    canvas.removeEventListener('mouseleave', handleTextMouseUp);
    canvas.removeEventListener('touchstart', handleTextTouchStart);
    canvas.removeEventListener('touchmove', handleTextTouchMove);
    canvas.removeEventListener('touchend', handleTextTouchEnd);

    // Add new listeners
    canvas.addEventListener('mousedown', handleTextMouseDown);
    canvas.addEventListener('mousemove', handleTextMouseMove);
    canvas.addEventListener('mouseup', handleTextMouseUp);
    canvas.addEventListener('mouseleave', handleTextMouseUp);

    // Touch events with passive: false for preventDefault to work
    canvas.addEventListener('touchstart', handleTextTouchStart, {passive: false});
    canvas.addEventListener('touchmove', handleTextTouchMove, {passive: false});
    canvas.addEventListener('touchend', handleTextTouchEnd);

    // Update text when inputs change
    const topTextInput = document.getElementById('top-text');
    const bottomTextInput = document.getElementById('bottom-text');

    if (topTextInput) {
      topTextInput.removeEventListener('input', updateTextElements);
      topTextInput.addEventListener('input', updateTextElements);
    }

    if (bottomTextInput) {
      bottomTextInput.removeEventListener('input', updateTextElements);
      bottomTextInput.addEventListener('input', updateTextElements);
    }

    // Initialize reset button
    const resetBtn = document.getElementById('reset-positions-btn');
    if (resetBtn) {
      resetBtn.removeEventListener('click', resetTextPositions);
      resetBtn.addEventListener('click', resetTextPositions);
    }

    return true;

  } catch (error) {
    console.error('Error initializing draggable text:', error);
    return false;
  }
}

// Handle mouse down event for draggable text
function handleTextMouseDown(e) {
  const rect = canvas.getBoundingClientRect();
  const x = (e.clientX - rect.left) * (canvas.width / rect.width);
  const y = (e.clientY - rect.top) * (canvas.height / rect.height);

  const elementKey = getTextElementAt(x, y);

  if (elementKey) {
    dragState.isDragging = true;
    dragState.currentElement = textElements[elementKey];
    dragState.startX = x;
    dragState.startY = y;
    dragState.offsetX = x - (textElements[elementKey].x * canvas.width);
    dragState.offsetY = y - (textElements[elementKey].y * canvas.height);

    // Visual feedback
    canvas.style.cursor = 'grabbing';
    canvas.classList.add('dragging');

    e.preventDefault();
  }
}

// Handle mouse move event for draggable text
function handleTextMouseMove(e) {
  if (dragState.isDragging && dragState.currentElement) {
    const rect = canvas.getBoundingClientRect();
    const x = (e.clientX - rect.left) * (canvas.width / rect.width);
    const y = (e.clientY - rect.top) * (canvas.height / rect.height);

    // Update position
    dragState.currentElement.x = Math.max(0, Math.min(1, (x - dragState.offsetX) / canvas.width));
    dragState.currentElement.y = Math.max(0, Math.min(1, (y - dragState.offsetY) / canvas.height));

    // Redraw canvas
    drawMemeWithDraggableText();

    e.preventDefault();
  }
}

// Handle mouse up event for draggable text
function handleTextMouseUp(e) {
  if (dragState.isDragging) {
    // Reset drag state
    dragState.isDragging = false;
    dragState.currentElement = null;

    // Reset cursor and visual feedback
    canvas.style.cursor = 'default';
    canvas.classList.remove('dragging');

    // Redraw canvas to finalize position
    drawMemeWithDraggableText();

    e.preventDefault();
  }
}

// Handle touch start event for draggable text
function handleTextTouchStart(e) {
  const rect = canvas.getBoundingClientRect();
  const touch = e.touches[0];
  const x = (touch.clientX - rect.left) * (canvas.width / rect.width);
  const y = (touch.clientY - rect.top) * (canvas.height / rect.height);

  const elementKey = getTextElementAt(x, y);

  if (elementKey) {
    dragState.isDragging = true;
    dragState.currentElement = textElements[elementKey];
    dragState.startX = x;
    dragState.startY = y;
    dragState.offsetX = x - (textElements[elementKey].x * canvas.width);
    dragState.offsetY = y - (textElements[elementKey].y * canvas.height);

    // Visual feedback
    canvas.classList.add('dragging');

    e.preventDefault();
  }
}

// Handle touch move event for draggable text
function handleTextTouchMove(e) {
  if (dragState.isDragging && dragState.currentElement) {
    const rect = canvas.getBoundingClientRect();
    const touch = e.touches[0];
    const x = (touch.clientX - rect.left) * (canvas.width / rect.width);
    const y = (touch.clientY - rect.top) * (canvas.height / rect.height);

    // Update position
    dragState.currentElement.x = Math.max(0, Math.min(1, (x - dragState.offsetX) / canvas.width));
    dragState.currentElement.y = Math.max(0, Math.min(1, (y - dragState.offsetY) / canvas.height));

    // Redraw canvas
    drawMemeWithDraggableText();

    e.preventDefault();
  }
}

// Handle touch end event for draggable text
function handleTextTouchEnd(e) {
  if (dragState.isDragging) {
    // Reset drag state
    dragState.isDragging = false;
    dragState.currentElement = null;

    // Reset visual feedback
    canvas.classList.remove('dragging');

    // Redraw canvas to finalize position
    drawMemeWithDraggableText();

    e.preventDefault();
  }
}

// Update text elements from inputs
function updateTextElements() {
  textElements.top.text = document.getElementById('top-text').value;
  textElements.bottom.text = document.getElementById('bottom-text').value;
  drawMemeWithDraggableText();
}

// Core function to draw meme with draggable text
function drawMemeWithDraggableText() {
  if (!currentImage || !canvas || !ctx) return;

  // Clear canvas
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // Draw background image
  ctx.drawImage(currentImage, 0, 0, canvas.width, canvas.height);

  // Draw text elements
  Object.keys(textElements).forEach(key => {
    const element = textElements[key];
    if (element.text) {
      drawTextElement(element);
    }
  });
}

// Draw individual text element
function drawTextElement(element) {
  const x = element.x * canvas.width;
  const y = element.y * canvas.height;

  // Set text properties
  ctx.font = `${element.fontSize}px ${element.fontFamily}`;
  ctx.textAlign = element.align;
  ctx.textBaseline = 'middle';
  ctx.fillStyle = element.color;
  ctx.strokeStyle = element.strokeColor;
  ctx.lineWidth = element.strokeWidth;
  ctx.lineJoin = 'round';
  ctx.miterLimit = 2;

  // Text wrapping
  const words = element.text.split(' ');
  const maxWidth = canvas.width * 0.9;
  const lines = [];
  let currentLine = '';

  words.forEach(word => {
    const testLine = currentLine + (currentLine ? ' ' : '') + word;
    const metrics = ctx.measureText(testLine);

    if (metrics.width > maxWidth && currentLine) {
      lines.push(currentLine);
      currentLine = word;
    } else {
      currentLine = testLine;
    }
  });

  if (currentLine) {
    lines.push(currentLine);
  }

  // Draw lines
  const lineHeight = element.fontSize * 1.2;
  const totalHeight = lines.length * lineHeight;
  const startY = y - (totalHeight / 2) + (lineHeight / 2);

  lines.forEach((line, index) => {
    const lineY = startY + (index * lineHeight);

    // Draw stroke (outline)
    ctx.strokeText(line, x, lineY);
    // Draw fill
    ctx.fillText(line, x, lineY);
  });
}

// Update text elements from input fields
function updateTextElements() {
  const topText = document.getElementById('top-text');
  const bottomText = document.getElementById('bottom-text');

  if (topText) {
    textElements.top.text = topText.value;
  }

  if (bottomText) {
    textElements.bottom.text = bottomText.value;
  }

  drawMemeWithDraggableText();
}

// Enhanced meme drawing function
function drawMemeWithDraggableText() {
  if (!currentImage) return;

  // Clear canvas
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // Draw image
  ctx.drawImage(currentImage, 0, 0, canvas.width, canvas.height);

  // Draw all text elements
  Object.keys(textElements).forEach(key => {
    const element = textElements[key];
    if (element.text) {
      drawDraggableText(element);
    }
  });

  // Draw drag indicators if dragging
  if (dragState.isDragging && dragState.currentElement) {
    drawDragIndicator(dragState.currentElement);
  }
}

// Draw individual text element
function drawDraggableText(element) {
  const x = element.x * canvas.width;
  const y = element.y * canvas.height;

  // Set text properties
  ctx.font = `${element.fontSize}px ${element.fontFamily} `;
  ctx.textAlign = element.align;
  ctx.textBaseline = 'middle';
  ctx.fillStyle = element.color;
  ctx.strokeStyle = element.strokeColor;
  ctx.lineWidth = element.strokeWidth;
  ctx.lineJoin = 'round';
  ctx.miterLimit = 2;

  // Draw text with word wrapping
  const words = element.text.split(' ');
  const maxWidth = canvas.width * 0.9;
  const lines = [];
  let currentLine = '';

  words.forEach(word => {
    const testLine = currentLine + (currentLine ? ' ' : '') + word;
    const metrics = ctx.measureText(testLine);

    if (metrics.width > maxWidth && currentLine) {
      lines.push(currentLine);
      currentLine = word;
    } else {
      currentLine = testLine;
    }
  });

  if (currentLine) {
    lines.push(currentLine);
  }

  // Draw each line
  const lineHeight = element.fontSize * 1.2;
  const totalHeight = lines.length * lineHeight;
  const startY = y - (totalHeight / 2) + (lineHeight / 2);

  lines.forEach((line, index) => {
    const lineY = startY + (index * lineHeight);

    // Draw stroke (outline)
    ctx.strokeText(line, x, lineY);
    // Draw fill
    ctx.fillText(line, x, lineY);
  });
}

// Update canvas size display and styling
function updateCanvasSize() {
  if (!canvas) return;

  const container = canvas.parentElement;
  if (!container) return;

  // Update container styling
  container.style.maxWidth = canvas.width + 'px';
  container.style.margin = '0 auto';

  // Add responsive scaling
  const maxWidth = Math.min(window.innerWidth * 0.9, 800);
  if (canvas.width > maxWidth) {
    const scale = maxWidth / canvas.width;
    canvas.style.width = (canvas.width * scale) + 'px';
    canvas.style.height = (canvas.height * scale) + 'px';
  } else {
    canvas.style.width = canvas.width + 'px';
    canvas.style.height = canvas.height + 'px';
  }

  // Update canvas attributes for better display
  canvas.style.border = '2px solid var(--glass-border)';
  canvas.style.borderRadius = 'var(--radius-lg)';
  canvas.style.boxShadow = 'var(--shadow-lg)';
  canvas.style.background = '#f8f9fa';
}

// Enhanced text element detection
function getTextElementAt(x, y) {
  const relativeX = x / canvas.width;
  const relativeY = y / canvas.height;

  for (let key of Object.keys(textElements)) {
    const element = textElements[key];
    if (!element.text) continue;

    // Calculate actual text bounds more accurately
    ctx.font = `${element.fontSize}px ${element.fontFamily}`;
    const metrics = ctx.measureText(element.text);
    const textWidth = metrics.width / canvas.width;
    const textHeight = (element.fontSize * 1.5) / canvas.height;

    const textX = element.x;
    const textY = element.y;

    // Create generous hit area for easy selection
    const hitAreaX = Math.max(textWidth / 2, 0.15); // อย่างน้อย 15% ของ canvas
    const hitAreaY = Math.max(textHeight / 2, 0.08); // อย่างน้อย 8% ของ canvas

    if (Math.abs(relativeX - textX) < hitAreaX &&
      Math.abs(relativeY - textY) < hitAreaY) {
      return key;
    }
  }

  return null;
}

// Enhanced text positioning with better visual feedback
function drawEnhancedDraggableText(element) {
  const x = element.x * canvas.width;
  const y = element.y * canvas.height;

  // Set text properties
  ctx.font = `${element.fontSize}px ${element.fontFamily}`;
  ctx.textAlign = element.align;
  ctx.textBaseline = 'middle';
  ctx.fillStyle = element.color;
  ctx.strokeStyle = element.strokeColor;
  ctx.lineWidth = element.strokeWidth;
  ctx.lineJoin = 'round';
  ctx.miterLimit = 2;

  // Enhanced text rendering with better wrapping
  const text = element.text;
  if (!text) return;

  const words = text.split(' ');
  const maxWidth = canvas.width * 0.95; // ใช้พื้นที่เกือบเต็ม canvas
  const lines = [];
  let currentLine = '';

  words.forEach(word => {
    const testLine = currentLine + (currentLine ? ' ' : '') + word;
    const metrics = ctx.measureText(testLine);

    if (metrics.width > maxWidth && currentLine) {
      lines.push(currentLine);
      currentLine = word;
    } else {
      currentLine = testLine;
    }
  });

  if (currentLine) {
    lines.push(currentLine);
  }

  // Draw each line with enhanced positioning
  const lineHeight = element.fontSize * 1.2;
  const totalHeight = lines.length * lineHeight;
  const startY = y - (totalHeight / 2) + (lineHeight / 2);

  lines.forEach((line, index) => {
    const lineY = startY + (index * lineHeight);

    // Add drop shadow for better visibility
    ctx.save();
    ctx.shadowColor = 'rgba(0, 0, 0, 0.8)';
    ctx.shadowBlur = 4;
    ctx.shadowOffsetX = 2;
    ctx.shadowOffsetY = 2;

    // Draw stroke (outline)
    ctx.strokeText(line, x, lineY);

    ctx.restore();

    // Draw fill
    ctx.fillText(line, x, lineY);
  });

  // Show coordinates when dragging
  if (dragState.isDragging && dragState.currentElement === element) {
    ctx.save();
    ctx.font = '14px Arial';
    ctx.fillStyle = '#00d4ff';
    ctx.textAlign = 'left';
    ctx.textBaseline = 'top';

    const coordText = `X: ${Math.round(element.x * 100)}%, Y: ${Math.round(element.y * 100)}%`;
    const coordX = Math.min(x + 20, canvas.width - 120);
    const coordY = Math.max(y - 40, 20);

    // Background for coordinates
    ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
    ctx.fillRect(coordX - 5, coordY - 5, 110, 25);

    // Coordinate text
    ctx.fillStyle = '#00d4ff';
    ctx.fillText(coordText, coordX, coordY);

    ctx.restore();
  }
}

// Override the original drawDraggableText function
function drawDraggableText(element) {
  drawEnhancedDraggableText(element);
}

// Set default meme template with error handling
function setDefaultMeme() {
  try {
    // Wait for templates to be loaded from templates.js
    if (typeof memeTemplates === 'undefined' || !memeTemplates.length) {
      setTimeout(setDefaultMeme, 300);
      return;
    }

    // Check if canvas is ready
    if (!canvas || !ctx) {
      setTimeout(setDefaultMeme, 200);
      return;
    }

    // Set default template to first one (Distracted Boyfriend)
    const defaultTemplate = memeTemplates.find(t => t.name === "Distracted Boyfriend") || memeTemplates[0];

    if (defaultTemplate && currentImage) {
      // Already have a template loaded
      return;
    }

    if (defaultTemplate) {
      setTimeout(() => {
        loadTemplate(defaultTemplate);
      }, 100);
    } else {
      console.warn('No default template found');
    }
  } catch (error) {
    console.error('Error in setDefaultMeme:', error);
    // Retry after delay
    setTimeout(setDefaultMeme, 500);
  }
}

// Enhanced template loading with error handling
function loadTemplate(template) {
  try {
    if (!template || !template.src) {
      console.error('Invalid template:', template);
      if (typeof showToast === 'function') {
        showToast('❌ เทมเพลตไม่ถูกต้อง', 'error');
      }
      return;
    }

    // Show loading state
    if (typeof showLoadingOverlay === 'function') {
      showLoadingOverlay('กำลังโหลดเทมเพลต...', 'pulse');
    }

    const img = new Image();
    img.crossOrigin = 'anonymous';

    img.onload = function() {
      try {
        currentImage = img;
        canvas.width = template.width || img.naturalWidth;
        canvas.height = template.height || img.naturalHeight;

        // Update canvas size and position
        updateCanvasSize();

        // Reset text positions for new template
        textElements.top.x = 0.5;
        textElements.top.y = 0.15;
        textElements.bottom.x = 0.5;
        textElements.bottom.y = 0.85;

        // Update text and redraw
        updateTextElements();
        drawMemeWithDraggableText();

        // Hide loading
        if (typeof hideLoadingOverlay === 'function') {
          hideLoadingOverlay();
        }

        // Success feedback
        if (typeof showToast === 'function') {
          showToast('✨ โหลด ' + template.name + ' สำเร็จ!', 'success');
        }

        // Add success animation
        if (typeof addSuccessAnimation === 'function') {
          addSuccessAnimation(canvas);
        }
      } catch (error) {
        console.error('Error processing loaded template:', error);
        if (typeof hideLoadingOverlay === 'function') {
          hideLoadingOverlay();
        }
        if (typeof showToast === 'function') {
          showToast('❌ เกิดข้อผิดพลาดในการประมวลผลเทมเพลต', 'error');
        }
      }
    };

    img.onerror = function() {
      console.error('Failed to load template image:', template.src);

      if (typeof hideLoadingOverlay === 'function') {
        hideLoadingOverlay();
      }

      if (typeof showToast === 'function') {
        showToast('❌ ไม่สามารถโหลดรูปภาพเทมเพลตได้', 'error');
      }
    };

    img.src = template.src;

  } catch (error) {
    console.error('Error in loadTemplate:', error);

    if (typeof hideLoadingOverlay === 'function') {
      hideLoadingOverlay();
    }

    if (typeof showToast === 'function') {
      showToast('❌ เกิดข้อผิดพลาดในการโหลดเทมเพลต', 'error');
    }
  }
}