/**
* 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);