Public Source Viewer
비나래아카이브 개발자 포털
실제 서비스 구조를 살펴볼 수 있는 공개용 코드 뷰어입니다. 인증, 세션, 외부 연동, 토큰, 관리자 식별 등 보안상 민감한 구현은 파일 단위 또는 줄 단위로 검열됩니다.
public/js/popup.js
공개 가능
1
/* popup.js — showAlert / showConfirm / showPrompt */
2
(function () {
3
/* ── CSS (한 번만 삽입) ── */
4
const style = document.createElement('style');
5
style.textContent = `
6
.popup-overlay{position:fixed;inset:0;background:rgba(0,0,0,.45);display:flex;align-items:center;justify-content:center;z-index:99999;opacity:0;transition:opacity .15s}
7
.popup-overlay.show{opacity:1}
8
.popup-box{background:var(--bg-secondary,#f2f0eb);color:var(--text-primary,#3e3a36);border:1px solid var(--border-color,#ccc6bc);border-radius:14px;padding:28px 30px 20px;min-width:320px;max-width:420px;width:90%;box-shadow:0 8px 32px rgba(0,0,0,.25);transform:scale(.92);transition:transform .15s}
9
.popup-overlay.show .popup-box{transform:scale(1)}
10
.popup-msg{font-size:.95rem;line-height:1.6;white-space:pre-wrap;word-break:break-word;margin-bottom:20px}
11
.popup-input{width:100%;padding:10px 12px;border:1px solid var(--border-color,#ccc6bc);border-radius:8px;background:var(--bg-main,#e4dfd7);color:var(--text-primary,#3e3a36);font-size:.95rem;margin-bottom:18px;outline:none;box-sizing:border-box}
12
.popup-input:focus{border-color:var(--accent-color,#b45309)}
13
.popup-btns{display:flex;justify-content:flex-end;gap:10px}
14
.popup-btn{padding:8px 22px;border:none;border-radius:8px;font-size:.9rem;font-weight:600;cursor:pointer;font-family:inherit;transition:filter .12s}
15
.popup-btn:hover{filter:brightness(.9)}
16
.popup-btn-cancel{background:var(--bg-tertiary,#dcd6ce);color:var(--text-secondary,#69615c)}
17
.popup-btn-ok{background:var(--accent-color,#b45309);color:#fff}
18
`;
19
document.head.appendChild(style);
20
21
/* ── 유틸 ── */
22
function create(tag, cls, text) {
23
const el = document.createElement(tag);
24
if (cls) el.className = cls;
25
if (text !== undefined) el.textContent = text;
26
return el;
27
}
28
29
function buildOverlay() {
30
const overlay = create('div', 'popup-overlay');
31
const box = create('div', 'popup-box');
32
overlay.appendChild(box);
33
document.body.appendChild(overlay);
34
// trigger reflow for animation
35
overlay.offsetHeight;
36
overlay.classList.add('show');
37
return { overlay, box };
38
}
39
40
function destroy(overlay) {
41
overlay.classList.remove('show');
42
setTimeout(() => overlay.remove(), 160);
43
}
44
45
/* ── showAlert ── */
46
window.showAlert = function (message) {
47
return new Promise(function (resolve) {
48
var ref = buildOverlay();
49
var overlay = ref.overlay;
50
var box = ref.box;
51
52
var msg = create('div', 'popup-msg', message);
53
var btns = create('div', 'popup-btns');
54
var ok = create('button', 'popup-btn popup-btn-ok', '확인');
55
btns.appendChild(ok);
56
box.appendChild(msg);
57
box.appendChild(btns);
58
59
function close() { destroy(overlay); resolve(); }
60
ok.addEventListener('click', close);
61
overlay.addEventListener('click', function (e) { if (e.target === overlay) close(); });
62
document.addEventListener('keydown', function handler(e) {
63
if (e.key === 'Enter' || e.key === 'Escape') { document.removeEventListener('keydown', handler); close(); }
64
});
65
ok.focus();
66
});
67
};
68
69
/* ── showConfirm ── */
70
window.showConfirm = function (message) {
71
return new Promise(function (resolve) {
72
var ref = buildOverlay();
73
var overlay = ref.overlay;
74
var box = ref.box;
75
76
var msg = create('div', 'popup-msg', message);
77
var btns = create('div', 'popup-btns');
78
var cancel = create('button', 'popup-btn popup-btn-cancel', '취소');
79
var ok = create('button', 'popup-btn popup-btn-ok', '확인');
80
btns.appendChild(cancel);
81
btns.appendChild(ok);
82
box.appendChild(msg);
83
box.appendChild(btns);
84
85
var done = false;
86
function finish(val) { if (done) return; done = true; destroy(overlay); resolve(val); }
87
ok.addEventListener('click', function () { finish(true); });
88
cancel.addEventListener('click', function () { finish(false); });
89
overlay.addEventListener('click', function (e) { if (e.target === overlay) finish(false); });
90
document.addEventListener('keydown', function handler(e) {
91
if (e.key === 'Enter') { document.removeEventListener('keydown', handler); finish(true); }
92
if (e.key === 'Escape') { document.removeEventListener('keydown', handler); finish(false); }
93
});
94
ok.focus();
95
});
96
};
97
98
/* ── showPrompt ── */
99
window.showPrompt = function (message, defaultValue) {
100
return new Promise(function (resolve) {
101
var ref = buildOverlay();
102
var overlay = ref.overlay;
103
var box = ref.box;
104
105
var msg = create('div', 'popup-msg', message);
106
var input = create('input', 'popup-input');
107
input.type = 'text';
108
if (defaultValue !== undefined) input.value = defaultValue;
109
var btns = create('div', 'popup-btns');
110
var cancel = create('button', 'popup-btn popup-btn-cancel', '취소');
111
var ok = create('button', 'popup-btn popup-btn-ok', '확인');
112
btns.appendChild(cancel);
113
btns.appendChild(ok);
114
box.appendChild(msg);
115
box.appendChild(input);
116
box.appendChild(btns);
117
118
var done = false;
119
function finish(val) { if (done) return; done = true; destroy(overlay); resolve(val); }
120
ok.addEventListener('click', function () { finish(input.value); });
121
cancel.addEventListener('click', function () { finish(null); });
122
overlay.addEventListener('click', function (e) { if (e.target === overlay) finish(null); });
123
input.addEventListener('keydown', function (e) {
124
if (e.key === 'Enter') { e.preventDefault(); finish(input.value); }
125
if (e.key === 'Escape') { e.preventDefault(); finish(null); }
126
});
127
input.focus();
128
});
129
};
130
131
/* ── onsubmit="return confirm(...)" 대체용 자동 바인딩 ── */
132
document.addEventListener('DOMContentLoaded', function () {
133
document.querySelectorAll('form[data-confirm]').forEach(function (form) {
134
form.addEventListener('submit', function (e) {
135
e.preventDefault();
136
showConfirm(form.dataset.confirm).then(function (ok) {
137
if (ok) {
138
// 임시로 data-confirm 제거 후 submit (무한 루프 방지)
139
var msg = form.dataset.confirm;
140
delete form.dataset.confirm;
141
form.submit();
142
form.dataset.confirm = msg;
143
}
144
});
145
});
146
});
147
});
148
})();
149