211 lines
5.6 KiB
JavaScript
211 lines
5.6 KiB
JavaScript
// DOM 요소
|
||
const linkTitleInput = document.getElementById('linkTitle');
|
||
const linkUrlInput = document.getElementById('linkUrl');
|
||
const addBtn = document.getElementById('addBtn');
|
||
const linksContainer = document.getElementById('linksContainer');
|
||
|
||
// 링크 목록을 저장하고 불러오기
|
||
let links = [];
|
||
|
||
// 초기 로드
|
||
loadLinks();
|
||
|
||
// 이벤트 리스너
|
||
addBtn.addEventListener('click', addLink);
|
||
linkTitleInput.addEventListener('keypress', (e) => {
|
||
if (e.key === 'Enter') addLink();
|
||
});
|
||
linkUrlInput.addEventListener('keypress', (e) => {
|
||
if (e.key === 'Enter') addLink();
|
||
});
|
||
|
||
// 링크 추가 함수
|
||
function addLink() {
|
||
const title = linkTitleInput.value.trim();
|
||
const url = linkUrlInput.value.trim();
|
||
|
||
if (!title || !url) {
|
||
alert('제목과 URL을 모두 입력해주세요.');
|
||
return;
|
||
}
|
||
|
||
// URL 형식 검증 (간단한 http/https 체크)
|
||
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
||
alert('올바른 URL 형식을 입력해주세요. (\'http://\' 또는 \'https://\'로 시작)');
|
||
return;
|
||
}
|
||
|
||
const newLink = {
|
||
id: Date.now(),
|
||
title: title,
|
||
url: url
|
||
};
|
||
|
||
links.push(newLink);
|
||
saveLinks();
|
||
renderLinks();
|
||
|
||
// 입력 필드 초기화
|
||
linkTitleInput.value = '';
|
||
linkUrlInput.value = '';
|
||
linkTitleInput.focus();
|
||
}
|
||
|
||
// 링크 삭제 함수
|
||
function deleteLink(id) {
|
||
if (confirm('이 링크를 삭제하시겠습니까?')) {
|
||
links = links.filter(link => link.id !== id);
|
||
saveLinks();
|
||
renderLinks();
|
||
}
|
||
}
|
||
|
||
// 링크로 이동 함수
|
||
function navigateToLink(url) {
|
||
window.location.href = url;
|
||
}
|
||
|
||
// 링크 렌더링 함수
|
||
function renderLinks() {
|
||
linksContainer.innerHTML = '';
|
||
|
||
if (links.length === 0) {
|
||
linksContainer.innerHTML = `
|
||
<div class="empty-state">
|
||
<p>저장된 링크가 없습니다</p>
|
||
<small>위에서 자주 방문하는 사이트를 추가해보세요</small>
|
||
</div>
|
||
`;
|
||
return;
|
||
}
|
||
|
||
links.forEach((link, index) => {
|
||
const linkCard = document.createElement('div');
|
||
linkCard.className = 'link-card';
|
||
linkCard.draggable = true;
|
||
linkCard.dataset.index = index;
|
||
|
||
linkCard.innerHTML = `
|
||
<button class="delete-btn">×</button>
|
||
<h3>${escapeHtml(link.title)}</h3>
|
||
<p>${escapeHtml(link.url)}</p>
|
||
`;
|
||
|
||
// 삭제 버튼 이벤트 리스너
|
||
const deleteBtn = linkCard.querySelector('.delete-btn');
|
||
deleteBtn.addEventListener('click', (e) => {
|
||
e.stopPropagation(); // 카드 클릭 이벤트 전파 방지
|
||
deleteLink(link.id);
|
||
});
|
||
|
||
// 카드 클릭 시 링크로 이동 (삭제 버튼 제외)
|
||
linkCard.addEventListener('click', (e) => {
|
||
if (!e.target.classList.contains('delete-btn')) {
|
||
navigateToLink(link.url);
|
||
}
|
||
});
|
||
|
||
// 드래그 앤 드롭 이벤트
|
||
linkCard.addEventListener('dragstart', handleDragStart);
|
||
linkCard.addEventListener('dragover', handleDragOver);
|
||
linkCard.addEventListener('drop', handleDrop);
|
||
linkCard.addEventListener('dragenter', handleDragEnter);
|
||
linkCard.addEventListener('dragleave', handleDragLeave);
|
||
linkCard.addEventListener('dragend', handleDragEnd);
|
||
|
||
linksContainer.appendChild(linkCard);
|
||
});
|
||
}
|
||
|
||
// Chrome Storage에 저장
|
||
function saveLinks() {
|
||
chrome.storage.sync.set({ links: links }, () => {
|
||
console.log('Links saved');
|
||
});
|
||
}
|
||
|
||
// Chrome Storage에서 불러오기
|
||
function loadLinks() {
|
||
chrome.storage.sync.get(['links'], (result) => {
|
||
if (result.links) {
|
||
links = result.links;
|
||
} else {
|
||
// 기본 링크 예시 (선택사항)
|
||
links = [
|
||
{ id: 1, title: 'Google', url: 'https://www.google.com' },
|
||
{ id: 2, title: 'YouTube', url: 'https://www.youtube.com' },
|
||
{ id: 3, title: 'GitHub', url: 'https://github.com' }
|
||
];
|
||
saveLinks();
|
||
}
|
||
renderLinks();
|
||
});
|
||
}
|
||
|
||
// XSS 방지를 위한 HTML 이스케이프
|
||
function escapeHtml(text) {
|
||
const div = document.createElement('div');
|
||
div.textContent = text;
|
||
return div.innerHTML;
|
||
}
|
||
|
||
// 드래그 앤 드롭 관련 변수
|
||
let draggedElement = null;
|
||
|
||
// 드래그 시작
|
||
function handleDragStart(e) {
|
||
draggedElement = this;
|
||
this.classList.add('dragging');
|
||
e.dataTransfer.effectAllowed = 'move';
|
||
}
|
||
|
||
// 드래그 오버
|
||
function handleDragOver(e) {
|
||
e.preventDefault();
|
||
e.dataTransfer.dropEffect = 'move';
|
||
return false;
|
||
}
|
||
|
||
// 드래그 진입
|
||
function handleDragEnter(e) {
|
||
if (this !== draggedElement) {
|
||
this.classList.add('drag-over');
|
||
}
|
||
}
|
||
|
||
// 드래그 떠남
|
||
function handleDragLeave(e) {
|
||
this.classList.remove('drag-over');
|
||
}
|
||
|
||
// 드롭
|
||
function handleDrop(e) {
|
||
e.stopPropagation();
|
||
e.preventDefault();
|
||
|
||
if (draggedElement !== this) {
|
||
const fromIndex = parseInt(draggedElement.dataset.index);
|
||
const toIndex = parseInt(this.dataset.index);
|
||
|
||
// 배열에서 항목 이동
|
||
const movedItem = links.splice(fromIndex, 1)[0];
|
||
links.splice(toIndex, 0, movedItem);
|
||
|
||
saveLinks();
|
||
renderLinks();
|
||
}
|
||
|
||
this.classList.remove('drag-over');
|
||
return false;
|
||
}
|
||
|
||
// 드래그 종료
|
||
function handleDragEnd(e) {
|
||
this.classList.remove('dragging');
|
||
|
||
// 모든 drag-over 클래스 제거
|
||
document.querySelectorAll('.link-card').forEach(card => {
|
||
card.classList.remove('drag-over');
|
||
});
|
||
}
|