var urlRegex = /(https?:\/\/[^\s]+)/g; var imageRegex = /\.(jpeg|jpg|gif|png|webp|svg)$/i; var pendingRequests = {}; function extractVideoId(url) { let videoId = null; let timeParam = ''; if (url.includes('youtube.com') || url.includes('youtu.be')) { let match = url.match(/(?:v=|youtu\.be\/|embed\/|shorts\/)([\w-]+)/); videoId = match ? match[1] : null; let timeMatch = url.match(/[?&]t=(\d+)/); timeParam = timeMatch ? `?start=${timeMatch[1]}` : ''; return videoId ? `https://www.youtube.com/embed/${videoId}${timeParam}` : null; } if (url.includes('vimeo.com')) { let match = url.match(/vimeo\.com\/(\d+)/); videoId = match ? match[1] : null; return videoId ? `https://player.vimeo.com/video/${videoId}` : null; } return null; } function fetchMetadataAndDisplay(url, range) { //
내부에서 실행되지 않도록 차단
if ($(range.commonAncestorContainer).closest('pre').length > 0) {
console.warn("🚫 내부에서는 미디어 또는 메타데이터를 삽입할 수 없습니다.");
return;
}
let embedUrl = extractVideoId(url);
if ($(range.commonAncestorContainer).closest('table, td, th').length) {
return;
}
if (embedUrl) {
displayVideoPreview(url, embedUrl, range);
return;
}
$.ajax({
url: g5Config.g5_editor_url + '/php/rb.metadata.php',
method: 'GET',
data: { url: url },
dataType: 'json',
success: function (response) {
if (response.error) {
return;
}
var meta = response.meta;
var title = response.title || '제목 없음';
var description = meta['description'] || meta['og:description'] || '';
var image = meta['og:image'] || '';
var wrapper = $(''); // 메타데이터 감싸는 div
var html = $('');
var imageWrapper = $(''); // 이미지 감싸는 요소
var metaData = $('');
function validateImageUrl(imageUrl, callback) {
var img = new Image();
img.src = imageUrl;
img.onload = function () {
callback(true); // 이미지 로드 성공
};
img.onerror = function () {
callback(false); // 이미지 로드 실패
};
}
if (image) {
var imageProxyUrl = g5Config.g5_editor_url + '/php/rb.image_proxy.php?url=' + encodeURIComponent(image);
var imageElement = $('' + description + '
'); metaData.append('' + url + ''); html.append(metaData); wrapper.append(html); insertMediaAfterUrl(wrapper, range); } }); } function displayVideoPreview(url, embedUrl, range) { // 내부에서는 동영상 미리보기를 삽입하지 않도록 제한
if ($(range.commonAncestorContainer).closest('pre').length > 0) {
//console.warn(" 내부에서는 동영상 미리보기를 삽입할 수 없습니다.");
return;
}
if ($(range.commonAncestorContainer).closest('table, td, th').length) {
return;
}
var wrapper = $(''); // ✅ 동영상 감싸는 div
var html = $('');
html.append('');
wrapper.append(html); // 감싸는 div 추가
insertMediaAfterUrl(wrapper, range);
}
function insertMediaAfterUrl(node, range) {
range.collapse(false);
range.insertNode(node[0]);
range.collapse(false);
var selection = window.getSelection();
selection.removeAllRanges();
var newRange = document.createRange();
newRange.setStartAfter(node[0]);
newRange.collapse(true);
selection.addRange(newRange);
// 미디어 노드 내의 .resizable 요소에 대해 현재 크기를 data 속성으로 업데이트
node.find('.resizable').each(function(){
var $this = $(this);
var currentWidth = $this.width();
var currentHeight = $this.height();
$this.attr('data-original-width', currentWidth);
$this.attr('data-original-height', currentHeight);
// 필요시 data-ratio도 갱신 (세로/가로)
$this.attr('data-ratio', currentHeight / currentWidth);
});
}
function insertImageDirectly(url, range) {
if ($(range.commonAncestorContainer).closest('table, td, th').length) {
return;
}
var wrapper = $(''); // ✅ 이미지 감싸는 div
var html = $('');
var img = $('
');
img.on('load', function () {
// ✅ 이미지 원본 크기 저장
var imgWidth = this.naturalWidth;
var imgHeight = this.naturalHeight;
var ratio = imgHeight / imgWidth;
const editorWidth = $('#editor').width(); // 에디터의 가로 크기
// ✅ 에디터보다 크면 width: 100%, 작으면 원본 크기 유지
if (imgWidth > editorWidth) {
html.css('width', '100%');
} else {
html.css('width', imgWidth + 'px');
}
// ✅ 원본 크기 저장 (비율 유지용)
html.attr('data-original-width', imgWidth);
html.attr('data-original-height', imgHeight);
html.attr('data-ratio', ratio);
// ✅ 높이 설정 (현재 width에 따라 비율 유지)
html.css('height', (html.width() * ratio) + 'px');
// ✅ 창 크기 변경 시 높이 자동 조정
function updateHeight() {
var currentWidth = html.width();
var originalWidth = parseFloat(html.attr('data-original-width')) || currentWidth;
var originalHeight = parseFloat(html.attr('data-original-height')) || (currentWidth * ratio);
var originalRatio = parseFloat(html.attr('data-ratio')) || (originalHeight / originalWidth);
html.css('height', (currentWidth * originalRatio) + 'px');
}
$(window).on('resize', updateHeight);
// ✅ 크기 조절 기능 적용 (비율 유지)
makeImageResizableWithObserver(html);
});
html.append(img);
html.append('');
wrapper.append(html);
insertMediaAfterUrl(wrapper, range);
}
$('#editor').on('click', '.delete-preview', function (e) {
e.preventDefault();
e.stopPropagation(); // 부모로의 이벤트 버블링 방지
$(this).closest('.url-preview-img, .url-preview-meta, .url-preview-video').remove();
$('#image-toolbar').fadeOut(0);
selectedImage = null;
});