cdnhost 2.6.6 → 2.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/ws_cdn.js +113 -167
- package/ws_cdnhtml.js +3 -3
- package/ws_css.css +54 -48
package/package.json
CHANGED
package/ws_cdn.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
2
|
document.addEventListener('DOMContentLoaded', function() {
|
|
3
3
|
if (window.FHLWidgetInitialized) return;
|
|
4
4
|
window.FHLWidgetInitialized = true;
|
|
5
5
|
|
|
6
6
|
const languages = [ { code: 'ko', name: '한국어' }, { code: 'en', name: 'English' }, { code: 'es', name: 'Español' }, { code: 'hi', name: 'हिन्दी' }, { code: 'ar', name: 'العربية' }, { code: 'de', name: 'Deutsch' }, { code: 'fr', name: 'Français' }, { code: 'pt', name: 'Português' }, { code: 'bn', name: 'বাংলা' }, { code: 'ja', name: '日本語' }, { code: 'ru', name: 'Русский' }, { code: 'zh', name: '简体中文' }, { code: 'th', name: 'ไทย' }, { code: 'vi', name: 'Tiếng Việt' }, { code: 'id', name: 'Indonesia' }, { code: 'tr', name: 'Türkçe' }, { code: 'ur', name: 'اردو' }];
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
let currentDisplayItems = [];
|
|
9
9
|
let currentPage = 1;
|
|
10
10
|
const ITEMS_PER_PAGE = 30;
|
|
11
11
|
let isLoadingNextPage = false;
|
|
12
|
+
let searchTimeout = null;
|
|
12
13
|
|
|
13
14
|
const widgetContainer = document.getElementById('fhl-widget-container');
|
|
14
15
|
const widgetWrapper = document.getElementById('fhl-widget-wrapper');
|
|
@@ -25,12 +26,14 @@
|
|
|
25
26
|
const languageBtn = document.getElementById('fhl-language-btn');
|
|
26
27
|
const languagePanel = document.getElementById('fhl-language-panel');
|
|
27
28
|
|
|
28
|
-
const
|
|
29
|
+
const fixedQuestionBtn = document.getElementById('fhl-fixed-question');
|
|
29
30
|
const fixedChatBtn = document.getElementById('fhl-fixed-chat');
|
|
30
31
|
const fixedAdBtn = document.getElementById('fhl-fixed-ad');
|
|
32
|
+
const fixedInfoBtn = document.getElementById('fhl-fixed-info');
|
|
31
33
|
|
|
32
34
|
let toastTimeout = null;
|
|
33
35
|
let currentLang = localStorage.getItem('fhl-widget-lang') || 'ko';
|
|
36
|
+
const API_BASE_URL = 'https://isai.kr';
|
|
34
37
|
|
|
35
38
|
const shortcutManager = {
|
|
36
39
|
key: 'fhl-custom-icons-with-expiry',
|
|
@@ -62,9 +65,7 @@
|
|
|
62
65
|
const decodedData = atob(shortcutsParam);
|
|
63
66
|
JSON.parse(decodedData);
|
|
64
67
|
localStorage.setItem(shortcutManager.key, decodedData);
|
|
65
|
-
} catch (e) {
|
|
66
|
-
console.error("Failed to parse shortcuts from URL:", e);
|
|
67
|
-
}
|
|
68
|
+
} catch (e) { console.error("Failed to parse shortcuts from URL:", e); }
|
|
68
69
|
}
|
|
69
70
|
},
|
|
70
71
|
getAsParam: () => {
|
|
@@ -77,7 +78,7 @@
|
|
|
77
78
|
const updateAllWidgetLinks = () => {
|
|
78
79
|
const paramString = shortcutManager.getAsParam();
|
|
79
80
|
if (!paramString) return;
|
|
80
|
-
const allLinks = document.querySelectorAll('.fhl-icon-display, .fhl-fixed-item');
|
|
81
|
+
const allLinks = document.querySelectorAll('#fhl-widget-container .fhl-icon-display, #fhl-widget-container .fhl-fixed-item');
|
|
81
82
|
allLinks.forEach(link => {
|
|
82
83
|
if (link.tagName === 'A') {
|
|
83
84
|
const originalHref = link.getAttribute('href');
|
|
@@ -95,194 +96,112 @@
|
|
|
95
96
|
const showToast = (message) => { clearTimeout(toastTimeout); toastElement.textContent = message; toastElement.classList.add('visible'); toastTimeout = setTimeout(() => { toastElement.classList.remove('visible'); }, 2500); };
|
|
96
97
|
|
|
97
98
|
const applyLanguage = (langCode) => {
|
|
98
|
-
if (typeof translations === 'undefined') {
|
|
99
|
-
console.error("Translations not loaded! Make sure lang.js is included correctly.");
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
99
|
+
if (typeof translations === 'undefined') { console.error("Translations not loaded! Make sure lang.js is included correctly."); return; }
|
|
102
100
|
const t = translations[langCode] || translations['en'];
|
|
103
|
-
|
|
104
101
|
document.documentElement.lang = langCode;
|
|
105
|
-
|
|
106
102
|
searchTrigger.setAttribute('aria-label', t.openSearch);
|
|
107
103
|
addToHomeBtn.setAttribute('aria-label', t.addToWidget);
|
|
108
104
|
searchClose.setAttribute('aria-label', t.closeSearch);
|
|
109
105
|
languageBtn.setAttribute('aria-label', t.changeLanguage);
|
|
110
106
|
searchAddBtn.setAttribute('aria-label', 'Copy Widget Code');
|
|
111
|
-
|
|
107
|
+
fixedQuestionBtn.setAttribute('aria-label', t.question);
|
|
112
108
|
fixedChatBtn.setAttribute('aria-label', t.characterChat);
|
|
113
109
|
fixedAdBtn.setAttribute('aria-label', t.ads);
|
|
114
|
-
|
|
110
|
+
fixedInfoBtn.setAttribute('aria-label', t.info);
|
|
115
111
|
searchInput.placeholder = t.searchPlaceholder;
|
|
116
|
-
|
|
117
112
|
initialRender();
|
|
118
113
|
};
|
|
119
114
|
|
|
120
|
-
const populateLanguagePanel = () => {
|
|
121
|
-
|
|
122
|
-
languages.forEach(lang => {
|
|
123
|
-
const langItem = document.createElement('a');
|
|
124
|
-
langItem.href = '#';
|
|
125
|
-
langItem.className = 'fhl-language-item';
|
|
126
|
-
langItem.textContent = lang.name;
|
|
127
|
-
langItem.dataset.lang = lang.code;
|
|
128
|
-
langItem.addEventListener('click', (e) => {
|
|
129
|
-
e.preventDefault();
|
|
130
|
-
e.stopPropagation();
|
|
131
|
-
const newLang = e.target.dataset.lang;
|
|
132
|
-
if (newLang !== currentLang) {
|
|
133
|
-
currentLang = newLang;
|
|
134
|
-
localStorage.setItem('fhl-widget-lang', currentLang);
|
|
135
|
-
applyLanguage(currentLang);
|
|
136
|
-
}
|
|
137
|
-
languagePanel.classList.remove('visible');
|
|
138
|
-
});
|
|
139
|
-
languagePanel.appendChild(langItem);
|
|
140
|
-
});
|
|
141
|
-
};
|
|
115
|
+
const populateLanguagePanel = () => { languagePanel.innerHTML = ''; languages.forEach(lang => { const langItem = document.createElement('a'); langItem.href = '#'; langItem.className = 'fhl-language-item'; langItem.textContent = lang.name; langItem.dataset.lang = lang.code; langItem.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); const newLang = e.target.dataset.lang; if (newLang !== currentLang) { currentLang = newLang; localStorage.setItem('fhl-widget-lang', currentLang); applyLanguage(currentLang); } languagePanel.classList.remove('visible'); }); languagePanel.appendChild(langItem); }); };
|
|
116
|
+
const getProcessedUrl = (fullUrl) => { try { const urlObj = new URL(fullUrl); return `${urlObj.protocol}//${urlObj.hostname}`; } catch (e) { return fullUrl; } };
|
|
142
117
|
|
|
143
|
-
const getProcessedUrl = (fullUrl) => {
|
|
144
|
-
try {
|
|
145
|
-
const urlObj = new URL(fullUrl);
|
|
146
|
-
return `${urlObj.protocol}//${urlObj.hostname}`;
|
|
147
|
-
} catch (e) {
|
|
148
|
-
return fullUrl;
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
118
|
const createIconItem = (item, type = 'default') => {
|
|
157
119
|
const link = document.createElement('a');
|
|
158
|
-
|
|
159
120
|
if (type === 'search') {
|
|
160
121
|
const paramString = shortcutManager.getAsParam();
|
|
161
122
|
let finalUrl = item.url;
|
|
162
|
-
if (paramString) {
|
|
163
|
-
try {
|
|
164
|
-
let newUrl = new URL(item.url);
|
|
165
|
-
const [key, value] = paramString.split('=');
|
|
166
|
-
newUrl.searchParams.set(key, value);
|
|
167
|
-
finalUrl = newUrl.toString();
|
|
168
|
-
} catch (e) { console.error('Invalid URL for search result link:', item.url); }
|
|
169
|
-
}
|
|
123
|
+
if (paramString) { try { let newUrl = new URL(item.url); const [key, value] = paramString.split('='); newUrl.searchParams.set(key, value); finalUrl = newUrl.toString(); } catch (e) { console.error('Invalid URL for search result link:', item.url); } }
|
|
170
124
|
link.href = finalUrl;
|
|
171
|
-
} else {
|
|
172
|
-
link.href = item.url;
|
|
173
|
-
}
|
|
174
|
-
|
|
125
|
+
} else { link.href = item.url; }
|
|
175
126
|
link.className = 'fhl-icon-display';
|
|
176
127
|
link.setAttribute('aria-label', item.name);
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
link.target = '_blank';
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const iconCircle = document.createElement('div');
|
|
186
|
-
iconCircle.className = 'fhl-icon-circle';
|
|
187
|
-
|
|
188
|
-
if (item.icon) {
|
|
189
|
-
const icon = document.createElement('i');
|
|
190
|
-
icon.className = `ph-bold ${item.icon}`;
|
|
191
|
-
iconCircle.appendChild(icon);
|
|
192
|
-
} else if (item.img) {
|
|
193
|
-
const customImage = document.createElement('img');
|
|
194
|
-
customImage.src = item.img;
|
|
195
|
-
customImage.alt = item.name;
|
|
196
|
-
customImage.onerror = () => { customImage.style.display = 'none'; };
|
|
197
|
-
iconCircle.appendChild(customImage);
|
|
198
|
-
} else {
|
|
199
|
-
const favicon = document.createElement('img');
|
|
200
|
-
favicon.src = `https://www.google.com/s2/favicons?sz=64&domain_url=${item.url}`;
|
|
201
|
-
favicon.alt = item.name;
|
|
202
|
-
favicon.onerror = () => { favicon.style.display = 'none'; };
|
|
203
|
-
iconCircle.appendChild(favicon);
|
|
204
|
-
}
|
|
205
|
-
|
|
128
|
+
if (type === 'search') { link.addEventListener('mousedown', () => { fetch(`${API_BASE_URL}/update_view_count.php`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url: item.url }) }).catch(console.error); }); link.target = '_blank'; }
|
|
129
|
+
const iconCircle = document.createElement('div'); iconCircle.className = 'fhl-icon-circle';
|
|
130
|
+
if (item.icon) { const icon = document.createElement('i'); icon.className = `ph-bold ${item.icon}`; iconCircle.appendChild(icon); }
|
|
131
|
+
else if (item.img) { const customImage = document.createElement('img'); customImage.src = item.img; customImage.alt = item.name; customImage.onerror = () => { customImage.style.display = 'none'; }; iconCircle.appendChild(customImage); }
|
|
132
|
+
else { const favicon = document.createElement('img'); favicon.src = `https://www.google.com/s2/favicons?sz=64&domain_url=${item.url}`; favicon.alt = item.name; favicon.onerror = () => { favicon.style.display = 'none'; }; iconCircle.appendChild(favicon); }
|
|
206
133
|
link.appendChild(iconCircle);
|
|
207
|
-
|
|
208
134
|
if (type === 'search') {
|
|
209
|
-
const tooltip = document.createElement('div');
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
link.addEventListener('mouseenter', (e) => {
|
|
214
|
-
const currentTooltip = e.currentTarget.querySelector('.fhl-item-tooltip');
|
|
215
|
-
if (!currentTooltip) return;
|
|
216
|
-
currentTooltip.classList.add('visible');
|
|
217
|
-
const widgetRect = widgetWrapper.getBoundingClientRect();
|
|
218
|
-
const tooltipRect = currentTooltip.getBoundingClientRect();
|
|
219
|
-
let offsetX = 0; const padding = 5;
|
|
220
|
-
if (tooltipRect.left < widgetRect.left) { offsetX = widgetRect.left - tooltipRect.left + padding; } else if (tooltipRect.right > widgetRect.right) { offsetX = widgetRect.right - tooltipRect.right - padding; }
|
|
221
|
-
if (offsetX !== 0) { currentTooltip.style.transform = `translateX(calc(-50% + ${offsetX}px))`; }
|
|
222
|
-
});
|
|
223
|
-
link.addEventListener('mouseleave', (e) => {
|
|
224
|
-
const currentTooltip = e.currentTarget.querySelector('.fhl-item-tooltip');
|
|
225
|
-
if (!currentTooltip) return;
|
|
226
|
-
currentTooltip.classList.remove('visible');
|
|
227
|
-
currentTooltip.style.transform = 'translateX(-50%)';
|
|
228
|
-
});
|
|
229
|
-
} else {
|
|
230
|
-
const nameSpan = document.createElement('span');
|
|
231
|
-
nameSpan.className = 'fhl-item-name';
|
|
232
|
-
nameSpan.textContent = item.name.length > 5 ? item.name.slice(0, 5) + '..' : item.name;
|
|
233
|
-
link.appendChild(nameSpan);
|
|
234
|
-
}
|
|
235
|
-
|
|
135
|
+
const tooltip = document.createElement('div'); tooltip.className = 'fhl-item-tooltip'; tooltip.textContent = item.name.length > 5 ? item.name.slice(0, 5) + '..' : item.name; link.appendChild(tooltip);
|
|
136
|
+
link.addEventListener('mouseenter', (e) => { const currentTooltip = e.currentTarget.querySelector('.fhl-item-tooltip'); if (!currentTooltip) return; currentTooltip.classList.add('visible'); const widgetRect = widgetWrapper.getBoundingClientRect(); const tooltipRect = currentTooltip.getBoundingClientRect(); let offsetX = 0; const padding = 5; if (tooltipRect.left < widgetRect.left) { offsetX = widgetRect.left - tooltipRect.left + padding; } else if (tooltipRect.right > widgetRect.right) { offsetX = widgetRect.right - tooltipRect.right - padding; } if (offsetX !== 0) { currentTooltip.style.transform = `translateX(calc(-50% + ${offsetX}px))`; } });
|
|
137
|
+
link.addEventListener('mouseleave', (e) => { const currentTooltip = e.currentTarget.querySelector('.fhl-item-tooltip'); if (!currentTooltip) return; currentTooltip.classList.remove('visible'); currentTooltip.style.transform = 'translateX(-50%)'; });
|
|
138
|
+
} else { const nameSpan = document.createElement('span'); nameSpan.className = 'fhl-item-name'; nameSpan.textContent = item.name.length > 5 ? item.name.slice(0, 5) + '..' : item.name; link.appendChild(nameSpan); }
|
|
236
139
|
if (type === 'local') {
|
|
237
|
-
const controls = document.createElement('div');
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
deleteBtn.
|
|
243
|
-
deleteBtn.addEventListener('click', (e) => {
|
|
244
|
-
e.preventDefault(); e.stopPropagation();
|
|
245
|
-
let storedItems = shortcutManager.get();
|
|
246
|
-
storedItems = storedItems.filter(stored => stored.url !== item.url);
|
|
247
|
-
shortcutManager.set(storedItems);
|
|
248
|
-
initialRender();
|
|
249
|
-
});
|
|
250
|
-
const moveBtn = document.createElement('button');
|
|
251
|
-
moveBtn.className = 'fhl-control-btn';
|
|
252
|
-
moveBtn.innerHTML = '>';
|
|
253
|
-
moveBtn.title = '오른쪽으로 이동';
|
|
254
|
-
moveBtn.addEventListener('click', (e) => {
|
|
255
|
-
e.preventDefault(); e.stopPropagation();
|
|
256
|
-
let storedItems = shortcutManager.get();
|
|
257
|
-
const currentIndex = storedItems.findIndex(stored => stored.url === item.url);
|
|
258
|
-
if (currentIndex > -1) {
|
|
259
|
-
const newIndex = (currentIndex + 1) % storedItems.length;
|
|
260
|
-
const [movedItem] = storedItems.splice(currentIndex, 1);
|
|
261
|
-
storedItems.splice(newIndex, 0, movedItem);
|
|
262
|
-
shortcutManager.set(storedItems);
|
|
263
|
-
initialRender();
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
controls.appendChild(deleteBtn);
|
|
267
|
-
controls.appendChild(moveBtn);
|
|
268
|
-
link.appendChild(controls);
|
|
140
|
+
const controls = document.createElement('div'); controls.className = 'fhl-item-controls';
|
|
141
|
+
const deleteBtn = document.createElement('button'); deleteBtn.className = 'fhl-control-btn'; deleteBtn.innerHTML = '×'; deleteBtn.title = '삭제';
|
|
142
|
+
deleteBtn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); let storedItems = shortcutManager.get(); storedItems = storedItems.filter(stored => stored.url !== item.url); shortcutManager.set(storedItems); initialRender(); });
|
|
143
|
+
const moveBtn = document.createElement('button'); moveBtn.className = 'fhl-control-btn'; moveBtn.innerHTML = '>'; moveBtn.title = '오른쪽으로 이동';
|
|
144
|
+
moveBtn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); let storedItems = shortcutManager.get(); const currentIndex = storedItems.findIndex(stored => stored.url === item.url); if (currentIndex > -1) { const newIndex = (currentIndex + 1) % storedItems.length; const [movedItem] = storedItems.splice(currentIndex, 1); storedItems.splice(newIndex, 0, movedItem); shortcutManager.set(storedItems); initialRender(); } });
|
|
145
|
+
controls.appendChild(deleteBtn); controls.appendChild(moveBtn); link.appendChild(controls);
|
|
269
146
|
}
|
|
270
147
|
return link;
|
|
271
148
|
};
|
|
272
149
|
|
|
150
|
+
const appendNextPage = () => { if (isLoadingNextPage) return; isLoadingNextPage = true; const startIndex = (currentPage - 1) * ITEMS_PER_PAGE; const endIndex = currentPage * ITEMS_PER_PAGE; if (startIndex >= currentDisplayItems.length) { isLoadingNextPage = false; return; } const itemsToAppend = currentDisplayItems.slice(startIndex, endIndex); itemsToAppend.forEach(item => { searchResultsContainer.appendChild(createIconItem(item, 'search')); }); currentPage++; isLoadingNextPage = false; };
|
|
151
|
+
|
|
152
|
+
const renderInitialResults = (items) => {
|
|
153
|
+
searchResultsContainer.innerHTML = '';
|
|
154
|
+
currentPage = 1;
|
|
155
|
+
currentDisplayItems = items;
|
|
156
|
+
appendNextPage();
|
|
157
|
+
};
|
|
273
158
|
|
|
159
|
+
const performSearch = async (query = '') => {
|
|
160
|
+
try {
|
|
161
|
+
const response = await fetch(`${API_BASE_URL}/appapi2.php?query=${encodeURIComponent(query)}`);
|
|
162
|
+
if (!response.ok) throw new Error('API 응답 오류');
|
|
163
|
+
const results = await response.json();
|
|
164
|
+
renderInitialResults(results);
|
|
165
|
+
} catch (error) {
|
|
166
|
+
console.error('앱 목록 로드 실패:', error);
|
|
167
|
+
searchResultsContainer.innerHTML = '<p>목록 로드 실패</p>';
|
|
168
|
+
}
|
|
169
|
+
};
|
|
274
170
|
|
|
171
|
+
const openSearch = () => {
|
|
172
|
+
widgetContainer.classList.add('search-mode');
|
|
173
|
+
widgetWrapper.classList.add('search-mode');
|
|
174
|
+
searchInput.focus();
|
|
175
|
+
performSearch();
|
|
176
|
+
};
|
|
275
177
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
178
|
+
const initialRender = () => {
|
|
179
|
+
const t = translations[currentLang] || translations['en'];
|
|
180
|
+
const b = [
|
|
181
|
+
{ name: t.search, url: 'https://isai.kr', icon: 'ph-sparkle' },
|
|
182
|
+
{ name: t.question, url: 'https://isai.kr/#chat', icon: 'ph-question-mark' },
|
|
183
|
+
{ name: t.characterChat, url: 'https://zoai.oduc.kr/ko/character/select', icon: 'ph-chats-circle' },
|
|
184
|
+
{ name: t.translate, url: 'https://translato.isai.kr/', icon: 'ph-translate' },
|
|
185
|
+
{ name: t.tarot, url: 'https://tarot.isai.kr/', icon: 'ph-cards' },
|
|
186
|
+
{ name: t.blog, url: 'https://blog.099.kr', icon: 'ph-article-medium' },
|
|
187
|
+
{ name: t.novel, url: 'https://ranovel.kr/', img: 'https://cdn.jsdelivr.net/npm/cdnhost@2.2.2/__ra.png' },
|
|
188
|
+
{ name: t.psychology, url: 'https://simpong.oduc.kr/', img: 'http://cdn.jsdelivr.net/npm/cdnhost@2.2.0/_simpong.png' },
|
|
189
|
+
{ name: t.forum, url: 'https://logig.im', img: 'https://cdn.jsdelivr.net/npm/cdnhost@2.3.9/_______________logig.png' },
|
|
190
|
+
{ name: t.ads, url: 'https://gig.snapp.im/', icon: 'ph-currency-circle-dollar' },
|
|
191
|
+
];
|
|
192
|
+
listContainer.innerHTML = '';
|
|
193
|
+
const c = shortcutManager.get();
|
|
194
|
+
let d = [...b];
|
|
195
|
+
d.splice(2, 0, ...c);
|
|
196
|
+
d.forEach(e => {
|
|
197
|
+
const f = c.some(g => g.url === e.url);
|
|
198
|
+
const h = f && !e.icon && !e.img ? 'local' : 'default';
|
|
199
|
+
listContainer.appendChild(createIconItem(e, h));
|
|
200
|
+
});
|
|
201
|
+
checkScrollability();
|
|
202
|
+
updateAllWidgetLinks();
|
|
203
|
+
};
|
|
204
|
+
|
|
286
205
|
const updateScrollIndicator = () => { const scrollLeft = listContainer.scrollLeft; const maxScrollLeft = listContainer.scrollWidth - listContainer.clientWidth; if (maxScrollLeft <= 0) return; const scrollFraction = scrollLeft / maxScrollLeft; const trackWidth = indicatorTrack.clientWidth; const indicatorWidth = scrollIndicator.clientWidth; const maxIndicatorLeft = trackWidth - indicatorWidth; const indicatorLeft = scrollFraction * maxIndicatorLeft; scrollIndicator.style.transform = `translateY(-50%) translateX(${indicatorLeft}px)`; };
|
|
287
206
|
const checkScrollability = () => { const isScrollable = listContainer.scrollWidth > listContainer.clientWidth; widgetContainer.classList.toggle('scrollable', isScrollable); if (isScrollable) updateScrollIndicator(); };
|
|
288
207
|
let isDragging = false;
|
|
@@ -293,17 +212,45 @@
|
|
|
293
212
|
let hideTimeout = null;
|
|
294
213
|
widgetContainer.addEventListener('mouseenter', () => { clearTimeout(hideTimeout); });
|
|
295
214
|
widgetContainer.addEventListener('mouseleave', () => { hideTimeout = setTimeout(() => { widgetContainer.classList.add('hidden'); }, 6000); });
|
|
296
|
-
|
|
215
|
+
|
|
216
|
+
document.addEventListener('click', (e) => {
|
|
217
|
+
if (!languagePanel.contains(e.target) && !languageBtn.contains(e.target)) {
|
|
218
|
+
languagePanel.classList.remove('visible');
|
|
219
|
+
}
|
|
220
|
+
if (widgetContainer.contains(e.target)) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if (widgetContainer.classList.contains('hidden')) {
|
|
224
|
+
setTimeout(() => {
|
|
225
|
+
widgetContainer.classList.remove('hidden');
|
|
226
|
+
clearTimeout(hideTimeout);
|
|
227
|
+
hideTimeout = setTimeout(() => {
|
|
228
|
+
widgetContainer.classList.add('hidden');
|
|
229
|
+
}, 6000);
|
|
230
|
+
}, 50);
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
|
|
297
234
|
listContainer.addEventListener('scroll', updateScrollIndicator);
|
|
298
235
|
window.addEventListener('resize', checkScrollability);
|
|
299
236
|
searchTrigger.addEventListener('click', openSearch);
|
|
300
237
|
languageBtn.addEventListener('click', (e) => { e.stopPropagation(); languagePanel.classList.toggle('visible'); });
|
|
301
|
-
|
|
238
|
+
|
|
239
|
+
searchInput.addEventListener('input', function() {
|
|
240
|
+
clearTimeout(searchTimeout);
|
|
241
|
+
const query = this.value.toLowerCase().trim();
|
|
242
|
+
searchTimeout = setTimeout(() => {
|
|
243
|
+
performSearch(query);
|
|
244
|
+
}, 300);
|
|
245
|
+
});
|
|
246
|
+
|
|
302
247
|
searchResultsContainer.addEventListener('scroll', () => { const isAtBottom = searchResultsContainer.scrollTop + searchResultsContainer.clientHeight >= searchResultsContainer.scrollHeight - 10; if (isAtBottom) { appendNextPage(); } });
|
|
303
248
|
searchClose.addEventListener('click', closeSearch);
|
|
304
249
|
document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && widgetWrapper.classList.contains('search-mode')) { closeSearch(); } });
|
|
305
250
|
|
|
306
|
-
|
|
251
|
+
|
|
252
|
+
const _0x3e4a = ['preventDefault', 'en', 'currentPage', 'get_title.php?url=', 'title', 'Failed to fetch title:', 'name', 'url', 'some', 'shift', 'push', 'POST', 'Content-Type', 'application/json', 'register_app.php', 'DB 등록/갱신 실패:', 'search-mode', 'alreadyAdded', 'json', 'ok', 'error', 'classList', 'contains', 'log']; (function(_0x1a8c3d, _0x3e4a3f) { const _0x58ab4a = function(_0x2e8f1c) { while (--_0x2e8f1c) { _0x1a8c3d['push'](_0x1a8c3d['shift']()); } }; _0x58ab4a(++_0x3e4a3f); }(_0x3e4a, 0x18a)); const _0x5a8d = function(_0x1a8c3d, _0x3e4a3f) { _0x1a8c3d = _0x1a8c3d - 0x0; let _0x58ab4a = _0x3e4a[_0x1a8c3d]; return _0x58ab4a; }; const addCurrentPageToWidget = async (_0x1b4c5d) => { _0x1b4c5d[_0x5a8d('0x0')](); const _0x5a7b8e = translations[currentLang] || translations[_0x5a8d('0x1')]; const _0x2a9e3f = getProcessedUrl(window.location.href); let _0x4d2a1c = document[_0x5a8d('0x4')] || _0x5a7b8e[_0x5a8d('0x2')]; try { const _0x4f12f8 = await fetch(`${API_BASE_URL}/${_0x5a8d('0x3')}${encodeURIComponent(_0x2a9e3f)}`); if (_0x4f12f8[_0x5a8d('0x13')]) { const _0x39f2c3 = await _0x4f12f8[_0x5a8d('0x12')](); if (_0x39f2c3[_0x5a8d('0x4')]) { _0x4d2a1c = _0x39f2c3[_0x5a8d('0x4')]; } } } catch (_0x5c4d5d) { console[_0x5a8d('0x14')](_0x5a8d('0x5'), _0x5c4d5d); } const _0x4d5b2e = { [_0x5a8d('0x6')]: _0x4d2a1c, [_0x5a8d('0x7')]: _0x2a9e3f }; let _0x2b8e4f = shortcutManager.get(); if (!_0x2b8e4f[_0x5a8d('0x8')]((_0x5b3e9a) => getProcessedUrl(_0x5b3e9a[_0x5a8d('0x7')]) === getProcessedUrl(_0x4d5b2e[_0x5a8d('0x7')]))) { if (_0x2b8e4f.length >= 0xa) { _0x2b8e4f[_0x5a8d('0x9')](); } _0x2b8e4f[_0x5a8d('0xa')](_0x4d5b2e); shortcutManager.set(_0x2b8e4f); try { await fetch(`${API_BASE_URL}/${_0x5a8d('0xe')}`, { 'method': _0x5a8d('0xb'), 'headers': { [_0x5a8d('0xc')]: _0x5a8d('0xd') }, 'body': JSON.stringify(_0x4d5b2e) }); } catch (_0x3e1b1b) { console[_0x5a8d('0x14')](_0x5a8d('0xf'), _0x3e1b1b); } initialRender(); if (widgetWrapper[_0x5a8d('0x15')][_0x5a8d('0x16')](_0x5a8d('0x10'))) { closeSearch(); } } else { showToast(_0x5a7b8e[_0x5a8d('0x11')]); } };
|
|
253
|
+
|
|
307
254
|
|
|
308
255
|
const copyWidgetScript = (e) => {
|
|
309
256
|
e.preventDefault();
|
|
@@ -321,5 +268,4 @@
|
|
|
321
268
|
|
|
322
269
|
populateLanguagePanel();
|
|
323
270
|
applyLanguage(currentLang);
|
|
324
|
-
widgetContainer.classList.add('hidden');
|
|
325
271
|
});
|
package/ws_cdnhtml.js
CHANGED
|
@@ -7,7 +7,7 @@ const widgetHTML = `
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
<div id="fhl-toast-notification" class="fhl-toast"></div>
|
|
10
|
-
<div
|
|
10
|
+
<div id="fhl-widget-container" class="hidden">
|
|
11
11
|
<div class="fhl-indicator-track" id="fhl-indicator-track">
|
|
12
12
|
<div class="fhl-scroll-indicator" id="fhl-scroll-indicator"></div>
|
|
13
13
|
</div>
|
|
@@ -27,10 +27,10 @@ const widgetHTML = `
|
|
|
27
27
|
<div class="fhl-search-results" id="fhl-search-results"></div>
|
|
28
28
|
<div class="fhl-fixed-panel">
|
|
29
29
|
<a href="#" id="fhl-search-add-btn" class="fhl-fixed-item"><i class="ph-bold ph-code"></i></a>
|
|
30
|
-
<a href="https://isai.kr/#chat" id="fhl-fixed-
|
|
30
|
+
<a href="https://isai.kr/#chat" id="fhl-fixed-question" class="fhl-fixed-item"><i class="ph-bold ph-question"></i></a>
|
|
31
31
|
<a href="https://zoai.oduc.kr/ko/character/select" id="fhl-fixed-chat" class="fhl-fixed-item"><i class="ph-bold ph-chats-circle"></i></a>
|
|
32
32
|
<a href="https://gig.snapp.im/" id="fhl-fixed-ad" class="fhl-fixed-item"><i class="ph-bold ph-currency-circle-dollar"></i></a>
|
|
33
|
-
<a href="https://webstore.isai.kr/" id="fhl-fixed-
|
|
33
|
+
<a href="https://webstore.isai.kr/" id="fhl-fixed-info" class="fhl-fixed-item"><i class="ph-bold ph-info"></i></a>
|
|
34
34
|
</div>
|
|
35
35
|
</div>
|
|
36
36
|
</div>
|
package/ws_css.css
CHANGED
|
@@ -1,51 +1,57 @@
|
|
|
1
1
|
|
|
2
2
|
.fhl-widget-wrapper *, .fhl-widget-wrapper *::before, .fhl-widget-wrapper *::after { box-sizing: border-box; }
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
.fhl-
|
|
12
|
-
.fhl-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
.fhl-
|
|
17
|
-
|
|
18
|
-
.fhl-icon-
|
|
19
|
-
.fhl-icon-
|
|
20
|
-
.fhl-icon-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
.fhl-list-container
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
.fhl-
|
|
27
|
-
|
|
28
|
-
.fhl-
|
|
29
|
-
|
|
30
|
-
.fhl-search-
|
|
31
|
-
.fhl-search-
|
|
32
|
-
.fhl-search-
|
|
33
|
-
.fhl-search-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
.fhl-
|
|
38
|
-
.fhl-
|
|
39
|
-
|
|
40
|
-
.fhl-
|
|
41
|
-
.fhl-
|
|
42
|
-
.fhl-item-
|
|
43
|
-
|
|
44
|
-
.fhl-
|
|
45
|
-
.fhl-
|
|
3
|
+
#fhl-widget-container {
|
|
4
|
+
position: fixed; z-index: 1000; bottom: 20px; left: 50%;
|
|
5
|
+
transform: translateX(-50%) translateY(0); display: flex; flex-direction: column; align-items: center;
|
|
6
|
+
width: calc(100% - 30px); max-width: 460px; pointer-events: auto;
|
|
7
|
+
transition: opacity 0.4s ease-in-out, transform 0.4s ease-in-out;
|
|
8
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
9
|
+
}
|
|
10
|
+
#fhl-widget-container.hidden { opacity: 0; transform: translateX(-50%) translateY(20px); pointer-events: none; }
|
|
11
|
+
#fhl-widget-container .fhl-indicator-track { position: relative; width: 100px; height: 14px; margin-bottom: 8px; cursor: pointer; background-color: transparent; transition: opacity 0.3s ease; opacity: 0; pointer-events: auto; }
|
|
12
|
+
#fhl-widget-container.scrollable .fhl-indicator-track { opacity: 1; }
|
|
13
|
+
#fhl-widget-container.search-mode .fhl-indicator-track { display: none; }
|
|
14
|
+
#fhl-widget-container .fhl-scroll-indicator { position: absolute; top: 50%; left: 0; width: 12px; height: 12px; background-color: #555; border-radius: 50%; transform: translateY(-50%); transition: transform 0.1s linear; pointer-events: none; }
|
|
15
|
+
#fhl-widget-container .fhl-widget-wrapper { position: relative; display: flex; align-items: center; width: 100%; max-width: 460px; height: 52px; padding: 7px 10px; border-radius: 26px; background-color: rgba(255, 255, 255, 0.9); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); box-shadow: 0 5px 20px rgba(0, 0, 0, 0.2); transition: all 0.3s ease-in-out; pointer-events: auto; overflow: visible; }
|
|
16
|
+
#fhl-widget-container .fhl-icon-display { position: relative; flex-shrink: 0; display: flex; justify-content: center; align-items: center; width: 38px; height: 38px; color: #333; background-color: rgba(0, 0, 0, 0.05); border: none; border-radius: 50%; text-decoration: none; cursor: pointer; padding: 0; transition: background-color 0.2s ease; }
|
|
17
|
+
#fhl-widget-container .fhl-icon-display:hover { background-color: rgba(0, 0, 0, 0.1); }
|
|
18
|
+
#fhl-widget-container .fhl-icon-display i { font-size: 22px; }
|
|
19
|
+
#fhl-widget-container .fhl-list-container .fhl-icon-display { display: inline-flex; width: auto; padding: 0 8px 0 0; border-radius: 20px; background-color: transparent; }
|
|
20
|
+
#fhl-widget-container .fhl-list-container .fhl-icon-display:hover { background-color: rgba(0, 0, 0, 0.08); }
|
|
21
|
+
#fhl-widget-container .fhl-list-container .fhl-icon-display:hover .fhl-icon-circle { background-color: transparent; }
|
|
22
|
+
#fhl-widget-container .fhl-item-name { font-size: 14px; font-weight: 500; white-space: nowrap; overflow: hidden; max-width: 0; opacity: 0; transition: max-width 0.3s ease-in-out, opacity 0.2s ease-in-out 0.05s; }
|
|
23
|
+
#fhl-widget-container .fhl-list-container .fhl-icon-display:hover .fhl-item-name { max-width: 150px; opacity: 1; }
|
|
24
|
+
#fhl-widget-container .fhl-icon-circle { flex-shrink: 0; display: flex; justify-content: center; align-items: center; width: 38px; height: 38px; background-color: rgba(0, 0, 0, 0.05); border-radius: 50%; transition: background-color 0.2s ease; overflow: hidden; }
|
|
25
|
+
#fhl-widget-container .fhl-icon-circle img { width: 24px; height: 24px; object-fit: cover; border-radius: 50%; }
|
|
26
|
+
#fhl-widget-container .fhl-icon-bar { display: flex; align-items: center; gap: 8px; width: 100%; transition: opacity 0.3s ease; }
|
|
27
|
+
#fhl-widget-container .fhl-list-container { padding:0 8px; flex-grow: 1; display: flex; align-items: center; gap: 6px; overflow-x: auto; -ms-overflow-style: none; scrollbar-width: none; scroll-snap-type: x mandatory; mask-image: linear-gradient(to right, transparent, black 20px, black calc(100% - 20px), transparent); -webkit-mask-image: linear-gradient(to right, transparent, black 20px, black calc(100% - 20px), transparent); padding-bottom: 30px; margin-bottom: -30px; overflow-y: visible; }
|
|
28
|
+
#fhl-widget-container .fhl-list-container::-webkit-scrollbar { display: none; }
|
|
29
|
+
#fhl-widget-container .fhl-list-container > .fhl-icon-display { scroll-snap-align: center; }
|
|
30
|
+
#fhl-widget-container .fhl-widget-wrapper.search-mode { height: 280px; align-items: flex-start; border-radius: 20px; }
|
|
31
|
+
#fhl-widget-container .fhl-widget-wrapper.search-mode .fhl-icon-bar { opacity: 0; pointer-events: none; }
|
|
32
|
+
#fhl-widget-container .fhl-search-view { position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: flex; flex-direction: column; padding: 10px; opacity: 0; visibility: hidden; transition: opacity 0.3s 0.1s ease; }
|
|
33
|
+
#fhl-widget-container .fhl-widget-wrapper.search-mode .fhl-search-view { opacity: 1; visibility: visible; }
|
|
34
|
+
#fhl-widget-container .fhl-search-input-wrapper { display: flex; align-items: center; flex-shrink: 0; margin-bottom: 10px; }
|
|
35
|
+
#fhl-widget-container .fhl-search-input { flex-grow: 1; height: 38px; border: none; background: transparent; font-size: 16px; color: #111; padding: 0 10px; outline: none; border-bottom: 2px solid #ccc; transition: border-color 0.2s ease; }
|
|
36
|
+
#fhl-widget-container .fhl-search-input:focus { border-bottom-color: #007bff; }
|
|
37
|
+
#fhl-widget-container .fhl-search-content { display: flex; flex-grow: 1; overflow: hidden; }
|
|
38
|
+
#fhl-widget-container .fhl-search-results { flex-grow: 1; width: 0; overflow-y: auto; display: flex; flex-wrap: wrap; justify-content: flex-start; align-content: flex-start; gap: 12px; padding: 15px; -ms-overflow-style: none; scrollbar-width: none; padding-bottom: 40px; }
|
|
39
|
+
#fhl-widget-container .fhl-search-results::-webkit-scrollbar { display: none; }
|
|
40
|
+
#fhl-widget-container .fhl-search-results .fhl-icon-display { background-color: transparent; }
|
|
41
|
+
#fhl-widget-container .fhl-search-results .fhl-icon-display:hover .fhl-icon-circle { background-color: rgba(0, 0, 0, 0.1); }
|
|
42
|
+
#fhl-widget-container .fhl-item-tooltip { position: absolute; bottom: -28px; left: 50%; transform: translateX(-50%); background-color: #333; color: #fff; padding: 3px 6px; border-radius: 5px; font-size: 11px; font-weight: 500; white-space: nowrap; opacity: 0; visibility: hidden; pointer-events: none; transition: opacity 0.2s ease-in-out, bottom 0.2s ease-in-out; z-index: 10; }
|
|
43
|
+
#fhl-widget-container .fhl-item-tooltip.visible { opacity: 1; visibility: visible; bottom: -32px; }
|
|
44
|
+
#fhl-widget-container .fhl-fixed-panel { flex-shrink: 0; width: 70px; height: 100%; padding: 0; border-left: 1px solid rgba(0,0,0,0.1); display: flex; flex-direction: column; justify-content: space-around; align-items: center; }
|
|
45
|
+
#fhl-widget-container .fhl-fixed-item { display: flex; align-items: center; justify-content: center; width: 42px; height: 42px; border-radius: 10px; text-decoration: none; color: #444; cursor: pointer; transition: background-color 0.2s ease; }
|
|
46
|
+
#fhl-widget-container .fhl-fixed-item:hover { background-color: rgba(0, 0, 0, 0.08); }
|
|
47
|
+
#fhl-widget-container .fhl-fixed-item i { font-size: 24px; }
|
|
48
|
+
#fhl-widget-container .fhl-item-controls { position: absolute; bottom: -20px; left: 50%; transform: translateX(-50%); display: flex; gap: 4px; background-color: rgba(0,0,0,0.7); border-radius: 10px; padding: 2px 4px; opacity: 0; visibility: hidden; transition: all 0.2s ease; pointer-events: none; z-index: 20; }
|
|
49
|
+
#fhl-widget-container .fhl-list-container .fhl-icon-display:hover .fhl-item-controls { opacity: 1; visibility: visible; pointer-events: auto; bottom: -13px; }
|
|
50
|
+
#fhl-widget-container .fhl-control-btn { background: none; border: none; color: white; cursor: pointer; font-size: 16px; line-height: 1; padding: 2px; font-family: monospace; }
|
|
51
|
+
#fhl-widget-container .fhl-control-btn:hover { color: #007bff; }
|
|
52
|
+
#fhl-widget-container .fhl-language-panel { position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); margin-bottom: 15px; background-color: rgba(255, 255, 255, 0.95); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.2); padding: 8px; display: grid; grid-template-columns: 1fr 1fr; gap: 4px; opacity: 0; visibility: hidden; pointer-events: none; transition: opacity 0.3s ease, transform 0.3s ease; transform-origin: bottom center; }
|
|
53
|
+
#fhl-widget-container .fhl-language-panel.visible { opacity: 1; visibility: visible; pointer-events: auto; }
|
|
54
|
+
#fhl-widget-container .fhl-language-item { display: block; padding: 6px 12px; font-size: 14px; color: #333; text-decoration: none; border-radius: 8px; transition: background-color 0.2s ease; cursor: pointer; text-align: center; }
|
|
55
|
+
#fhl-widget-container .fhl-language-item:hover { background-color: rgba(0,0,0,0.1); }
|
|
46
56
|
.fhl-toast { position: fixed; bottom: 90px; left: 50%; transform: translateX(-50%) translateY(10px); background-color: rgba(0, 0, 0, 0.8); color: #fff; padding: 8px 16px; border-radius: 18px; font-size: 14px; z-index: 1001; opacity: 0; visibility: hidden; pointer-events: none; transition: opacity 0.3s ease, transform 0.3s ease, visibility 0.3s; }
|
|
47
|
-
.fhl-toast.visible { opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0); }
|
|
48
|
-
.fhl-language-panel { position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); margin-bottom: 15px; background-color: rgba(255, 255, 255, 0.95); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.2); padding: 8px; display: grid; grid-template-columns: 1fr 1fr; gap: 4px; opacity: 0; visibility: hidden; pointer-events: none; transition: opacity 0.3s ease, transform 0.3s ease; transform-origin: bottom center; }
|
|
49
|
-
.fhl-language-panel.visible { opacity: 1; visibility: visible; pointer-events: auto; }
|
|
50
|
-
.fhl-language-item { display: block; padding: 6px 12px; font-size: 14px; color: #333; text-decoration: none; border-radius: 8px; transition: background-color 0.2s ease; cursor: pointer; text-align: center; }
|
|
51
|
-
.fhl-language-item:hover { background-color: rgba(0,0,0,0.1); }
|
|
57
|
+
.fhl-toast.visible { opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0); }
|