/** * Website Template Editor - Main Entry Point * จัดการแอปพลิเคชันแก้ไขเทมเพลตทั้งหมด โมดูล ปลั๊กอิน และบริการ */ (function(global) { 'use strict'; /** * คลาส Editor หลัก * รับผิดชอบการเริ่มต้นและโหลดโมดูล ปลั๊กอิน และบริการทั้งหมด */ class Editor { constructor(config = {}) { this.config = Object.assign({ container: '#editor-container', autoSave: true, debug: false, theme: 'default' }, config); // คุณสมบัติหลัก this.container = null; this.modules = {}; this.plugins = {}; this.services = {}; this.state = 'init'; // สถานะ: init, ready, edit, preview // ระบบเหตุการณ์ this.eventListeners = {}; // วงจรชีวิตหลัก this.lifecycle = { init: false, ready: false, edit: false, preview: false }; // เริ่มต้นคอมโพเนนต์หลัก this.initCore(); } /** * เริ่มต้นคอมโพเนนต์หลัก */ initCore() { // ค้นหาอิลิเมนต์คอนเทนเนอร์ this.container = document.querySelector(this.config.container); if (!this.container) { throw new Error(`ไม่พบคอนเทนเนอร์ตัวแก้ไข: ${this.config.container}`); } // เริ่มต้นบริการหลัก this.initEventBus(); this.initModuleManager(); this.initUIManager(); this.initDOMUtils(); } /** * เริ่มต้นตัวแก้ไข */ init() { if (this.lifecycle.init) { console.warn('ตัวแก้ไขถูกเริ่มต้นแล้ว'); return; } // โหลดบริการหลัก this.loadCoreServices(); // กู้คืนเทมเพลตที่บันทึกไว้ถ้ามี this.restoreSavedTemplate(); // โหลดโมดูล this.loadModules(); // โหลดปลั๊กอิน this.loadPlugins(); // ตั้งค่า event listeners this.setupEventListeners(); // สร้าง UI หลัก this.createMainUI(); // ทำเครื่องหมายว่าเริ่มต้นแล้ว this.lifecycle.init = true; this.state = 'ready'; this.lifecycle.ready = true; // ส่งเหตุการณ์พร้อม this.emit('editor:ready'); if (this.config.debug) { console.log('ตัวแก้ไขเริ่มต้นสำเร็จ'); } } /** * เริ่มต้น event bus */ initEventBus() { if (typeof EventBus !== 'undefined') { this.eventBus = new EventBus(); } else { // การใช้งาน event bus แบบง่าย this.eventBus = { events: {}, on: function(event, callback) { if (!this.events[event]) { this.events[event] = []; } this.events[event].push(callback); }, emit: function(event, data) { if (this.events[event]) { this.events[event].forEach(callback => callback(data)); } }, off: function(event, callback) { if (this.events[event]) { const index = this.events[event].indexOf(callback); if (index > -1) { this.events[event].splice(index, 1); } } } }; } } /** * เริ่มต้น module manager */ initModuleManager() { if (typeof ModuleManager !== 'undefined') { this.moduleManager = new ModuleManager(this); } else { // การใช้งาน module manager แบบง่าย this.moduleManager = { editor: this, modules: {}, register: function(name, module) { this.modules[name] = module; }, get: function(name) { return this.modules[name]; }, unload: function(name) { if (this.modules[name]) { if (typeof this.modules[name].destroy === 'function') { this.modules[name].destroy(); } delete this.modules[name]; } } }; } } /** * เริ่มต้น UI manager */ initUIManager() { if (typeof UIManager !== 'undefined') { this.uiManager = new UIManager(this); } else { // การใช้งาน UI manager แบบง่าย this.uiManager = { editor: this, panels: {}, toolbars: {}, registerPanel: function(name, element) { this.panels[name] = element; }, getPanel: function(name) { return this.panels[name]; }, registerToolbar: function(name, element) { this.toolbars[name] = element; }, getToolbar: function(name) { return this.toolbars[name]; }, showPanel: function(name) { if (this.panels[name]) { this.panels[name].classList.add('editor-panel-open'); } }, hidePanel: function(name) { if (this.panels[name]) { this.panels[name].classList.remove('editor-panel-open'); } } }; } } /** * เริ่มต้น DOM utils */ initDOMUtils() { if (typeof DOMUtils !== 'undefined') { this.domUtils = new DOMUtils(this); } else { // การใช้งาน DOM utils แบบง่าย this.domUtils = { editor: this, createElement: function(tag, attributes, content) { const element = document.createElement(tag); if (attributes) { Object.keys(attributes).forEach(key => { element.setAttribute(key, attributes[key]); }); } if (content) { element.innerHTML = content; } return element; }, findParent: function(element, selector) { let parent = element.parentElement; while (parent) { if (parent.matches(selector)) { return parent; } parent = parent.parentElement; } return null; }, sanitizeHTML: function(html) { const temp = document.createElement('div'); temp.textContent = html; return temp.innerHTML; }, getComputedStyle: function(element, property) { return window.getComputedStyle(element).getPropertyValue(property); } }; } } /** * โหลดบริการหลัก */ loadCoreServices() { // Template Loader if (typeof TemplateLoader !== 'undefined') { this.services.templateLoader = new TemplateLoader(this); } // Template Saver if (typeof TemplateSaver !== 'undefined') { this.services.templateSaver = new TemplateSaver(this); } // Storage Service if (typeof StorageService !== 'undefined') { this.services.storageService = new StorageService(this); } } /** * โหลดโมดูล */ loadModules() { // Content Editor if (typeof ContentEditor !== 'undefined') { this.loadModule('contentEditor', ContentEditor); } // Drag & Drop if (typeof DragDrop !== 'undefined') { this.loadModule('dragDrop', DragDrop); } // Property Editor if (typeof PropertyEditor !== 'undefined') { this.loadModule('propertyEditor', PropertyEditor); } // Layout Manager if (typeof LayoutManager !== 'undefined') { this.loadModule('layoutManager', LayoutManager); } // Component Panel if (typeof ComponentPanel !== 'undefined') { this.loadModule('componentPanel', ComponentPanel); } // Toolbar if (typeof Toolbar !== 'undefined') { this.loadModule('toolbar', Toolbar); } } /** * โหลดปลั๊กอิน */ loadPlugins() { // Header Plugin if (typeof HeaderPlugin !== 'undefined') { this.loadPlugin('header', HeaderPlugin); } // Footer Plugin if (typeof FooterPlugin !== 'undefined') { this.loadPlugin('footer', FooterPlugin); } // Hero Plugin if (typeof HeroPlugin !== 'undefined') { this.loadPlugin('hero', HeroPlugin); } // Carousel Plugin if (typeof CarouselPlugin !== 'undefined') { this.loadPlugin('carousel', CarouselPlugin); } // Card Plugin if (typeof CardPlugin !== 'undefined') { this.loadPlugin('card', CardPlugin); } // Grid Plugin if (typeof GridPlugin !== 'undefined') { this.loadPlugin('grid', GridPlugin); } } /** * โหลดโมดูล */ loadModule(name, moduleClass) { try { const module = new moduleClass(this); this.modules[name] = module; this.moduleManager.register(name, module); if (typeof module.init === 'function') { module.init(); } this.emit('module:loaded', {name}); if (this.config.debug) { console.log(`โมดูลถูกโหลด: ${name}`); } } catch (error) { console.error(`ไม่สามารถโหลดโมดูล: ${name}`, error); } } /** * โหลดปลั๊กอิน */ loadPlugin(name, pluginClass) { try { const plugin = new pluginClass(this); this.plugins[name] = plugin; if (typeof plugin.init === 'function') { plugin.init(); } this.emit('plugin:loaded', {name}); if (this.config.debug) { console.log(`ปลั๊กอินถูกโหลด: ${name}`); } } catch (error) { console.error(`ไม่สามารถโหลดปลั๊กอิน: ${name}`, error); } } /** * สร้าง UI หลัก */ createMainUI() { // สร้างแถบเครื่องมือหลัก this.createMainToolbar(); // สร้างแผงคุณสมบัติ this.createPropertyPanel(); // สร้างแผงคอมโพเนนต์ this.createComponentPanel(); // สร้างแผงจัดการเลย์เอาต์ this.createLayoutPanel(); } /** * สร้างแถบเครื่องมือหลัก */ createMainToolbar() { const toolbar = this.domUtils.createElement('div', { 'class': 'editor-main-toolbar', 'id': 'editor-main-toolbar' }); // สร้างกลุ่มซ้าย const leftGroup = this.domUtils.createElement('div', { 'class': 'toolbar-group toolbar-group-left' }); // ปุ่มแก้ไข const editBtn = this.domUtils.createElement('button', { 'class': 'toolbar-btn', 'id': 'toolbar-edit-btn' }, 'edit แก้ไข'); // ปุ่มดูตัวอย่าง const previewBtn = this.domUtils.createElement('button', { 'class': 'toolbar-btn', 'id': 'toolbar-preview-btn' }, 'visibility ดูตัวอย่าง'); leftGroup.appendChild(editBtn); leftGroup.appendChild(previewBtn); // สร้างกลุ่มกลาง const centerGroup = this.domUtils.createElement('div', { 'class': 'toolbar-group toolbar-group-center' }); // ปุ่มเลิกทำ const undoBtn = this.domUtils.createElement('button', { 'class': 'toolbar-btn', 'id': 'toolbar-undo-btn' }, 'undo'); // ปุ่มทำซ้ำ const redoBtn = this.domUtils.createElement('button', { 'class': 'toolbar-btn', 'id': 'toolbar-redo-btn' }, 'redo'); centerGroup.appendChild(undoBtn); centerGroup.appendChild(redoBtn); // สร้างกลุ่มขวา const rightGroup = this.domUtils.createElement('div', { 'class': 'toolbar-group toolbar-group-right' }); // ปุ่มบันทึก const saveBtn = this.domUtils.createElement('button', { 'class': 'toolbar-btn', 'id': 'toolbar-save-btn' }, 'save บันทึก'); // ปุ่มส่งออก const exportBtn = this.domUtils.createElement('button', { 'class': 'toolbar-btn', 'id': 'toolbar-export-btn' }, 'download ส่งออก'); rightGroup.appendChild(saveBtn); rightGroup.appendChild(exportBtn); // เพิ่มกลุ่มทั้งหมดในแถบเครื่องมือ toolbar.appendChild(leftGroup); toolbar.appendChild(centerGroup); toolbar.appendChild(rightGroup); // เพิ่มแถบเครื่องมือใน body document.body.appendChild(toolbar); // ลงทะเบียนแถบเครื่องมือ this.uiManager.registerToolbar('main', toolbar); // ตั้งค่า event listeners สำหรับปุ่มในแถบเครื่องมือหลังจากสร้าง DOM แล้ว this.setupToolbarEventListeners(); } /** * สร้างแผงคุณสมบัติ */ createPropertyPanel() { const panel = this.domUtils.createElement('div', { 'class': 'editor-property-panel', 'id': 'editor-property-panel' }); const header = this.domUtils.createElement('h3', {}, 'คุณสมบัติ'); panel.appendChild(header); // สร้างเนื้อหาแผง const content = this.domUtils.createElement('div', { 'class': 'property-panel-content' }); // สร้างกลุ่มคุณสมบัติ const textGroup = this.createPropertyGroup('text', 'ข้อความ', [ {type: 'text', label: 'เนื้อหา', id: 'text-content'}, { type: 'select', label: 'แบบอักษร', id: 'font-family', options: [ {value: 'Arial', text: 'Arial'}, {value: 'Times New Roman', text: 'Times New Roman'}, {value: 'Courier New', text: 'Courier New'} ] }, {type: 'number', label: 'ขนาด', id: 'font-size', min: 8, max: 72}, {type: 'color', label: 'สี', id: 'color'} ]); const styleGroup = this.createPropertyGroup('style', 'สไตล์', [ {type: 'color', label: 'สีพื้นหลัง', id: 'background-color'}, {type: 'text', label: 'รูปภาพพื้นหลัง', id: 'background-image'}, {type: 'number', label: 'ช่องว่างภายใน', id: 'padding', min: 0, max: 100}, {type: 'number', label: 'ขอบ', id: 'margin', min: 0, max: 100} ]); const layoutGroup = this.createPropertyGroup('layout', 'เลย์เอาต์', [ { type: 'select', label: 'การแสดงผล', id: 'display', options: [ {value: 'block', text: 'Block'}, {value: 'inline', text: 'Inline'}, {value: 'flex', text: 'Flex'}, {value: 'grid', text: 'Grid'} ] }, { type: 'select', label: 'ตำแหน่ง', id: 'position', options: [ {value: 'static', text: 'Static'}, {value: 'relative', text: 'Relative'}, {value: 'absolute', text: 'Absolute'}, {value: 'fixed', text: 'Fixed'} ] } ]); content.appendChild(textGroup); content.appendChild(styleGroup); content.appendChild(layoutGroup); panel.appendChild(content); // เพิ่มแผงใน body document.body.appendChild(panel); // ลงทะเบียนแผง this.uiManager.registerPanel('property', panel); } /** * สร้างกลุ่มคุณสมบัติ */ createPropertyGroup(id, title, properties) { const group = this.domUtils.createElement('div', { 'class': 'editor-property-group', 'id': `property-group-${id}` }); const groupTitle = this.domUtils.createElement('h4', {}, title); group.appendChild(groupTitle); properties.forEach(prop => { const propContainer = this.domUtils.createElement('div', { 'class': 'property-item' }); const label = this.domUtils.createElement('label', { 'for': prop.id }, prop.label); let input; switch (prop.type) { case 'text': input = this.domUtils.createElement('input', { 'type': 'text', 'id': prop.id, 'class': 'property-input' }); break; case 'number': input = this.domUtils.createElement('input', { 'type': 'number', 'id': prop.id, 'class': 'property-input', 'min': prop.min, 'max': prop.max }); break; case 'color': input = this.domUtils.createElement('input', { 'type': 'color', 'id': prop.id, 'class': 'property-input' }); break; case 'select': input = this.domUtils.createElement('select', { 'id': prop.id, 'class': 'property-input' }); prop.options.forEach(option => { const optionElement = this.domUtils.createElement('option', { 'value': option.value }, option.text); input.appendChild(optionElement); }); break; } propContainer.appendChild(label); propContainer.appendChild(input); group.appendChild(propContainer); }); return group; } /** * สร้างแผงคอมโพเนนต์ */ createComponentPanel() { const panel = this.domUtils.createElement('div', { 'class': 'editor-component-panel', 'id': 'editor-component-panel' }); const header = this.domUtils.createElement('h3', {}, 'คอมโพเนนต์'); panel.appendChild(header); // สร้างเนื้อหาแผง const content = this.domUtils.createElement('div', { 'class': 'component-panel-content' }); // สร้างรายการคอมโพเนนต์ const components = [ {id: 'header', name: 'ส่วนหัว', icon: 'header', description: 'ส่วนหัวของเว็บไซต์'}, {id: 'footer', name: 'ส่วนท้าย', icon: 'footer', description: 'ส่วนท้ายของเว็บไซต์'}, {id: 'hero', name: 'Hero Section', icon: 'view_carousel', description: 'ส่วนแสดงผลหลัก'}, {id: 'carousel', name: 'Carousel', icon: 'slideshow', description: 'สไลด์โชว์รูปภาพ'}, {id: 'card', name: 'การ์ด', icon: 'style', description: 'การ์ดเนื้อหา'}, {id: 'grid', name: 'กริด', icon: 'grid_on', description: 'เลย์เอาต์กริด'} ]; components.forEach(component => { const item = this.domUtils.createElement('div', { 'class': 'editor-component-item', 'data-component': component.id, 'draggable': 'true' }); const icon = this.domUtils.createElement('i', { 'class': 'material-icons' }, component.icon); const name = this.domUtils.createElement('h4', {}, component.name); const description = this.domUtils.createElement('p', {}, component.description); item.appendChild(icon); item.appendChild(name); item.appendChild(description); content.appendChild(item); }); panel.appendChild(content); // เพิ่มแผงใน body document.body.appendChild(panel); // ลงทะเบียนแผง this.uiManager.registerPanel('component', panel); } /** * สร้างแผงจัดการเลย์เอาต์ */ createLayoutPanel() { const panel = this.domUtils.createElement('div', { 'class': 'editor-layout-panel', 'id': 'editor-layout-panel' }); const header = this.domUtils.createElement('h3', {}, 'โครงสร้างเลย์เอาต์'); panel.appendChild(header); // สร้างเนื้อหาแผง const content = this.domUtils.createElement('div', { 'class': 'layout-panel-content' }); // สร้างต้นไม้ DOM const tree = this.domUtils.createElement('div', { 'class': 'editor-layout-tree', 'id': 'editor-layout-tree' }); content.appendChild(tree); panel.appendChild(content); // เพิ่มแผงใน body document.body.appendChild(panel); // ลงทะเบียนแผง this.uiManager.registerPanel('layout', panel); } /** * ตั้งค่า event listeners */ setupEventListeners() { // ฟังเหตุการณ์การสลับโหมดแก้ไข this.on('toolbar:edit', () => { this.enterEditMode(); }); // ฟังเหตุการณ์การสลับโหมดดูตัวอย่าง this.on('toolbar:preview', () => { this.enterPreviewMode(); }); // ฟังเหตุการณ์การบันทึก this.on('toolbar:save', () => { this.saveTemplate(); }); // ฟังเหตุการณ์การส่งออก this.on('toolbar:export', () => { this.exportTemplate(); }); // ฟังเหตุการณ์การเลือกอิลิเมนต์ this.on('element:selected', (data) => { this.selectElement(data.element); }); // ฟังเหตุการณ์การเปลี่ยนแปลงคุณสมบัติ this.on('property:changed', (data) => { this.updateElementProperty(data.property, data.value); }); } /** * ตั้งค่า event listeners สำหรับแถบเครื่องมือ */ setupToolbarEventListeners() { // ปุ่มแก้ไข const editBtn = document.getElementById('toolbar-edit-btn'); if (editBtn) { editBtn.addEventListener('click', () => { this.emit('toolbar:edit'); }); } // ปุ่มดูตัวอย่าง const previewBtn = document.getElementById('toolbar-preview-btn'); if (previewBtn) { previewBtn.addEventListener('click', () => { this.emit('toolbar:preview'); }); } // ปุ่มเลิกทำ const undoBtn = document.getElementById('toolbar-undo-btn'); if (undoBtn) { undoBtn.addEventListener('click', () => { this.emit('toolbar:undo'); }); } // ปุ่มทำซ้ำ const redoBtn = document.getElementById('toolbar-redo-btn'); if (redoBtn) { redoBtn.addEventListener('click', () => { this.emit('toolbar:redo'); }); } // ปุ่มบันทึก const saveBtn = document.getElementById('toolbar-save-btn'); if (saveBtn) { saveBtn.addEventListener('click', () => { this.emit('toolbar:save'); }); } // ปุ่มส่งออก const exportBtn = document.getElementById('toolbar-export-btn'); if (exportBtn) { exportBtn.addEventListener('click', () => { this.emit('toolbar:export'); }); } } /** * เข้าสู่โหมดแก้ไข */ enterEditMode() { if (this.state === 'edit') return; this.state = 'edit'; this.lifecycle.edit = true; this.lifecycle.preview = false; // เพิ่มคลาสโหมดแก้ไขในคอนเทนเนอร์ this.container.classList.add('editor-edit-mode'); this.container.classList.remove('editor-preview-mode'); // แสดง overlay ตัวแก้ไข this.showEditorOverlays(); // แสดงแผงคอมโพเนนต์ this.uiManager.showPanel('component'); // ส่งเหตุการณ์ this.emit('editor:mode-changed', {mode: 'edit'}); } /** * เข้าสู่โหมดดูตัวอย่าง */ enterPreviewMode() { if (this.state === 'preview') return; this.state = 'preview'; this.lifecycle.edit = false; this.lifecycle.preview = true; // เพิ่มคลาสโหมดดูตัวอย่างในคอนเทนเนอร์ this.container.classList.add('editor-preview-mode'); this.container.classList.remove('editor-edit-mode'); // ซ่อน overlay ตัวแก้ไข this.hideEditorOverlays(); // ซ่อนแผงทั้งหมด this.uiManager.hidePanel('component'); this.uiManager.hidePanel('property'); this.uiManager.hidePanel('layout'); // ส่งเหตุการณ์ this.emit('editor:mode-changed', {mode: 'preview'}); } /** * แสดง overlay ตัวแก้ไข */ showEditorOverlays() { // เพิ่มตัวบ่งชี้ overlay ในอิลิเมนต์ที่แก้ไขได้ const editableElements = this.container.querySelectorAll('[data-editable="true"]'); editableElements.forEach(element => { element.classList.add('editor-editable'); }); } /** * ซ่อน overlay ตัวแก้ไข */ hideEditorOverlays() { // ลบตัวบ่งชี้ overlay const editableElements = this.container.querySelectorAll('.editor-editable'); editableElements.forEach(element => { element.classList.remove('editor-editable'); }); } /** * เลือกอิลิเมนต์ */ selectElement(element) { // ลบการเลือกก่อนหน้า const previousSelected = this.container.querySelectorAll('.editor-selected'); previousSelected.forEach(el => { el.classList.remove('editor-selected'); }); // เลือกอิลิเมนต์ใหม่ element.classList.add('editor-selected'); // แสดงแผงคุณสมบัติ this.uiManager.showPanel('property'); // อัปเดตคุณสมบัติในแผง this.updatePropertyPanel(element); // อัปเดตต้นไม้เลย์เอาต์ this.updateLayoutTree(element); // ส่งเหตุการณ์ this.emit('element:selected', {element}); } /** * อัปเดตแผงคุณสมบัติ */ updatePropertyPanel(element) { // รับค่าคุณสมบัติปัจจุบัน const computedStyle = window.getComputedStyle(element); // อัปเดตค่าในแผงคุณสมบัติ const textContent = document.getElementById('text-content'); if (textContent) { textContent.value = element.textContent; } const fontFamily = document.getElementById('font-family'); if (fontFamily) { fontFamily.value = computedStyle.fontFamily; } const fontSize = document.getElementById('font-size'); if (fontSize) { fontSize.value = parseInt(computedStyle.fontSize); } const color = document.getElementById('color'); if (color) { color.value = this.rgbToHex(computedStyle.color); } const backgroundColor = document.getElementById('background-color'); if (backgroundColor) { backgroundColor.value = this.rgbToHex(computedStyle.backgroundColor); } const backgroundImage = document.getElementById('background-image'); if (backgroundImage) { backgroundImage.value = computedStyle.backgroundImage; } const padding = document.getElementById('padding'); if (padding) { padding.value = parseInt(computedStyle.padding); } const margin = document.getElementById('margin'); if (margin) { margin.value = parseInt(computedStyle.margin); } const display = document.getElementById('display'); if (display) { display.value = computedStyle.display; } const position = document.getElementById('position'); if (position) { position.value = computedStyle.position; } } /** * อัปเดตต้นไม้เลย์เอาต์ */ updateLayoutTree(element) { const tree = document.getElementById('editor-layout-tree'); if (!tree) return; // ล้างต้นไม้เก่า tree.innerHTML = ''; // สร้างต้นไม้ใหม่ this.buildLayoutTree(this.container, tree, 0); // เลือกอิลิเมนต์ในต้นไม้ const treeElement = tree.querySelector(`[data-element-id="${element.getAttribute('data-element-id') || ''}"]`); if (treeElement) { treeElement.classList.add('tree-selected'); } } /** * สร้างต้นไม้เลย์เอาต์ */ buildLayoutTree(element, parent, level) { // สร้าง ID สำหรับอิลิเมนต์ if (!element.getAttribute('data-element-id')) { element.setAttribute('data-element-id', 'element-' + Math.random().toString(36).substr(2, 9)); } const elementId = element.getAttribute('data-element-id'); // สร้างรายการต้นไม้ const item = this.domUtils.createElement('div', { 'class': 'editor-layout-tree-item', 'data-element-id': elementId, 'style': `padding-left: ${level * 20}px` }); // สร้างชื่อแท็ก const tagName = element.tagName.toLowerCase(); const className = element.className ? '.' + element.className.split(' ').join('.') : ''; const name = this.domUtils.createElement('span', { 'class': 'tree-element-name' }, `${tagName}${className}`); // สร้างไอคอน const icon = this.domUtils.createElement('i', { 'class': 'material-icons tree-icon' }, this.getElementIcon(element)); // สร้างปุ่มดำเนินการ const actions = this.domUtils.createElement('div', { 'class': 'tree-actions' }); const selectBtn = this.domUtils.createElement('button', { 'class': 'tree-action-btn', 'title': 'เลือก' }, 'edit'); const visibilityBtn = this.domUtils.createElement('button', { 'class': 'tree-action-btn', 'title': element.style.display === 'none' ? 'แสดง' : 'ซ่อน' }, `${element.style.display === 'none' ? 'visibility_off' : 'visibility'}`); actions.appendChild(selectBtn); actions.appendChild(visibilityBtn); // เพิ่มอิลิเมนต์ในรายการ item.appendChild(icon); item.appendChild(name); item.appendChild(actions); // เพิ่มรายการในพาเรนต์ parent.appendChild(item); // เพิ่ม event listeners selectBtn.addEventListener('click', () => { this.selectElement(element); }); visibilityBtn.addEventListener('click', () => { if (element.style.display === 'none') { element.style.display = ''; visibilityBtn.innerHTML = 'visibility'; } else { element.style.display = 'none'; visibilityBtn.innerHTML = 'visibility_off'; } }); // สร้างรายการย่อยสำหรับอิลิเมนต์ลูก const children = Array.from(element.children); if (children.length > 0) { const childrenContainer = this.domUtils.createElement('div', { 'class': 'tree-children' }); children.forEach(child => { this.buildLayoutTree(child, childrenContainer, level + 1); }); parent.appendChild(childrenContainer); } } /** * รับไอคอนสำหรับอิลิเมนต์ */ getElementIcon(element) { const tagName = element.tagName.toLowerCase(); switch (tagName) { case 'header': return 'header'; case 'footer': return 'footer'; case 'nav': return 'menu'; case 'section': return 'view_module'; case 'article': return 'article'; case 'div': return 'crop_square'; case 'img': return 'image'; case 'p': return 'text_fields'; case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6': return 'title'; case 'ul': case 'ol': return 'list'; case 'li': return 'label'; case 'a': return 'link'; case 'button': return 'touch_app'; case 'form': return 'input'; case 'input': case 'textarea': case 'select': return 'text_format'; default: return 'code'; } } /** * อัปเดตคุณสมบัติอิลิเมนต์ */ updateElementProperty(property, value) { const selectedElement = this.container.querySelector('.editor-selected'); if (!selectedElement) return; switch (property) { case 'text-content': selectedElement.textContent = value; break; case 'font-family': selectedElement.style.fontFamily = value; break; case 'font-size': selectedElement.style.fontSize = value + 'px'; break; case 'color': selectedElement.style.color = value; break; case 'background-color': selectedElement.style.backgroundColor = value; break; case 'background-image': if (value) { selectedElement.style.backgroundImage = `url(${value})`; } else { selectedElement.style.backgroundImage = ''; } break; case 'padding': selectedElement.style.padding = value + 'px'; break; case 'margin': selectedElement.style.margin = value + 'px'; break; case 'display': selectedElement.style.display = value; break; case 'position': selectedElement.style.position = value; break; } // ส่งเหตุการณ์ this.emit('element:property-updated', { element: selectedElement, property, value }); } /** * แปลงค่า RGB เป็น Hex */ rgbToHex(rgb) { if (!rgb || rgb === 'transparent' || rgb === 'rgba(0, 0, 0, 0)') { return '#ffffff'; } const result = rgb.match(/\d+/g); if (!result || result.length < 3) { return '#ffffff'; } const r = parseInt(result[0]); const g = parseInt(result[1]); const b = parseInt(result[2]); return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); } /** * บันทึกเทมเพลต */ saveTemplate() { if (this.services.templateSaver) { this.services.templateSaver.save(); } else { // การใช้งานสำรอง const html = this.getCleanHTML(); if (this.services.storageService) { this.services.storageService.save('template', html); } this.emit('template:saved', {html}); } } /** * กู้คืนเทมเพลตที่บันทึกไว้จาก storage */ restoreSavedTemplate() { if (!this.container) { return; } let savedTemplate = null; if (this.services.storageService && typeof this.services.storageService.load === 'function') { savedTemplate = this.services.storageService.load('template'); } if (!savedTemplate && typeof localStorage !== 'undefined') { const fallback = localStorage.getItem('editor-template'); if (fallback) { try { const parsedFallback = JSON.parse(fallback); savedTemplate = parsedFallback && typeof parsedFallback === 'object' ? parsedFallback : {html: parsedFallback}; } catch (error) { savedTemplate = {html: fallback}; } } } if (!savedTemplate) { return; } const html = typeof savedTemplate === 'string' ? savedTemplate : savedTemplate.html; if (html) { this.container.innerHTML = html; } if (savedTemplate.css) { let styleElement = document.querySelector('style[data-editor-restored]'); if (!styleElement) { styleElement = document.createElement('style'); styleElement.setAttribute('data-editor-restored', 'true'); document.head.appendChild(styleElement); } styleElement.textContent = savedTemplate.css; } if (savedTemplate.metadata && savedTemplate.metadata.title) { document.title = savedTemplate.metadata.title; } this.emit('template:restored', {template: savedTemplate}); } /** * ส่งออกเทมเพลต */ exportTemplate() { const html = this.getCleanHTML(); // สร้างลิงก์ดาวน์โหลด const blob = new Blob([html], {type: 'text/html'}); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'template.html'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); this.emit('template:exported', {html}); } /** * รับ HTML ที่สะอาดโดยไม่มีมาร์กอัปตัวแก้ไข */ getCleanHTML() { // โคลนคอนเทนเนอร์ const clone = this.container.cloneNode(true); // ลบคลาสและแอตทริบิวต์ตัวแก้ไข const editableElements = clone.querySelectorAll('.editor-editable, .editor-selected'); editableElements.forEach(element => { element.classList.remove('editor-editable', 'editor-selected'); element.removeAttribute('contenteditable'); element.removeAttribute('data-editable'); element.removeAttribute('data-element-id'); }); // ลบอิลิเมนต์ตัวแก้ไข const editorElements = clone.querySelectorAll('.editor-overlay, .editor-panel, .editor-toolbar'); editorElements.forEach(element => { element.remove(); }); return clone.innerHTML; } /** * ส่งเหตุการณ์ */ emit(event, data = {}) { if (this.eventBus) { this.eventBus.emit(event, data); } // เรียก event listeners ในเครื่องด้วย if (this.eventListeners[event]) { this.eventListeners[event].forEach(callback => callback(data)); } } /** * ฟังเหตุการณ์ */ on(event, callback) { if (this.eventBus) { this.eventBus.on(event, callback); } // เก็บใน event listeners ในเครื่องด้วย if (!this.eventListeners[event]) { this.eventListeners[event] = []; } this.eventListeners[event].push(callback); } /** * ลบ event listener */ off(event, callback) { if (this.eventBus && typeof this.eventBus.off === 'function') { this.eventBus.off(event, callback); } // ลบจาก event listeners ในเครื่องด้วย if (this.eventListeners[event]) { const index = this.eventListeners[event].indexOf(callback); if (index > -1) { this.eventListeners[event].splice(index, 1); } } } } // เปิดเผยคลาส Editor ทั่วโลก global.Editor = Editor; })(typeof window !== 'undefined' ? window : this);