Files
firstgarden-web-gnu/plugin/editor/rb.editor/js/table.js
2025-07-02 14:13:16 +09:00

902 lines
30 KiB
JavaScript

//#region 셀 선택 및 툴바 표시
let selectedCells = [];
// 셀 클릭 이벤트: Ctrl 키와 함께 클릭 시 셀 선택 토글
$('#editor').on('click', 'td, th', function (e) {
if (e.ctrlKey) {
// Ctrl + 클릭: 선택/해제 토글 기능 유지
const cell = this;
const isSelected = $(cell).hasClass('selected-cell');
if (isSelected) {
// 이미 선택된 셀인 경우 선택 해제
$(cell).removeClass('selected-cell');
selectedCells = selectedCells.filter(c => c !== cell);
} else {
// 선택되지 않은 셀인 경우 선택
$(cell).addClass('selected-cell');
selectedCells.push(cell);
}
if (selectedCells.length > 0) {
// 마지막 클릭 위치(e.pageX, e.pageY)를 기준으로 툴바 표시
showCellToolbar(e.pageX, e.pageY);
} else {
$('#cell-toolbar').fadeOut(0); // 선택된 셀이 없으면 툴바 숨김
}
} else {
// Ctrl 없이 클릭 시 모든 선택된 셀 해제
$('td, th').removeClass('selected-cell');
selectedCells = [];
$('#cell-toolbar').fadeOut(0);
}
});
// 선택된 셀 강조 및 툴바 위치 계산
function showCellToolbar(x, y) {
const $cellToolbar = $('#cell-toolbar');
const $editorContainer = $('#editor-container');
$cellToolbar.css({
top: y + 'px',
left: x + 'px',
position: 'absolute'
}).fadeIn(0);
// 화면을 넘어가지 않도록 조정
ensureToolbarWithinBounds($cellToolbar, $editorContainer);
}
// 툴바가 컨테이너 내에 있도록 위치 조정하는 함수
function ensureToolbarWithinBounds($toolbar, $container) {
const toolbarRect = $toolbar[0].getBoundingClientRect();
const containerRect = $container[0].getBoundingClientRect();
let newTop = parseInt($toolbar.css('top'));
let newLeft = parseInt($toolbar.css('left'));
if (toolbarRect.bottom > containerRect.bottom) {
newTop -= (toolbarRect.bottom - containerRect.bottom);
}
if (toolbarRect.right > containerRect.right) {
newLeft -= (toolbarRect.right - containerRect.right);
}
if (toolbarRect.top < containerRect.top) {
newTop = containerRect.top;
}
if (toolbarRect.left < containerRect.left) {
newLeft = containerRect.left;
}
$toolbar.css({
top: newTop + 'px',
left: newLeft + 'px'
});
}
//#endregion 셀 선택 및 툴바 표시
//#region 테이블 그리드 매핑 함수
/**
* 테이블을 그리드로 매핑하여 각 셀의 위치를 반환합니다.
* @param {jQuery} $table - jQuery 객체로 된 테이블 요소
* @returns {Array} grid - 2차원 배열로 매핑된 셀 위치
*/
function mapTableGrid($table) {
const grid = [];
const rows = $table.find('tr');
rows.each(function (rowIndex, row) {
if (!grid[rowIndex]) {
grid[rowIndex] = [];
}
let colIndex = 0;
$(row).children('td, th').each(function (cellIndex, cell) {
// colspan과 rowspan 속성 가져오기
const colspan = parseInt($(cell).attr('colspan')) || 1;
const rowspan = parseInt($(cell).attr('rowspan')) || 1;
// 그리드에서 빈 위치 찾기
while (grid[rowIndex][colIndex]) {
colIndex++;
}
// 현재 셀을 그리드에 매핑
for (let i = 0; i < rowspan; i++) {
for (let j = 0; j < colspan; j++) {
if (!grid[rowIndex + i]) {
grid[rowIndex + i] = [];
}
grid[rowIndex + i][colIndex + j] = cell;
}
}
colIndex += colspan;
});
});
return grid;
}
//#endregion 테이블 그리드 매핑 함수
//#region 셀 병합 기능
// 셀 병합 버튼 클릭 이벤트
$('#merge-cells-btn').click(function () {
if (selectedCells.length <= 1) {
alert('병합할 셀을 2개 이상 선택해주세요.');
return;
}
const $table = $(selectedCells[0]).closest('table');
const grid = mapTableGrid($table);
// 선택된 셀들의 행과 열 인덱스 추출
const cellPositions = selectedCells.map(cell => {
for (let r = 0; r < grid.length; r++) {
for (let c = 0; c < grid[r].length; c++) {
if (grid[r][c] === cell) {
return {
row: r,
col: c
};
}
}
}
});
// 최소 및 최대 행, 열 인덱스 계산
const rowsIndices = cellPositions.map(pos => pos.row);
const colsIndices = cellPositions.map(pos => pos.col);
const minRow = Math.min(...rowsIndices);
const maxRow = Math.max(...rowsIndices);
const minCol = Math.min(...colsIndices);
const maxCol = Math.max(...colsIndices);
const rowSpan = maxRow - minRow + 1;
const colSpan = maxCol - minCol + 1;
// 선택된 영역이 완전한 사각형을 형성하는지 검증
for (let r = minRow; r <= maxRow; r++) {
for (let c = minCol; c <= maxCol; c++) {
const cell = grid[r][c];
if (!selectedCells.includes(cell)) {
alert('선택된 셀들은 병합할 수 없습니다.');
return;
}
const $cell = $(cell);
const existingRowSpan = parseInt($cell.attr('rowspan')) || 1;
const existingColSpan = parseInt($cell.attr('colspan')) || 1;
if (existingRowSpan > 1 || existingColSpan > 1) {
alert('이미 병합된 셀은 병합할 수 없습니다.');
return;
}
}
}
// 좌상단 셀 선택 및 rowspan, colspan 설정
const topLeftCell = grid[minRow][minCol];
const $topLeftCell = $(topLeftCell);
if (rowSpan > 1) {
$topLeftCell.attr('rowspan', rowSpan);
}
if (colSpan > 1) {
$topLeftCell.attr('colspan', colSpan);
}
// 나머지 셀들 제거
for (let r = minRow; r <= maxRow; r++) {
for (let c = minCol; c <= maxCol; c++) {
if (r === minRow && c === minCol) continue;
const cell = grid[r][c];
$(cell).remove();
}
}
// 선택 초기화 및 툴바 숨김
selectedCells = [];
$('td, th').removeClass('selected-cell');
$('#cell-toolbar').fadeOut(0);
});
//#endregion 셀 병합 기능
//#region 셀 병합 해제 기능
$('#unmerge-cells-btn').click(function () {
if (selectedCells.length !== 1) {
alert('병합 해제는 병합된 셀 하나만 선택해야 합니다.');
return;
}
const $table = $(selectedCells[0]).closest('table');
const grid = mapTableGrid($table);
// 병합된 셀 정보 가져오기
const $mergedCell = $(selectedCells[0]);
const rowspan = parseInt($mergedCell.attr('rowspan')) || 1;
const colspan = parseInt($mergedCell.attr('colspan')) || 1;
if (rowspan === 1 && colspan === 1) {
alert('이 셀은 병합되지 않았습니다.');
return;
}
const mergedCellPosition = (() => {
for (let r = 0; r < grid.length; r++) {
for (let c = 0; c < grid[r].length; c++) {
if (grid[r][c] === $mergedCell[0]) {
return {
row: r,
col: c
};
}
}
}
return null;
})();
if (!mergedCellPosition) {
alert('병합된 셀의 위치를 확인할 수 없습니다.');
return;
}
const {
row: startRow,
col: startCol
} = mergedCellPosition;
// 병합된 셀의 rowspan 및 colspan에 따라 셀 추가
for (let r = startRow; r < startRow + rowspan; r++) {
for (let c = startCol; c < startCol + colspan; c++) {
if (r === startRow && c === startCol) {
// 기존 병합된 셀은 rowspan, colspan 제거
$mergedCell.removeAttr('rowspan').removeAttr('colspan');
} else {
// 나머지 셀 복원
const $newCell = $('<td><div><br></div></td>').css({
border: '1px solid #ddd',
padding: '5px',
});
// 그리드 위치에 따라 셀 삽입
const $row = $table.find('tr').eq(r);
// 그리드 내 현재 셀의 위치를 찾기 (colSpan을 고려)
let insertBefore = null;
let currentCol = 0;
$row.children('td, th').each(function () {
const colspanAttr = parseInt($(this).attr('colspan')) || 1;
if (currentCol === c) {
insertBefore = this;
return false; // 루프 종료
}
currentCol += colspanAttr;
});
if (insertBefore) {
$(insertBefore).before($newCell);
} else {
$row.append($newCell);
}
// 그리드 업데이트
grid[r][c] = $newCell[0];
}
}
}
// 선택 초기화 및 툴바 숨김
selectedCells = [];
$('td, th').removeClass('selected-cell');
$('#cell-toolbar').fadeOut(0);
});
//#endregion 셀 병합 해제 기능
//#region 셀 배경색 변경
$('#cell-bg-color-btn').on('click', function () {
$('#cell-bg-color-picker').click();
});
$('#cell-bg-color-picker').on('input', function () {
const selectedColor = $(this).val();
// 선택된 셀의 배경 색상 변경
$('.selected-cell').each(function () {
$(this).css('background-color', selectedColor);
$('#cell-bg-color-btn').css('background-color', selectedColor);
// 선택 후 초기화 (필요 시 주석 해제)
// selectedCells = [];
// $('td, th').removeClass('selected-cell');
});
});
//#endregion 셀 배경색 변경
//#region 문서 외부 클릭 이벤트 처리
// 외부 클릭 시 초기화
$(document).on('click', function (e) {
if (!$(e.target).closest('#editor').length && !$(e.target).closest('#toolbar').length) {
savedSelection = null;
}
const $grid = $('#table-grid');
if (!$(e.target).closest('#table-grid').length && !$(e.target).is('#insert-table-btn')) {
$grid.fadeOut(0);
}
// 표 셀 선택 해제
if (!$(e.target).closest('td, th, #cell-toolbar').length) {
selectedCells = [];
$('td, th').removeClass('selected-cell');
$('#cell-toolbar').fadeOut(0);
}
const isInsideEditor = $(e.target).closest('#editor').length > 0; // 클릭한 요소가 #editor 내부인지 확인
const isResizable = $(e.target).closest('.resizable').length > 0; // 클릭한 요소가 .resizable인지 확인
const isToolbar = $(e.target).closest('#image-toolbar').length > 0; // 클릭한 요소가 툴바인지 확인
if (!isResizable && !isToolbar) {
$('.resizable').removeClass('selected'); // 모든 이미지에서 selected 제거
$('#image-toolbar').fadeOut(0); // 툴바 숨기기
}
});
// #cell-toolbar 내부 클릭 시 이벤트 전파 차단
$('#cell-toolbar').on('click', function (e) {
e.stopPropagation();
});
//#endregion 문서 외부 클릭 이벤트 처리
//#region 정렬 관련 함수
function applyAlignmentToSelectedCells(alignment) {
const selection = window.getSelection();
const range = selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
if (range) {
const ancestorTable = $(range.commonAncestorContainer).closest('table');
// 표 내부일 경우
if (ancestorTable.length) {
// 드래그로 선택된 셀들 가져오기
const selectedCells = ancestorTable.find('td, th').filter(function () {
const cellRange = document.createRange();
cellRange.selectNodeContents(this);
// 드래그로 선택된 범위와 셀이 겹치는지 확인
return (
range.compareBoundaryPoints(Range.START_TO_END, cellRange) !== -1 &&
range.compareBoundaryPoints(Range.END_TO_START, cellRange) !== 1
);
});
// Ctrl + 클릭으로 선택된 셀은 제외
const filteredCells = selectedCells.filter(function () {
return !$(this).hasClass('selected-cell'); // 'selected-cell' 클래스 제외
});
// 선택된 셀만 정렬 적용
filteredCells.each(function () {
$(this).css('text-align', alignment);
});
return true; // 표 정렬 적용 완료
}
}
return false; // 표 정렬 미적용
}
function applyAlignmentToSelectedCellsOrEditor(alignment) {
const selection = window.getSelection();
const range = selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
if (range) {
const commonAncestor = range.commonAncestorContainer;
// 선택된 범위 내의 모든 셀(td, th)을 가져옴
const selectedCells = $(commonAncestor)
.closest('table')
.find('td, th')
.filter(function () {
const cellRange = document.createRange();
cellRange.selectNodeContents(this);
// 셀이 선택된 범위와 겹치는지 확인
return (
range.compareBoundaryPoints(Range.START_TO_END, cellRange) !== -1 &&
range.compareBoundaryPoints(Range.END_TO_START, cellRange) !== 1
);
});
if (selectedCells.length > 0) {
// 선택된 모든 셀에 정렬 적용
selectedCells.each(function () {
$(this).css('text-align', alignment);
});
} else {
// 선택된 범위가 없거나 표 외부인 경우 전체 에디터에 정렬 적용
$('#editor').children(':not(table)').css('text-align', alignment);
}
}
}
function applyAlignmentToTableCells(range, alignment) {
const selectedCells = getSelectedCellsInRange(range);
if (selectedCells.length > 0) {
selectedCells.each(function () {
$(this).css('text-align', alignment);
});
return true; // 표 내부 정렬 적용 완료
}
return false; // 표 내부 정렬 없음
}
// 범위 내에서 선택된 셀(td, th) 가져오기
function getSelectedCellsInRange(range) {
const selectedCells = [];
const ancestor = range.commonAncestorContainer;
// 범위가 표 내부인지 확인
const table = $(ancestor).closest('table');
if (table.length) {
const startContainer = range.startContainer;
const endContainer = range.endContainer;
const startCell = $(startContainer).closest('td, th')[0];
const endCell = $(endContainer).closest('td, th')[0];
if (startCell && endCell) {
let selecting = false;
table.find('tr').each(function () {
$(this).children('td, th').each(function () {
if (this === startCell) {
selecting = true; // 선택 시작
selectedCells.push(this);
} else if (this === endCell) {
selectedCells.push(this); // 선택 끝
selecting = false; // 선택 종료
} else if (selecting) {
selectedCells.push(this); // 선택 중간 셀
}
});
if (!selecting && selectedCells.length > 0) {
return false; // 선택 종료 시 루프 탈출
}
});
}
}
return $(selectedCells); // jQuery 객체로 반환
}
//#endregion 정렬 관련 함수
//#region 테이블 삽입 기능
/**
* 주어진 행과 열 수로 테이블 생성 및 에디터에 삽입
* @param {number} rows - 테이블 행 수
* @param {number} cols - 테이블 열 수
*/
function insertTable(rows, cols) {
if (savedSelection) {
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(savedSelection); // 이전 커서 위치 복원
}
const selection = window.getSelection();
const range = selection.rangeCount ? selection.getRangeAt(0) : null;
if (range) {
let container = range.commonAncestorContainer;
if (container.nodeType === Node.TEXT_NODE) container = container.parentElement;
// ✅ 테이블 내부(td, th)인지 확인 후 생성 제한
if ($(container).closest('td, th').length) {
alert("테이블 내부에서는 테이블을 생성할 수 없습니다.");
return;
}
}
// ✅ 테이블을 정확한 행*열로 생성
let tableHTML = `
<div class="resizable-table" style="width: 100%; position: relative;">
<div class="rb_editor_table_wrap">
<div class="rb_editor_table_wrap_inner">
<table style="width: 100%; border-collapse: collapse; border: 1px solid #ddd;">
<tbody>
${Array(rows).fill(`<tr>${Array(cols).fill('<td style="border: 1px solid #ddd; padding: 10px; height:40px;"><div><br></div></td>').join('')}</tr>`).join('')}
</tbody>
</table>
</div>
</div>
<div class="resizable-table-handle" style="width: 5px; height: 100%; position: absolute; right: 0; top: 0; cursor: ew-resize; background: rgba(170, 32, 255, 0.3);"></div>
</div>
<div><br></div>
`;
const $table = $(tableHTML);
const $editor = $('#editor');
if (range) {
let container = range.commonAncestorContainer;
if (container.nodeType === Node.TEXT_NODE) container = container.parentElement;
if ($(container).closest('#editor').length) {
range.deleteContents();
range.insertNode($table[0]);
range.collapse(true);
selection.removeAllRanges();
selection.addRange(range);
} else {
$editor.append($table);
}
} else {
$editor.append($table);
}
enableResizableTable($table);
}
/**
* 테이블 크기 조정 기능 활성화
* @param {HTMLElement} table - 크기 조정할 테이블 요소
*/
function enableResizableTable(table) {
// 테이블이 jQuery 객체라면 DOM 요소로 변환
if (table instanceof jQuery) {
table = table.get(0);
}
// 테이블이 NodeList(배열)라면 첫 번째 요소 선택
if (NodeList.prototype.isPrototypeOf(table) || Array.isArray(table)) {
table = table[0];
}
// 유효한 DOM 요소인지 확인
if (!(table instanceof HTMLElement)) {
//console.error("⚠️ enableResizableTable: 유효한 테이블 요소가 아닙니다.", table);
return;
}
let handle = table.querySelector('.resizable-table-handle');
// ✅ 핸들이 없으면 자동 추가
if (!handle) {
//console.warn('resizable-table-handle 요소가 없습니다. 자동 추가합니다.');
handle = document.createElement("div");
handle.classList.add("resizable-table-handle");
table.appendChild(handle);
}
let isResizing = false;
let startX = 0;
let startWidth = 0;
function startResize(event) {
isResizing = true;
startX = event.type.includes('touch') ? event.touches[0].clientX : event.clientX;
startWidth = table.offsetWidth;
document.addEventListener('mousemove', doResize);
document.addEventListener('mouseup', stopResize);
document.addEventListener('touchmove', doResize, { passive: false });
document.addEventListener('touchend', stopResize, { passive: false });
event.preventDefault();
}
function doResize(event) {
if (!isResizing) return;
const moveX = event.type.includes('touch') ? event.touches[0].clientX : event.clientX;
const newWidth = startWidth + (moveX - startX);
if (newWidth > 100) { // 최소 너비 제한
table.style.width = `${newWidth}px`;
}
}
function stopResize() {
isResizing = false;
document.removeEventListener('mousemove', doResize);
document.removeEventListener('mouseup', stopResize);
document.removeEventListener('touchmove', doResize);
document.removeEventListener('touchend', stopResize);
}
// 기존 이벤트 제거 후 다시 바인딩
handle.removeEventListener('mousedown', startResize);
handle.removeEventListener('touchstart', startResize);
handle.addEventListener('mousedown', startResize);
handle.addEventListener('touchstart', startResize, { passive: false });
}
/**
* 에디터 내 모든 테이블을 감싸고 핸들 추가
*/
function wrapTablesWithEditorWrap() {
$('#editor table').each(function () {
const $table = $(this);
if ($table.closest('.resizable-table').length) return; // 이미 감싸져 있으면 패스
// 테이블 스타일 초기화 및 새로운 스타일 적용
$table.removeAttr('style').css({
'width': '100%',
'border-collapse': 'collapse',
'border': '1px solid rgb(221, 221, 221)',
'table-layout': 'fixed'
});
// 테이블 내부의 td 및 th 스타일 초기화 및 새로운 스타일 적용
$table.find('td, th').each(function () {
$(this).removeAttr('style').css({
'border': '1px solid rgb(221, 221, 221)',
'padding': '10px',
'height': '40px',
'position': 'relative'
});
});
const $wrapper = $('<div class="resizable-table" style="width: 100%; position: relative;"></div>');
//const $handle = $('<div class="resizable-table-handle" style="width: 5px; height: 100%; position: absolute; right: 0; top: 0; cursor: ew-resize; background: rgba(170, 32, 255, 0.3)"></div>');
// 기존 래퍼 유지
const $wrapInner = $('<div class="rb_editor_table_wrap"><div class="rb_editor_table_wrap_inner"></div></div>');
// 테이블을 감싸기만 함 (이동 X, 제거 X)
$table.wrap($wrapInner);
// 최종 래퍼 구성
$table.parent().parent().wrap($wrapper);
//$table.parent().parent().parent().append($handle);
enableResizableTable($table.closest('.resizable-table'));
});
}
/**
* 주어진 테이블의 셀 크기 조정 기능 활성화
* @param {jQuery} $table - 크기 조정할 테이블 요소
*/
function enableResizableTableCells($table) {
const resizeMargin = 10; // 커서 반경 확대
let isResizing = false;
let resizingCell = null;
const passiveOptions = { passive: true };
$table.css({
"table-layout": "fixed", // 테이블 레이아웃 고정
"width": "100%", // 테이블 전체 폭 100%
});
$table.find('td, th').each(function () {
const $cell = $(this);
$cell.css({ position: 'relative' });
// ✅ 마지막 열인지 확인하여 크기 조정 방지
const isLastColumn = $cell.is(':last-child');
if (isLastColumn) return; // 마지막 열은 크기 조정 X
function handleMove(event) {
const isTouch = event.type.includes('touch');
const offsetX = isTouch ? event.touches[0].clientX - $cell.offset().left : event.offsetX;
const offsetY = isTouch ? event.touches[0].clientY - $cell.offset().top : event.offsetY;
const cellWidth = $cell.outerWidth();
const cellHeight = $cell.outerHeight();
const $row = $cell.closest('tr');
$table.find('tr').removeClass('resize-row-highlight');
$cell.removeClass('resize-right-highlight resize-bottom-highlight');
// ✅ 마지막 열은 커서 변경 X
if (isLastColumn) {
$cell.css('cursor', 'text');
return;
}
if (offsetX > cellWidth - resizeMargin && offsetY > cellHeight - resizeMargin) {
$cell.addClass('resize-right-highlight resize-bottom-highlight');
$row.addClass('resize-row-highlight');
$cell.css('cursor', 'se-resize');
} else if (offsetX > cellWidth - resizeMargin) {
$cell.addClass('resize-right-highlight');
$cell.css('cursor', 'ew-resize');
} else if (offsetY > cellHeight - resizeMargin) {
$row.addClass('resize-row-highlight');
$cell.css('cursor', 'ns-resize');
} else {
$cell.css('cursor', 'text');
}
}
$cell.on('mousemove', handleMove);
$cell.get(0).addEventListener('touchmove', handleMove, passiveOptions);
function handleStart(event) {
const isTouch = event.type.includes('touch');
const touch = isTouch ? event.touches[0] : null;
const startX = isTouch ? touch.clientX : event.pageX;
const startY = isTouch ? touch.clientY : event.pageY;
const cellWidth = $cell.outerWidth();
const cellHeight = $cell.outerHeight();
const $table = $cell.closest('table');
if (isLastColumn) return; // ✅ 마지막 열이면 크기 조정 시작 X
const isResizeArea = (
startX > $cell.offset().left + cellWidth - resizeMargin ||
startY > $cell.offset().top + cellHeight - resizeMargin
);
if (!isResizeArea) return;
event.preventDefault();
isResizing = true;
resizingCell = $cell;
const startWidth = $cell.outerWidth();
const startHeight = $cell.outerHeight();
const cellIndex = $cell.index();
const $row = $cell.closest('tr');
const rowIndex = $row.index();
function doResize(e) {
const isTouchMove = e.type.includes('touch');
const moveX = isTouchMove ? e.touches[0].clientX : e.pageX;
const moveY = isTouchMove ? e.touches[0].clientY : e.pageY;
if (isResizing && resizingCell) {
if (startX > $cell.offset().left + cellWidth - resizeMargin) {
const newWidth = startWidth + (moveX - startX);
if (newWidth > 30) {
$table.find('tr').each(function () {
$(this).children().eq(cellIndex).css('width', `${newWidth}px`);
});
}
}
if (startY > $cell.offset().top + cellHeight - resizeMargin) {
const newHeight = startHeight + (moveY - startY);
if (newHeight > 20) {
$table.find('tr').eq(rowIndex).children().css('height', `${newHeight}px`);
}
}
}
}
function stopResize() {
isResizing = false;
resizingCell = null;
$(document).off('mousemove', doResize);
$(document).off('mouseup', stopResize);
document.removeEventListener('touchmove', doResize, passiveOptions);
document.removeEventListener('touchend', stopResize, passiveOptions);
}
$(document).on('mousemove', doResize);
$(document).on('mouseup', stopResize);
document.addEventListener('touchmove', doResize, passiveOptions);
document.addEventListener('touchend', stopResize, passiveOptions);
}
$cell.on('mousedown', handleStart);
$cell.get(0).addEventListener('touchstart', handleStart, { passive: false });
$cell.on('mouseleave touchend', function () {
if (!isResizing) {
$table.find('tr').removeClass('resize-row-highlight');
$cell.removeClass('resize-right-highlight resize-bottom-highlight');
}
});
});
}
//#endregion 셀 크기 조정 기능
//#region 테이블 리사이징 초기화 (기존 테이블에 적용)
function initializeResizableTables() {
$('#editor table').each(function () {
enableResizableTableCells($(this));
});
}
//#endregion 테이블 리사이징 초기화 (기존 테이블에 적용)
//#region MutationObserver 설정
/**
* MutationObserver를 사용하여 새로운 테이블이 추가될 때 자동으로 크기 조정 기능을 적용합니다.
*/
const tableObserver = new MutationObserver(function (mutationsList) {
for (let mutation of mutationsList) {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
if ($(node).is('table')) {
enableResizableTableCells($(node));
} else {
// 만약 노드가 테이블을 포함하고 있다면
$(node).find('table').each(function () {
enableResizableTableCells($(this));
});
}
});
}
}
});
// #editor 내부의 변경 사항 관찰
tableObserver.observe(document.getElementById('editor'), {
childList: true,
subtree: true
});
//#endregion MutationObserver 설정
//#region 이벤트 핸들러 등록
// 정렬 버튼 이벤트 예시 (Assuming you have buttons for alignment)
$('#align-left-btn').click(function () {
applyAlignmentToSelectedCells('left');
});
$('#align-center-btn').click(function () {
applyAlignmentToSelectedCells('center');
});
$('#align-right-btn').click(function () {
applyAlignmentToSelectedCells('right');
});
// 페이지 로드 시 실행
$(document).ready(function () {
wrapTablesWithEditorWrap();
initializeResizableTables();
// MutationObserver로 동적 감지
const observer = new MutationObserver(() => {
wrapTablesWithEditorWrap();
});
observer.observe(document.getElementById('editor'), { childList: true, subtree: true });
});