editor.js

41.89 KB
12/10/2025 05:15
JS
editor.js
/**
 * 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'
      }, '<i class="material-icons">edit</i> แก้ไข');

      // ปุ่มดูตัวอย่าง
      const previewBtn = this.domUtils.createElement('button', {
        'class': 'toolbar-btn',
        'id': 'toolbar-preview-btn'
      }, '<i class="material-icons">visibility</i> ดูตัวอย่าง');

      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'
      }, '<i class="material-icons">undo</i>');

      // ปุ่มทำซ้ำ
      const redoBtn = this.domUtils.createElement('button', {
        'class': 'toolbar-btn',
        'id': 'toolbar-redo-btn'
      }, '<i class="material-icons">redo</i>');

      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'
      }, '<i class="material-icons">save</i> บันทึก');

      // ปุ่มส่งออก
      const exportBtn = this.domUtils.createElement('button', {
        'class': 'toolbar-btn',
        'id': 'toolbar-export-btn'
      }, '<i class="material-icons">download</i> ส่งออก');

      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': 'เลือก'
      }, '<i class="material-icons">edit</i>');

      const visibilityBtn = this.domUtils.createElement('button', {
        'class': 'tree-action-btn',
        'title': element.style.display === 'none' ? 'แสดง' : 'ซ่อน'
      }, `<i class="material-icons">${element.style.display === 'none' ? 'visibility_off' : 'visibility'}</i>`);

      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 = '<i class="material-icons">visibility</i>';
        } else {
          element.style.display = 'none';
          visibilityBtn.innerHTML = '<i class="material-icons">visibility_off</i>';
        }
      });

      // สร้างรายการย่อยสำหรับอิลิเมนต์ลูก
      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);