cdnhost 2.8.7 → 2.9.0
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/_1.png +0 -0
- package/__________box.png +0 -0
- package/box.png +0 -0
- package/iwinv - /353/263/265/354/202/254/353/263/270.js" +52 -0
- package/iwinv.js +46 -117
- package/iwinv_new.js +48 -102
- package/link.js +12 -13
- package/package.json +1 -1
- package/seekr.js +45 -58
- package/seekr2.js +45 -58
- package/test.php +1 -0
- package/tistory.js +76 -0
- package/total.js +45 -57
- package/wordpress-chat.js +45 -58
- package/wp-pc-pop.js +45 -58
- package/ws_cdn.js +1 -2
- package/ws_cdnhtml.js +4 -4
- package/ws_cdn - /353/263/265/354/202/254/353/263/270.js" +0 -7
package/ws_cdn.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
© 2025 webstore. All Rights Reserved.
|
|
3
3
|
Unauthorized copying, modification, or redistribution is strictly prohibited.
|
|
4
|
-
무단 복제, 수정, 배포를 금지합니다.
|
|
5
4
|
*/
|
|
6
5
|
|
|
7
|
-
document.addEventListener('DOMContentLoaded', function() { if (window.FHLWidgetInitialized) return; window.FHLWidgetInitialized = true; 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: 'اردو' }]; let currentDisplayItems = []; let currentPage = 1; const ITEMS_PER_PAGE = 30; let isLoadingNextPage = false; let searchTimeout = null; const widgetContainer = document.getElementById('fhl-widget-container'); const widgetWrapper = document.getElementById('fhl-widget-wrapper'); const listContainer = document.getElementById('fhl-list-container'); const searchInput = document.getElementById('fhl-search-input'); const searchResultsContainer = document.getElementById('fhl-search-results'); const searchTrigger = document.getElementById('fhl-search-trigger'); const searchClose = document.getElementById('fhl-search-close'); const indicatorTrack = document.getElementById('fhl-indicator-track'); const scrollIndicator = document.getElementById('fhl-scroll-indicator'); const addToHomeBtn = document.getElementById('fhl-add-to-home-btn'); const searchAddBtn = document.getElementById('fhl-search-add-btn'); const toastElement = document.getElementById('fhl-toast-notification'); const languageBtn = document.getElementById('fhl-language-btn'); const languagePanel = document.getElementById('fhl-language-panel'); const fixedQuestionBtn = document.getElementById('fhl-fixed-question'); const fixedChatBtn = document.getElementById('fhl-fixed-chat'); const fixedAdBtn = document.getElementById('fhl-fixed-ad'); const fixedInfoBtn = document.getElementById('fhl-fixed-info'); let toastTimeout = null; let currentLang = localStorage.getItem('fhl-widget-lang') || 'ko'; const API_BASE_URL = 'https://isai.kr'; const shortcutManager = { key: 'fhl-custom-icons-with-expiry', get: () => { const rawData = localStorage.getItem(shortcutManager.key); if (!rawData) return []; try { const data = JSON.parse(rawData); const thirtyDaysInMs = 30 * 24 * 60 * 60 * 1000; if (Date.now() - data.timestamp > thirtyDaysInMs) { localStorage.removeItem(shortcutManager.key); return []; } return Array.isArray(data.shortcuts) ? data.shortcuts.filter(item => item && item.name && item.url) : []; } catch (e) { localStorage.removeItem(shortcutManager.key); return []; } }, set: (shortcuts) => { const dataToStore = { timestamp: Date.now(), shortcuts: shortcuts }; localStorage.setItem(shortcutManager.key, JSON.stringify(dataToStore)); }, syncFromURL: () => { const params = new URLSearchParams(window.location.search); const shortcutsParam = params.get('shortcuts'); if (shortcutsParam) { try { const decodedData = decodeURIComponent(shortcutsParam); JSON.parse(decodedData); localStorage.setItem(shortcutManager.key, decodedData); } catch (e) { console.error("Failed to parse shortcuts from URL:", e)} } }, getAsParam: () => { const rawData = localStorage.getItem(shortcutManager.key); if (!rawData) return ''; return `shortcuts=${encodeURIComponent(rawData)}`; } }; const updateAllWidgetLinks = () => { const paramString = shortcutManager.getAsParam(); if (!paramString) return; const allLinks = document.querySelectorAll('#fhl-widget-container .fhl-icon-display, #fhl-widget-container .fhl-fixed-item'); allLinks.forEach(link => { if (link.tagName === 'A') { const originalHref = link.getAttribute('href'); if (!originalHref || originalHref.startsWith('#')) return; try { let newUrl = new URL(originalHref); const [key, value] = paramString.split('='); newUrl.searchParams.set(key, value); link.href = newUrl.toString(); link.rel = 'nofollow'; } catch (e) { console.error('Invalid URL for link update:', originalHref)} } }); }; const showToast = (message) => { clearTimeout(toastTimeout); toastElement.textContent = message; toastElement.classList.add('visible'); toastTimeout = setTimeout(() => { toastElement.classList.remove('visible')}, 2500)}; const applyLanguage = (langCode) => { if (typeof translations === 'undefined') { console.error("Translations not loaded! Make sure lang.js is included correctly."); return} const t = translations[langCode] || translations['en']; document.documentElement.lang = langCode; searchTrigger.setAttribute('aria-label', t.openSearch); addToHomeBtn.setAttribute('aria-label', t.addToWidget); searchClose.setAttribute('aria-label', t.closeSearch); languageBtn.setAttribute('aria-label', t.changeLanguage); searchAddBtn.setAttribute('aria-label', 'Copy Widget Code'); fixedQuestionBtn.setAttribute('aria-label', t.question); fixedChatBtn.setAttribute('aria-label', t.characterChat); fixedAdBtn.setAttribute('aria-label', t.ads); fixedInfoBtn.setAttribute('aria-label', t.info); searchInput.placeholder = t.searchPlaceholder; initialRender(); }; 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)})}; const getProcessedUrl = (fullUrl) => { try { const urlObj = new URL(fullUrl); return `${urlObj.protocol}//${urlObj.hostname}`} catch (e) { return fullUrl} }; const createIconItem = (item, type = 'default') => { if (!item || !item.name || !item.url) return document.createDocumentFragment(); const link = document.createElement('a'); if (type === 'search') { const paramString = shortcutManager.getAsParam(); let finalUrl = item.url; 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)} } link.href = finalUrl; } else { link.href = item.url} link.className = 'fhl-icon-display'; link.rel = 'nofollow'; link.setAttribute('aria-label', item.name); 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'} const iconCircle = document.createElement('div'); iconCircle.className = 'fhl-icon-circle'; if (item.icon) { const icon = document.createElement('i'); icon.className = `ph-bold ${item.icon}`; iconCircle.appendChild(icon)} 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)} 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)} link.appendChild(iconCircle); if (type === 'search') { 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); 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))`} }); link.addEventListener('mouseleave', (e) => { const currentTooltip = e.currentTarget.querySelector('.fhl-item-tooltip'); if (!currentTooltip) return; currentTooltip.classList.remove('visible'); currentTooltip.style.transform = 'translateX(-50%)'}); } 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)} if (type === 'local') { const controls = document.createElement('div'); controls.className = 'fhl-item-controls'; const deleteBtn = document.createElement('button'); deleteBtn.className = 'fhl-control-btn'; deleteBtn.innerHTML = '×'; deleteBtn.title = '삭제'; deleteBtn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); let storedItems = shortcutManager.get(); storedItems = storedItems.filter(stored => stored.url !== item.url); shortcutManager.set(storedItems); initialRender()}); const moveBtn = document.createElement('button'); moveBtn.className = 'fhl-control-btn'; moveBtn.innerHTML = '>'; moveBtn.title = '오른쪽으로 이동'; 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()} }); controls.appendChild(deleteBtn); controls.appendChild(moveBtn); link.appendChild(controls); } return link; }; 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}; const renderInitialResults = (items) => { searchResultsContainer.innerHTML = ''; currentPage = 1; currentDisplayItems = Array.isArray(items) ? items : []; appendNextPage(); }; const performSearch = async (query = '') => { try { const response = await fetch(`${API_BASE_URL}/appapi2.php?query=${encodeURIComponent(query)}`); if (!response.ok) throw new Error('API 응답 오류'); const results = await response.json(); renderInitialResults(results); } catch (error) { console.error('앱 목록 로드 실패:', error); searchResultsContainer.innerHTML = '<p>목록 로드 실패</p>'; } }; const openSearch = () => { widgetContainer.classList.add('search-mode'); widgetWrapper.classList.add('search-mode'); searchInput.focus(); performSearch()}; const initialRender = () => { const a = translations[currentLang] || translations['en']; const b = [ { name: a.search, url: 'https://isai.kr', icon: 'ph-sparkle' }, { name: a.question, url: 'https://isai.kr/#chat', icon: 'ph-question-mark' }, { name: a.blog, url: 'https://blog.099.kr', icon: 'ph-article-medium' }, { name: a.characterChat, url: 'https://lai.oduc.kr/', img: 'https://cdn.jsdelivr.net/npm/cdnhost@2.7.6/lai48.png' }, { name: a.translate, url: 'https://translato.isai.kr/', icon: 'ph-translate' }, { name: a.tarot, url: 'https://tarot.isai.kr/', icon: 'ph-cards' }, { name: '라노벨', url: 'https://ranovel.kr/', img: 'https://cdn.jsdelivr.net/npm/cdnhost@2.2.2/__ra.png' }, { name: a.psychology, url: 'https://simpong.oduc.kr/', img: '//cdn.jsdelivr.net/npm/cdnhost@2.2.0/_simpong.png' }, { name: a.forum, url: 'https://logig.im', img: 'https://cdn.jsdelivr.net/npm/cdnhost@2.3.9/_______________logig.png' }, { name: a.ads, url: 'https://gig.snapp.im/', icon: 'ph-currency-circle-dollar' }, ]; listContainer.innerHTML = '<button class="fhl-icon-circle" style="margin: 0 8px 0 0;border:0" id="fhl-edit-mode-btn"><i class="ph-bold ph-pencil-simple"></i></button>'; const editModeBtn = document.getElementById('fhl-edit-mode-btn'); if (editModeBtn) { if (listContainer.classList.contains('edit-mode')) { editModeBtn.classList.add('active'); } editModeBtn.addEventListener('click', () => { listContainer.classList.toggle('edit-mode'); editModeBtn.classList.toggle('active'); }); } const c = shortcutManager.get(); let d = [...b]; d.splice(2, 0, ...c); const fragment = document.createDocumentFragment(); d.forEach(e => { const f = c.some(g => g.url === e.url); const h = f && !e.icon && !e.img ? 'local' : 'default'; fragment.appendChild(createIconItem(e, h)); }); listContainer.appendChild(fragment); checkScrollability(); updateAllWidgetLinks(); }; 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)`}; const checkScrollability = () => { const isScrollable = listContainer.scrollWidth > listContainer.clientWidth; widgetContainer.classList.toggle('scrollable', isScrollable); if (isScrollable) updateScrollIndicator()}; let isDragging = false; const handleDragMove = (e) => { if (!isDragging) return; e.preventDefault(); const trackRect = indicatorTrack.getBoundingClientRect(); const maxScrollLeft = listContainer.scrollWidth - listContainer.clientWidth; let positionRatio = (e.clientX - trackRect.left) / trackRect.width; positionRatio = Math.max(0, Math.min(1, positionRatio)); listContainer.scrollLeft = positionRatio * maxScrollLeft}; const handleDragEnd = () => { if (!isDragging) return; isDragging = false; document.removeEventListener('mousemove', handleDragMove); document.removeEventListener('mouseup', handleDragEnd)}; indicatorTrack.addEventListener('mousedown', (e) => { isDragging = true; handleDragMove(e); document.addEventListener('mousemove', handleDragMove); document.addEventListener('mouseup', handleDragEnd)}); const closeSearch = () => { widgetContainer.classList.remove('search-mode'); widgetWrapper.classList.remove('search-mode'); searchInput.value = ''; searchResultsContainer.innerHTML = ''}; let hideTimeout = null; widgetContainer.addEventListener('mouseenter', () => { clearTimeout(hideTimeout)}); widgetContainer.addEventListener('mouseleave', () => { hideTimeout = setTimeout(() => { widgetContainer.classList.add('hidden')}, 6000)}); document.addEventListener('click', (e) => { if (!languagePanel.contains(e.target) && !languageBtn.contains(e.target)) { languagePanel.classList.remove('visible')} if (widgetContainer.contains(e.target)) { return} if (widgetContainer.classList.contains('hidden')) { setTimeout(() => { widgetContainer.classList.remove('hidden'); clearTimeout(hideTimeout); hideTimeout = setTimeout(() => { widgetContainer.classList.add('hidden')}, 6000)}, 50)} }); listContainer.addEventListener('scroll', updateScrollIndicator); window.addEventListener('resize', checkScrollability); searchTrigger.addEventListener('click', openSearch); languageBtn.addEventListener('click', (e) => { e.stopPropagation(); languagePanel.classList.toggle('visible')}); searchInput.addEventListener('input', function() { clearTimeout(searchTimeout); const query = this.value.toLowerCase().trim(); searchTimeout = setTimeout(() => { performSearch(query); }, 300); }); searchResultsContainer.addEventListener('scroll', () => { const isAtBottom = searchResultsContainer.scrollTop + searchResultsContainer.clientHeight >= searchResultsContainer.scrollHeight - 10; if (isAtBottom) { appendNextPage()} }); searchClose.addEventListener('click', closeSearch); document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && widgetWrapper.classList.contains('search-mode')) { closeSearch()} }); const addCurrentPageToWidget = async (e) => { e.preventDefault(); const t = translations[currentLang] || translations['en']; const baseUrl = getProcessedUrl(window.location.href); let iconName = document.title || t.currentPage; try { const response = await fetch(`${API_BASE_URL}/get_title.php?url=${encodeURIComponent(baseUrl)}`); if (response.ok) { const data = await response.json(); if (data.title) { iconName = data.title} } } catch (error) { console.error('Failed to fetch title:', error)} const newIcon = { name: iconName, url: baseUrl }; let storedItems = shortcutManager.get(); const isAlreadyAdded = storedItems.some(item => getProcessedUrl(item.url) === getProcessedUrl(newIcon.url)); if (!isAlreadyAdded) { if (storedItems.length >= 10) { storedItems.shift()} storedItems.push(newIcon); shortcutManager.set(storedItems); try { await fetch(`${API_BASE_URL}/register_app.php`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(newIcon) })} catch (error) { console.error('DB 등록/갱신 실패:', error)} initialRender(); if (widgetWrapper.classList.contains('search-mode')) { closeSearch()} } else { showToast(t.alreadyAdded); } }; const copyWidgetScript = (e) => { e.preventDefault(); const textToCopy = '<script src="https://unpkg.com/@phosphor-icons/web"><\/script><script src="https://cdn.jsdelivr.net/npm/cdnhost@latest/ws_lg.js"><\/script><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/cdnhost@latest/ws_css.css"><div id="app"><\/div><script src="https://cdn.jsdelivr.net/npm/cdnhost@latest/ws_cdnhtml.js"><\/script> <script src="https://cdn.jsdelivr.net/npm/cdnhost@latest/ws_cdn.js"><\/script>'; const t = translations[currentLang] || translations['en']; const fallbackCopy = () => { const textArea = document.createElement("textarea"); textArea.value = textToCopy; textArea.style.position = "fixed"; textArea.style.top = 0; textArea.style.left = "-9999px"; document.body.appendChild(textArea); textArea.focus(); textArea.select(); try { document.execCommand('copy'); showToast(t.codeCopied)} catch (err) { console.error('Fallback: Oops, unable to copy', err)} document.body.removeChild(textArea)}; if (navigator.clipboard && window.isSecureContext) { navigator.clipboard.writeText(textToCopy).then(() => { showToast(t.codeCopied)}).catch(() => { fallbackCopy()})} else { fallbackCopy()} }; addToHomeBtn.addEventListener('click', addCurrentPageToWidget); searchAddBtn.addEventListener('click', copyWidgetScript); shortcutManager.syncFromURL(); shortcutManager.get(); populateLanguagePanel(); applyLanguage(currentLang); });
|
|
6
|
+
eval(decodeURIComponent(escape(window.atob('ICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCBmdW5jdGlvbigpIHsNCiAgICAgICAgaWYgKHdpbmRvdy5GSExXaWRnZXRJbml0aWFsaXplZCkgcmV0dXJuOw0KICAgICAgICB3aW5kb3cuRkhMV2lkZ2V0SW5pdGlhbGl6ZWQgPSB0cnVlOw0KDQogICAgICAgIGNvbnN0IGxhbmd1YWdlcyA9IFsgeyBjb2RlOiAnZW4nLCBuYW1lOiAnRW5nbGlzaCcgfSwgeyBjb2RlOiAna28nLCBuYW1lOiAn7ZWc6rWt7Ja0JyB9LCB7IGNvZGU6ICdlcycsIG5hbWU6ICdFc3Bhw7FvbCcgfSwgeyBjb2RlOiAnaGknLCBuYW1lOiAn4KS54KS/4KSo4KWN4KSm4KWAJyB9LCB7IGNvZGU6ICdhcicsIG5hbWU6ICfYp9mE2LnYsdio2YrYqScgfSwgeyBjb2RlOiAnZGUnLCBuYW1lOiAnRGV1dHNjaCcgfSwgeyBjb2RlOiAnZnInLCBuYW1lOiAnRnJhbsOnYWlzJyB9LCB7IGNvZGU6ICdwdCcsIG5hbWU6ICdQb3J0dWd1w6pzJyB9LCB7IGNvZGU6ICdibicsIG5hbWU6ICfgpqzgpr7gpoLgprLgpr4nIH0sIHsgY29kZTogJ2phJywgbmFtZTogJ+aXpeacrOiqnicgfSwgeyBjb2RlOiAncnUnLCBuYW1lOiAn0KDRg9GB0YHQutC40LknIH0sIHsgY29kZTogJ3poJywgbmFtZTogJ+eugOS9k+S4reaWhycgfSwgeyBjb2RlOiAndGgnLCBuYW1lOiAn4LmE4LiX4LiiJyB9LCB7IGNvZGU6ICd2aScsIG5hbWU6ICdUaeG6v25nIFZp4buHdCcgfSwgeyBjb2RlOiAnaWQnLCBuYW1lOiAnSW5kb25lc2lhJyB9LCB7IGNvZGU6ICd0cicsIG5hbWU6ICdUw7xya8OnZScgfSwgeyBjb2RlOiAndXInLCBuYW1lOiAn2KfYsdiv2YgnIH1dOw0KICAgICAgICBsZXQgY3VycmVudERpc3BsYXlJdGVtcyA9IFtdOyANCiAgICAgICAgbGV0IGN1cnJlbnRQYWdlID0gMTsgDQogICAgICAgIGNvbnN0IElURU1TX1BFUl9QQUdFID0gMzA7DQogICAgICAgIGxldCBpc0xvYWRpbmdOZXh0UGFnZSA9IGZhbHNlOyANCiAgICAgICAgbGV0IHNlYXJjaFRpbWVvdXQgPSBudWxsOw0KDQogICAgICAgIGNvbnN0IHdpZGdldENvbnRhaW5lciA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdmaGwtd2lkZ2V0LWNvbnRhaW5lcicpOw0KICAgICAgICBjb25zdCB3aWRnZXRXcmFwcGVyID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2ZobC13aWRnZXQtd3JhcHBlcicpOw0KICAgICAgICBjb25zdCBsaXN0Q29udGFpbmVyID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2ZobC1saXN0LWNvbnRhaW5lcicpOw0KICAgICAgICBjb25zdCBzZWFyY2hJbnB1dCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdmaGwtc2VhcmNoLWlucHV0Jyk7DQogICAgICAgIGNvbnN0IHNlYXJjaFJlc3VsdHNDb250YWluZXIgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZmhsLXNlYXJjaC1yZXN1bHRzJyk7DQogICAgICAgIGNvbnN0IHNlYXJjaFRyaWdnZXIgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZmhsLXNlYXJjaC10cmlnZ2VyJyk7DQogICAgICAgIGNvbnN0IHNlYXJjaENsb3NlID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2ZobC1zZWFyY2gtY2xvc2UnKTsNCiAgICAgICAgY29uc3QgaW5kaWNhdG9yVHJhY2sgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZmhsLWluZGljYXRvci10cmFjaycpOw0KICAgICAgICBjb25zdCBzY3JvbGxJbmRpY2F0b3IgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZmhsLXNjcm9sbC1pbmRpY2F0b3InKTsNCiAgICAgICAgY29uc3QgYWRkVG9Ib21lQnRuID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2ZobC1hZGQtdG8taG9tZS1idG4nKTsNCiAgICAgICAgY29uc3Qgc2VhcmNoQWRkQnRuID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2ZobC1zZWFyY2gtYWRkLWJ0bicpOw0KICAgICAgICBjb25zdCB0b2FzdEVsZW1lbnQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZmhsLXRvYXN0LW5vdGlmaWNhdGlvbicpOw0KICAgICAgICBjb25zdCBsYW5ndWFnZUJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdmaGwtbGFuZ3VhZ2UtYnRuJyk7DQogICAgICAgIGNvbnN0IGxhbmd1YWdlUGFuZWwgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZmhsLWxhbmd1YWdlLXBhbmVsJyk7DQogICAgICAgIGNvbnN0IGZpeGVkUXVlc3Rpb25CdG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZmhsLWZpeGVkLXF1ZXN0aW9uJyk7DQogICAgICAgIGNvbnN0IGZpeGVkQ2hhdEJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdmaGwtZml4ZWQtY2hhdCcpOw0KICAgICAgICBjb25zdCBmaXhlZEFkQnRuID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2ZobC1maXhlZC1hZCcpOw0KICAgICAgICBjb25zdCBmaXhlZEluZm9CdG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZmhsLWZpeGVkLWluZm8nKTsNCiAgICAgICAgDQogICAgICAgIGxldCB0b2FzdFRpbWVvdXQgPSBudWxsOw0KICAgICAgICBsZXQgY3VycmVudExhbmcgPSBsb2NhbFN0b3JhZ2UuZ2V0SXRlbSgnZmhsLXdpZGdldC1sYW5nJykgfHwgJ2VuJzsNCiAgICAgICAgY29uc3QgQVBJX0JBU0VfVVJMID0gJ2h0dHBzOi8vaXNhaS5rcic7DQoNCiAgICAgICAgY29uc3Qgc2hvcnRjdXRNYW5hZ2VyID0gew0KICAgICAgICAgICAga2V5OiAnZmhsLWN1c3RvbS1pY29ucy13aXRoLWV4cGlyeScsDQogICAgICAgICAgICBnZXQ6ICgpID0+IHsNCiAgICAgICAgICAgICAgICBjb25zdCByYXdEYXRhID0gbG9jYWxTdG9yYWdlLmdldEl0ZW0oc2hvcnRjdXRNYW5hZ2VyLmtleSk7DQogICAgICAgICAgICAgICAgaWYgKCFyYXdEYXRhKSByZXR1cm4gW107DQogICAgICAgICAgICAgICAgdHJ5IHsNCiAgICAgICAgICAgICAgICAgICAgY29uc3QgZGF0YSA9IEpTT04ucGFyc2UocmF3RGF0YSk7DQogICAgICAgICAgICAgICAgICAgIGNvbnN0IHRoaXJ0eURheXNJbk1zID0gMzAgKiAyNCAqIDYwICogNjAgKiAxMDAwOw0KICAgICAgICAgICAgICAgICAgICBpZiAoRGF0ZS5ub3coKSAtIGRhdGEudGltZXN0YW1wID4gdGhpcnR5RGF5c0luTXMpIHsNCiAgICAgICAgICAgICAgICAgICAgICAgIGxvY2FsU3RvcmFnZS5yZW1vdmVJdGVtKHNob3J0Y3V0TWFuYWdlci5rZXkpOw0KICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFtdOw0KICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgIHJldHVybiBBcnJheS5pc0FycmF5KGRhdGEuc2hvcnRjdXRzKSA/IGRhdGEuc2hvcnRjdXRzLmZpbHRlcihpdGVtID0+IGl0ZW0gJiYgaXRlbS5uYW1lICYmIGl0ZW0udXJsKSA6IFtdOw0KICAgICAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHsNCiAgICAgICAgICAgICAgICAgICAgbG9jYWxTdG9yYWdlLnJlbW92ZUl0ZW0oc2hvcnRjdXRNYW5hZ2VyLmtleSk7DQogICAgICAgICAgICAgICAgICAgIHJldHVybiBbXTsNCiAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9LA0KICAgICAgICAgICAgc2V0OiAoc2hvcnRjdXRzKSA9PiB7DQogICAgICAgICAgICAgICAgY29uc3QgZGF0YVRvU3RvcmUgPSB7IHRpbWVzdGFtcDogRGF0ZS5ub3coKSwgc2hvcnRjdXRzOiBzaG9ydGN1dHMgfTsNCiAgICAgICAgICAgICAgICBsb2NhbFN0b3JhZ2Uuc2V0SXRlbShzaG9ydGN1dE1hbmFnZXIua2V5LCBKU09OLnN0cmluZ2lmeShkYXRhVG9TdG9yZSkpOw0KICAgICAgICAgICAgfSwNCiAgICAgICAgICAgIHN5bmNGcm9tVVJMOiAoKSA9PiB7DQogICAgICAgICAgICAgICAgY29uc3QgcGFyYW1zID0gbmV3IFVSTFNlYXJjaFBhcmFtcyh3aW5kb3cubG9jYXRpb24uc2VhcmNoKTsNCiAgICAgICAgICAgICAgICBjb25zdCBzaG9ydGN1dHNQYXJhbSA9IHBhcmFtcy5nZXQoJ3Nob3J0Y3V0cycpOw0KICAgICAgICAgICAgICAgIGlmIChzaG9ydGN1dHNQYXJhbSkgew0KICAgICAgICAgICAgICAgICAgICB0cnkgew0KICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgZGVjb2RlZERhdGEgPSBkZWNvZGVVUklDb21wb25lbnQoc2hvcnRjdXRzUGFyYW0pOw0KICAgICAgICAgICAgICAgICAgICAgICAgSlNPTi5wYXJzZShkZWNvZGVkRGF0YSk7DQogICAgICAgICAgICAgICAgICAgICAgICBsb2NhbFN0b3JhZ2Uuc2V0SXRlbShzaG9ydGN1dE1hbmFnZXIua2V5LCBkZWNvZGVkRGF0YSk7DQogICAgICAgICAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHsgY29uc29sZS5lcnJvcigiRmFpbGVkIHRvIHBhcnNlIHNob3J0Y3V0cyBmcm9tIFVSTDoiLCBlKX0NCiAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9LA0KICAgICAgICAgICAgZ2V0QXNQYXJhbTogKCkgPT4gew0KICAgICAgICAgICAgICAgIGNvbnN0IHJhd0RhdGEgPSBsb2NhbFN0b3JhZ2UuZ2V0SXRlbShzaG9ydGN1dE1hbmFnZXIua2V5KTsNCiAgICAgICAgICAgICAgICBpZiAoIXJhd0RhdGEpIHJldHVybiAnJzsNCiAgICAgICAgICAgICAgICByZXR1cm4gYHNob3J0Y3V0cz0ke2VuY29kZVVSSUNvbXBvbmVudChyYXdEYXRhKX1gOw0KICAgICAgICAgICAgfQ0KICAgICAgICB9Ow0KICAgICAgICANCiAgICAgICAgY29uc3QgdXBkYXRlQWxsV2lkZ2V0TGlua3MgPSAoKSA9PiB7DQogICAgICAgICAgICBjb25zdCBwYXJhbVN0cmluZyA9IHNob3J0Y3V0TWFuYWdlci5nZXRBc1BhcmFtKCk7DQogICAgICAgICAgICBpZiAoIXBhcmFtU3RyaW5nKSByZXR1cm47DQogICAgICAgICAgICBjb25zdCBhbGxMaW5rcyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJyNmaGwtd2lkZ2V0LWNvbnRhaW5lciAuZmhsLWljb24tZGlzcGxheSwgI2ZobC13aWRnZXQtY29udGFpbmVyIC5maGwtZml4ZWQtaXRlbScpOw0KICAgICAgICAgICAgYWxsTGlua3MuZm9yRWFjaChsaW5rID0+IHsNCiAgICAgICAgICAgICAgICBpZiAobGluay50YWdOYW1lID09PSAnQScpIHsNCiAgICAgICAgICAgICAgICAgICAgY29uc3Qgb3JpZ2luYWxIcmVmID0gbGluay5nZXRBdHRyaWJ1dGUoJ2hyZWYnKTsNCiAgICAgICAgICAgICAgICAgICAgaWYgKCFvcmlnaW5hbEhyZWYgfHwgb3JpZ2luYWxIcmVmLnN0YXJ0c1dpdGgoJyMnKSkgcmV0dXJuOw0KICAgICAgICAgICAgICAgICAgICB0cnkgew0KICAgICAgICAgICAgICAgICAgICAgICAgbGV0IG5ld1VybCA9IG5ldyBVUkwob3JpZ2luYWxIcmVmKTsNCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IFtrZXksIHZhbHVlXSA9IHBhcmFtU3RyaW5nLnNwbGl0KCc9Jyk7DQogICAgICAgICAgICAgICAgICAgICAgICBuZXdVcmwuc2VhcmNoUGFyYW1zLnNldChrZXksIHZhbHVlKTsNCiAgICAgICAgICAgICAgICAgICAgICAgIGxpbmsuaHJlZiA9IG5ld1VybC50b1N0cmluZygpOw0KICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChlKSB7IGNvbnNvbGUuZXJyb3IoJ0ludmFsaWQgVVJMIGZvciBsaW5rIHVwZGF0ZTonLCBvcmlnaW5hbEhyZWYpfQ0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0pOw0KICAgICAgICB9Ow0KDQogICAgICAgIGNvbnN0IHNob3dUb2FzdCA9IChtZXNzYWdlKSA9PiB7IGNsZWFyVGltZW91dCh0b2FzdFRpbWVvdXQpOyB0b2FzdEVsZW1lbnQudGV4dENvbnRlbnQgPSBtZXNzYWdlOyB0b2FzdEVsZW1lbnQuY2xhc3NMaXN0LmFkZCgndmlzaWJsZScpOyB0b2FzdFRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHsgdG9hc3RFbGVtZW50LmNsYXNzTGlzdC5yZW1vdmUoJ3Zpc2libGUnKX0sIDI1MDApfTsNCiAgICAgICAgDQogICAgICAgIGNvbnN0IGFwcGx5TGFuZ3VhZ2UgPSAobGFuZ0NvZGUpID0+IHsNCiAgICAgICAgICAgIGlmICh0eXBlb2YgdHJhbnNsYXRpb25zID09PSAndW5kZWZpbmVkJykgeyBjb25zb2xlLmVycm9yKCJUcmFuc2xhdGlvbnMgbm90IGxvYWRlZCEgTWFrZSBzdXJlIGxhbmcuanMgaXMgaW5jbHVkZWQgY29ycmVjdGx5LiIpOyByZXR1cm59DQogICAgICAgICAgICBjb25zdCB0ID0gdHJhbnNsYXRpb25zW2xhbmdDb2RlXSB8fCB0cmFuc2xhdGlvbnNbJ2VuJ107DQogICAgICAgICAgICBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQubGFuZyA9IGxhbmdDb2RlOw0KICAgICAgICAgICAgc2VhcmNoVHJpZ2dlci5zZXRBdHRyaWJ1dGUoJ2FyaWEtbGFiZWwnLCB0Lm9wZW5TZWFyY2gpOw0KICAgICAgICAgICAgYWRkVG9Ib21lQnRuLnNldEF0dHJpYnV0ZSgnYXJpYS1sYWJlbCcsIHQuYWRkVG9XaWRnZXQpOw0KICAgICAgICAgICAgc2VhcmNoQ2xvc2Uuc2V0QXR0cmlidXRlKCdhcmlhLWxhYmVsJywgdC5jbG9zZVNlYXJjaCk7DQogICAgICAgICAgICBsYW5ndWFnZUJ0bi5zZXRBdHRyaWJ1dGUoJ2FyaWEtbGFiZWwnLCB0LmNoYW5nZUxhbmd1YWdlKTsNCiAgICAgICAgICAgIHNlYXJjaEFkZEJ0bi5zZXRBdHRyaWJ1dGUoJ2FyaWEtbGFiZWwnLCAnQ29weSBXaWRnZXQgQ29kZScpOyANCiAgICAgICAgICAgIGZpeGVkUXVlc3Rpb25CdG4uc2V0QXR0cmlidXRlKCdhcmlhLWxhYmVsJywgdC5xdWVzdGlvbik7DQogICAgICAgICAgICBmaXhlZENoYXRCdG4uc2V0QXR0cmlidXRlKCdhcmlhLWxhYmVsJywgdC5jaGFyYWN0ZXJDaGF0KTsNCiAgICAgICAgICAgIGZpeGVkQWRCdG4uc2V0QXR0cmlidXRlKCdhcmlhLWxhYmVsJywgdC5hZHMpOw0KICAgICAgICAgICAgZml4ZWRJbmZvQnRuLnNldEF0dHJpYnV0ZSgnYXJpYS1sYWJlbCcsIHQuaW5mbyk7DQogICAgICAgICAgICBzZWFyY2hJbnB1dC5wbGFjZWhvbGRlciA9IHQuc2VhcmNoUGxhY2Vob2xkZXI7DQogICAgICAgICAgICBpbml0aWFsUmVuZGVyKCk7DQogICAgICAgIH07DQoNCiAgICAgICAgY29uc3QgcG9wdWxhdGVMYW5ndWFnZVBhbmVsID0gKCkgPT4geyBsYW5ndWFnZVBhbmVsLmlubmVySFRNTCA9ICcnOyBsYW5ndWFnZXMuZm9yRWFjaChsYW5nID0+IHsgY29uc3QgbGFuZ0l0ZW0gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdhJyk7IGxhbmdJdGVtLmhyZWYgPSAnIyc7IGxhbmdJdGVtLmNsYXNzTmFtZSA9ICdmaGwtbGFuZ3VhZ2UtaXRlbSc7IGxhbmdJdGVtLnRleHRDb250ZW50ID0gbGFuZy5uYW1lOyBsYW5nSXRlbS5kYXRhc2V0LmxhbmcgPSBsYW5nLmNvZGU7IGxhbmdJdGVtLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgKGUpID0+IHsgZS5wcmV2ZW50RGVmYXVsdCgpOyBlLnN0b3BQcm9wYWdhdGlvbigpOyBjb25zdCBuZXdMYW5nID0gZS50YXJnZXQuZGF0YXNldC5sYW5nOyBpZiAobmV3TGFuZyAhPT0gY3VycmVudExhbmcpIHsgY3VycmVudExhbmcgPSBuZXdMYW5nOyBsb2NhbFN0b3JhZ2Uuc2V0SXRlbSgnZmhsLXdpZGdldC1sYW5nJywgY3VycmVudExhbmcpOyBhcHBseUxhbmd1YWdlKGN1cnJlbnRMYW5nKX0gbGFuZ3VhZ2VQYW5lbC5jbGFzc0xpc3QucmVtb3ZlKCd2aXNpYmxlJyl9KTsgbGFuZ3VhZ2VQYW5lbC5hcHBlbmRDaGlsZChsYW5nSXRlbSl9KX07DQogICAgICAgIGNvbnN0IGdldFByb2Nlc3NlZFVybCA9IChmdWxsVXJsKSA9PiB7IHRyeSB7IGNvbnN0IHVybE9iaiA9IG5ldyBVUkwoZnVsbFVybCk7IHJldHVybiBgJHt1cmxPYmoucHJvdG9jb2x9Ly8ke3VybE9iai5ob3N0bmFtZX1gfSBjYXRjaCAoZSkgeyByZXR1cm4gZnVsbFVybH0gfTsNCiAgICAgICAgDQogICAgICAgIGNvbnN0IGNyZWF0ZUljb25JdGVtID0gKGl0ZW0sIHR5cGUgPSAnZGVmYXVsdCcpID0+IHsNCiAgICAgICAgICAgIGlmICghaXRlbSB8fCAhaXRlbS5uYW1lIHx8ICFpdGVtLnVybCkgcmV0dXJuIGRvY3VtZW50LmNyZWF0ZURvY3VtZW50RnJhZ21lbnQoKTsNCiAgICAgICAgICAgIGNvbnN0IGxpbmsgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdhJyk7DQogICAgICAgICAgICBpZiAodHlwZSA9PT0gJ3NlYXJjaCcpIHsNCiAgICAgICAgICAgICAgICBjb25zdCBwYXJhbVN0cmluZyA9IHNob3J0Y3V0TWFuYWdlci5nZXRBc1BhcmFtKCk7DQogICAgICAgICAgICAgICAgbGV0IGZpbmFsVXJsID0gaXRlbS51cmw7DQogICAgICAgICAgICAgICAgaWYgKHBhcmFtU3RyaW5nKSB7IHRyeSB7IGxldCBuZXdVcmwgPSBuZXcgVVJMKGl0ZW0udXJsKTsgY29uc3QgW2tleSwgdmFsdWVdID0gcGFyYW1TdHJpbmcuc3BsaXQoJz0nKTsgbmV3VXJsLnNlYXJjaFBhcmFtcy5zZXQoa2V5LCB2YWx1ZSk7IGZpbmFsVXJsID0gbmV3VXJsLnRvU3RyaW5nKCl9IGNhdGNoIChlKSB7IGNvbnNvbGUuZXJyb3IoJ0ludmFsaWQgVVJMIGZvciBzZWFyY2ggcmVzdWx0IGxpbms6JywgaXRlbS51cmwpfSB9DQogICAgICAgICAgICAgICAgbGluay5ocmVmID0gZmluYWxVcmw7DQogICAgICAgICAgICB9IGVsc2UgeyBsaW5rLmhyZWYgPSBpdGVtLnVybH0NCiAgICAgICAgICAgIGxpbmsuY2xhc3NOYW1lID0gJ2ZobC1pY29uLWRpc3BsYXknOw0KCQkJbGluay5yZWwgPSAnbm9mb2xsb3cnOw0KICAgICAgICAgICAgbGluay5zZXRBdHRyaWJ1dGUoJ2FyaWEtbGFiZWwnLCBpdGVtLm5hbWUpOw0KICAgICAgICAgICAgaWYgKHR5cGUgPT09ICdzZWFyY2gnKSB7IGxpbmsuYWRkRXZlbnRMaXN0ZW5lcignbW91c2Vkb3duJywgKCkgPT4geyBmZXRjaChgJHtBUElfQkFTRV9VUkx9L3VwZGF0ZV92aWV3X2NvdW50LnBocGAsIHsgbWV0aG9kOiAnUE9TVCcsIGhlYWRlcnM6IHsgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyB9LCBib2R5OiBKU09OLnN0cmluZ2lmeSh7IHVybDogaXRlbS51cmwgfSkgfSkuY2F0Y2goY29uc29sZS5lcnJvcil9KTsgbGluay50YXJnZXQgPSAnX2JsYW5rJ30NCiAgICAgICAgICAgIGNvbnN0IGljb25DaXJjbGUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsgaWNvbkNpcmNsZS5jbGFzc05hbWUgPSAnZmhsLWljb24tY2lyY2xlJzsNCiAgICAgICAgICAgIGlmIChpdGVtLmljb24pIHsgY29uc3QgaWNvbiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2knKTsgaWNvbi5jbGFzc05hbWUgPSBgcGgtYm9sZCAke2l0ZW0uaWNvbn1gOyBpY29uQ2lyY2xlLmFwcGVuZENoaWxkKGljb24pfQ0KICAgICAgICAgICAgZWxzZSBpZiAoaXRlbS5pbWcpIHsgY29uc3QgY3VzdG9tSW1hZ2UgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdpbWcnKTsgY3VzdG9tSW1hZ2Uuc3JjID0gaXRlbS5pbWc7IGN1c3RvbUltYWdlLmFsdCA9IGl0ZW0ubmFtZTsgY3VzdG9tSW1hZ2Uub25lcnJvciA9ICgpID0+IHsgY3VzdG9tSW1hZ2Uuc3R5bGUuZGlzcGxheSA9ICdub25lJ307IGljb25DaXJjbGUuYXBwZW5kQ2hpbGQoY3VzdG9tSW1hZ2UpfQ0KICAgICAgICAgICAgZWxzZSB7IGNvbnN0IGZhdmljb24gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdpbWcnKTsgZmF2aWNvbi5zcmMgPSBgaHR0cHM6Ly93d3cuZ29vZ2xlLmNvbS9zMi9mYXZpY29ucz9zej02NCZkb21haW5fdXJsPSR7aXRlbS51cmx9YDsgZmF2aWNvbi5hbHQgPSBpdGVtLm5hbWU7IGZhdmljb24ub25lcnJvciA9ICgpID0+IHsgZmF2aWNvbi5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnfTsgaWNvbkNpcmNsZS5hcHBlbmRDaGlsZChmYXZpY29uKX0NCiAgICAgICAgICAgIGxpbmsuYXBwZW5kQ2hpbGQoaWNvbkNpcmNsZSk7DQogICAgICAgICAgICBpZiAodHlwZSA9PT0gJ3NlYXJjaCcpIHsNCiAgICAgICAgICAgICAgICBjb25zdCB0b29sdGlwID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7IHRvb2x0aXAuY2xhc3NOYW1lID0gJ2ZobC1pdGVtLXRvb2x0aXAnOyB0b29sdGlwLnRleHRDb250ZW50ID0gaXRlbS5uYW1lLmxlbmd0aCA+IDUgPyBpdGVtLm5hbWUuc2xpY2UoMCwgNSkgKyAnLi4nIDogaXRlbS5uYW1lOyBsaW5rLmFwcGVuZENoaWxkKHRvb2x0aXApOw0KICAgICAgICAgICAgICAgIGxpbmsuYWRkRXZlbnRMaXN0ZW5lcignbW91c2VlbnRlcicsIChlKSA9PiB7IGNvbnN0IGN1cnJlbnRUb29sdGlwID0gZS5jdXJyZW50VGFyZ2V0LnF1ZXJ5U2VsZWN0b3IoJy5maGwtaXRlbS10b29sdGlwJyk7IGlmICghY3VycmVudFRvb2x0aXApIHJldHVybjsgY3VycmVudFRvb2x0aXAuY2xhc3NMaXN0LmFkZCgndmlzaWJsZScpOyBjb25zdCB3aWRnZXRSZWN0ID0gd2lkZ2V0V3JhcHBlci5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTsgY29uc3QgdG9vbHRpcFJlY3QgPSBjdXJyZW50VG9vbHRpcC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTsgbGV0IG9mZnNldFggPSAwOyBjb25zdCBwYWRkaW5nID0gNTsgaWYgKHRvb2x0aXBSZWN0LmxlZnQgPCB3aWRnZXRSZWN0LmxlZnQpIHsgb2Zmc2V0WCA9IHdpZGdldFJlY3QubGVmdCAtIHRvb2x0aXBSZWN0LmxlZnQgKyBwYWRkaW5nfSBlbHNlIGlmICh0b29sdGlwUmVjdC5yaWdodCA+IHdpZGdldFJlY3QucmlnaHQpIHsgb2Zmc2V0WCA9IHdpZGdldFJlY3QucmlnaHQgLSB0b29sdGlwUmVjdC5yaWdodCAtIHBhZGRpbmd9IGlmIChvZmZzZXRYICE9PSAwKSB7IGN1cnJlbnRUb29sdGlwLnN0eWxlLnRyYW5zZm9ybSA9IGB0cmFuc2xhdGVYKGNhbGMoLTUwJSArICR7b2Zmc2V0WH1weCkpYH0gfSk7DQogICAgICAgICAgICAgICAgbGluay5hZGRFdmVudExpc3RlbmVyKCdtb3VzZWxlYXZlJywgKGUpID0+IHsgY29uc3QgY3VycmVudFRvb2x0aXAgPSBlLmN1cnJlbnRUYXJnZXQucXVlcnlTZWxlY3RvcignLmZobC1pdGVtLXRvb2x0aXAnKTsgaWYgKCFjdXJyZW50VG9vbHRpcCkgcmV0dXJuOyBjdXJyZW50VG9vbHRpcC5jbGFzc0xpc3QucmVtb3ZlKCd2aXNpYmxlJyk7IGN1cnJlbnRUb29sdGlwLnN0eWxlLnRyYW5zZm9ybSA9ICd0cmFuc2xhdGVYKC01MCUpJ30pOw0KICAgICAgICAgICAgfSBlbHNlIHsgY29uc3QgbmFtZVNwYW4gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7IG5hbWVTcGFuLmNsYXNzTmFtZSA9ICdmaGwtaXRlbS1uYW1lJzsgbmFtZVNwYW4udGV4dENvbnRlbnQgPSBpdGVtLm5hbWUubGVuZ3RoID4gNSA/IGl0ZW0ubmFtZS5zbGljZSgwLCA1KSArICcuLicgOiBpdGVtLm5hbWU7IGxpbmsuYXBwZW5kQ2hpbGQobmFtZVNwYW4pfQ0KICAgICAgICAgICAgaWYgKHR5cGUgPT09ICdsb2NhbCcpIHsNCiAgICAgICAgICAgICAgICBjb25zdCBjb250cm9scyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOyBjb250cm9scy5jbGFzc05hbWUgPSAnZmhsLWl0ZW0tY29udHJvbHMnOw0KICAgICAgICAgICAgICAgIGNvbnN0IGRlbGV0ZUJ0biA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2J1dHRvbicpOyBkZWxldGVCdG4uY2xhc3NOYW1lID0gJ2ZobC1jb250cm9sLWJ0bic7IGRlbGV0ZUJ0bi5pbm5lckhUTUwgPSAnJnRpbWVzOyc7IGRlbGV0ZUJ0bi50aXRsZSA9ICfsgq3soJwnOw0KICAgICAgICAgICAgICAgIGRlbGV0ZUJ0bi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIChlKSA9PiB7IGUucHJldmVudERlZmF1bHQoKTsgZS5zdG9wUHJvcGFnYXRpb24oKTsgbGV0IHN0b3JlZEl0ZW1zID0gc2hvcnRjdXRNYW5hZ2VyLmdldCgpOyBzdG9yZWRJdGVtcyA9IHN0b3JlZEl0ZW1zLmZpbHRlcihzdG9yZWQgPT4gc3RvcmVkLnVybCAhPT0gaXRlbS51cmwpOyBzaG9ydGN1dE1hbmFnZXIuc2V0KHN0b3JlZEl0ZW1zKTsgaW5pdGlhbFJlbmRlcigpfSk7DQogICAgICAgICAgICAgICAgY29uc3QgbW92ZUJ0biA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2J1dHRvbicpOyBtb3ZlQnRuLmNsYXNzTmFtZSA9ICdmaGwtY29udHJvbC1idG4nOyBtb3ZlQnRuLmlubmVySFRNTCA9ICcmZ3Q7JzsgbW92ZUJ0bi50aXRsZSA9ICfsmKTrpbjsqr3snLzroZwg7J2064+ZJzsNCiAgICAgICAgICAgICAgICBtb3ZlQnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgKGUpID0+IHsgZS5wcmV2ZW50RGVmYXVsdCgpOyBlLnN0b3BQcm9wYWdhdGlvbigpOyBsZXQgc3RvcmVkSXRlbXMgPSBzaG9ydGN1dE1hbmFnZXIuZ2V0KCk7IGNvbnN0IGN1cnJlbnRJbmRleCA9IHN0b3JlZEl0ZW1zLmZpbmRJbmRleChzdG9yZWQgPT4gc3RvcmVkLnVybCA9PT0gaXRlbS51cmwpOyBpZiAoY3VycmVudEluZGV4ID4gLTEpIHsgY29uc3QgbmV3SW5kZXggPSAoY3VycmVudEluZGV4ICsgMSkgJSBzdG9yZWRJdGVtcy5sZW5ndGg7IGNvbnN0IFttb3ZlZEl0ZW1dID0gc3RvcmVkSXRlbXMuc3BsaWNlKGN1cnJlbnRJbmRleCwgMSk7IHN0b3JlZEl0ZW1zLnNwbGljZShuZXdJbmRleCwgMCwgbW92ZWRJdGVtKTsgc2hvcnRjdXRNYW5hZ2VyLnNldChzdG9yZWRJdGVtcyk7IGluaXRpYWxSZW5kZXIoKX0gfSk7DQogICAgICAgICAgICAgICAgY29udHJvbHMuYXBwZW5kQ2hpbGQoZGVsZXRlQnRuKTsgY29udHJvbHMuYXBwZW5kQ2hpbGQobW92ZUJ0bik7IGxpbmsuYXBwZW5kQ2hpbGQoY29udHJvbHMpOw0KICAgICAgICAgICAgfQ0KICAgICAgICAgICAgcmV0dXJuIGxpbms7DQogICAgICAgIH07DQoNCiAgICAgICAgY29uc3QgYXBwZW5kTmV4dFBhZ2UgPSAoKSA9PiB7IGlmIChpc0xvYWRpbmdOZXh0UGFnZSkgcmV0dXJuOyBpc0xvYWRpbmdOZXh0UGFnZSA9IHRydWU7IGNvbnN0IHN0YXJ0SW5kZXggPSAoY3VycmVudFBhZ2UgLSAxKSAqIElURU1TX1BFUl9QQUdFOyBjb25zdCBlbmRJbmRleCA9IGN1cnJlbnRQYWdlICogSVRFTVNfUEVSX1BBR0U7IGlmIChzdGFydEluZGV4ID49IGN1cnJlbnREaXNwbGF5SXRlbXMubGVuZ3RoKSB7IGlzTG9hZGluZ05leHRQYWdlID0gZmFsc2U7IHJldHVybn0gY29uc3QgaXRlbXNUb0FwcGVuZCA9IGN1cnJlbnREaXNwbGF5SXRlbXMuc2xpY2Uoc3RhcnRJbmRleCwgZW5kSW5kZXgpOyBpdGVtc1RvQXBwZW5kLmZvckVhY2goaXRlbSA9PiB7IHNlYXJjaFJlc3VsdHNDb250YWluZXIuYXBwZW5kQ2hpbGQoY3JlYXRlSWNvbkl0ZW0oaXRlbSwgJ3NlYXJjaCcpKX0pOyBjdXJyZW50UGFnZSsrOyBpc0xvYWRpbmdOZXh0UGFnZSA9IGZhbHNlfTsNCiAgICAgICAgDQogICAgICAgIGNvbnN0IHJlbmRlckluaXRpYWxSZXN1bHRzID0gKGl0ZW1zKSA9PiB7DQogICAgICAgICAgICBzZWFyY2hSZXN1bHRzQ29udGFpbmVyLmlubmVySFRNTCA9ICcnOw0KICAgICAgICAgICAgY3VycmVudFBhZ2UgPSAxOw0KICAgICAgICAgICAgY3VycmVudERpc3BsYXlJdGVtcyA9IEFycmF5LmlzQXJyYXkoaXRlbXMpID8gaXRlbXMgOiBbXTsNCiAgICAgICAgICAgIGFwcGVuZE5leHRQYWdlKCk7DQogICAgICAgIH07DQoNCiAgICAgICAgY29uc3QgcGVyZm9ybVNlYXJjaCA9IGFzeW5jIChxdWVyeSA9ICcnKSA9PiB7DQogICAgICAgICAgICB0cnkgew0KICAgICAgICAgICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2goYCR7QVBJX0JBU0VfVVJMfS9hcHBhcGkyLnBocD9xdWVyeT0ke2VuY29kZVVSSUNvbXBvbmVudChxdWVyeSl9YCk7DQogICAgICAgICAgICAgICAgaWYgKCFyZXNwb25zZS5vaykgdGhyb3cgbmV3IEVycm9yKCdBUEkg7J2R64u1IOyYpOulmCcpOw0KICAgICAgICAgICAgICAgIGNvbnN0IHJlc3VsdHMgPSBhd2FpdCByZXNwb25zZS5qc29uKCk7DQogICAgICAgICAgICAgICAgcmVuZGVySW5pdGlhbFJlc3VsdHMocmVzdWx0cyk7DQogICAgICAgICAgICB9IGNhdGNoIChlcnJvcikgew0KICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ+yVsSDrqqnroZ0g66Gc65OcIOyLpO2MqDonLCBlcnJvcik7DQogICAgICAgICAgICAgICAgc2VhcmNoUmVzdWx0c0NvbnRhaW5lci5pbm5lckhUTUwgPSAnPHA+66qp66GdIOuhnOuTnCDsi6TtjKg8L3A+JzsNCiAgICAgICAgICAgIH0NCiAgICAgICAgfTsNCiAgICAgICAgDQogICAgICAgIGNvbnN0IG9wZW5TZWFyY2ggPSAoKSA9PiB7IHdpZGdldENvbnRhaW5lci5jbGFzc0xpc3QuYWRkKCdzZWFyY2gtbW9kZScpOyB3aWRnZXRXcmFwcGVyLmNsYXNzTGlzdC5hZGQoJ3NlYXJjaC1tb2RlJyk7IHNlYXJjaElucHV0LmZvY3VzKCk7IHBlcmZvcm1TZWFyY2goKX07DQogICAgICAgIA0KCQljb25zdCBpbml0aWFsUmVuZGVyID0gKCkgPT4gew0KICAgICAgICAgICAgY29uc3QgYSA9IHRyYW5zbGF0aW9uc1tjdXJyZW50TGFuZ10gfHwgdHJhbnNsYXRpb25zWydlbiddOw0KICAgICAgICAgICAgY29uc3QgYiA9IFsNCiAgICAgICAgICAgICAgICB7IG5hbWU6IGEuc2VhcmNoLCB1cmw6ICdodHRwczovL2lzYWkua3InLCBpY29uOiAncGgtc3BhcmtsZScgfSwNCiAgICAgICAgICAgICAgICB7IG5hbWU6IGEucXVlc3Rpb24sIHVybDogJ2h0dHBzOi8vaXNhaS5rci8jY2hhdCcsIGljb246ICdwaC1xdWVzdGlvbi1tYXJrJyB9LA0KICAgICAgICAgICAgICAgIHsgbmFtZTogYS5mb3J1bSwgdXJsOiAnaHR0cHM6Ly9sb2dpZy5pbScsIGltZzogJ2h0dHBzOi8vY2RuLmpzZGVsaXZyLm5ldC9ucG0vY2RuaG9zdEAyLjMuOS9fX19fX19fX19fX19fX19sb2dpZy5wbmcnIH0sDQogICAgICAgICAgICAgICAgeyBuYW1lOiBhLmNoYXJhY3RlckNoYXQsIHVybDogJ2h0dHBzOi8vbGFpLm9kdWMua3IvJywgaW1nOiAnaHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L25wbS9jZG5ob3N0QDIuNy42L2xhaTQ4LnBuZycgfSwNCiAgICAgICAgICAgICAgICB7IG5hbWU6IGEuYmxvZywgdXJsOiAnaHR0cHM6Ly9ibG9nLjA5OS5rcicsIGljb246ICdwaC1hcnRpY2xlLW1lZGl1bScgfSwNCiAgICAgICAgICAgICAgICB7IG5hbWU6ICdBcmNoaXZlIEFJJywgdXJsOiAnaHR0cHM6Ly9hcmNoaS52ZWFpLndvcmtlcnMuZGV2LycsIGljb246ICdwaC1hcmNoaXZlJyB9LA0KICAgICAgICAgICAgXTsNCg0KICAgICAgICAgICAgbGlzdENvbnRhaW5lci5pbm5lckhUTUwgPSAnPGJ1dHRvbiBjbGFzcz0iZmhsLWljb24tY2lyY2xlIiBzdHlsZT0ibWFyZ2luOiAwIDhweCAwIDA7Ym9yZGVyOjAiIGlkPSJmaGwtZWRpdC1tb2RlLWJ0biI+PGkgY2xhc3M9InBoLWJvbGQgcGgtcGVuY2lsLXNpbXBsZSI+PC9pPjwvYnV0dG9uPic7DQogICAgICAgICAgICANCiAgICAgICAgICAgIGNvbnN0IGVkaXRNb2RlQnRuID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2ZobC1lZGl0LW1vZGUtYnRuJyk7DQogICAgICAgICAgICBpZiAoZWRpdE1vZGVCdG4pIHsNCiAgICAgICAgICAgICAgICBpZiAobGlzdENvbnRhaW5lci5jbGFzc0xpc3QuY29udGFpbnMoJ2VkaXQtbW9kZScpKSB7DQogICAgICAgICAgICAgICAgICAgIGVkaXRNb2RlQnRuLmNsYXNzTGlzdC5hZGQoJ2FjdGl2ZScpOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICBlZGl0TW9kZUJ0bi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsICgpID0+IHsNCiAgICAgICAgICAgICAgICAgICAgbGlzdENvbnRhaW5lci5jbGFzc0xpc3QudG9nZ2xlKCdlZGl0LW1vZGUnKTsNCiAgICAgICAgICAgICAgICAgICAgZWRpdE1vZGVCdG4uY2xhc3NMaXN0LnRvZ2dsZSgnYWN0aXZlJyk7IA0KICAgICAgICAgICAgICAgIH0pOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICBjb25zdCBjID0gc2hvcnRjdXRNYW5hZ2VyLmdldCgpOw0KICAgICAgICAgICAgbGV0IGQgPSBbLi4uYl07DQogICAgICAgICAgICBkLnNwbGljZSgyLCAwLCAuLi5jKTsNCiAgICAgICAgICAgIA0KICAgICAgICAgICAgY29uc3QgZnJhZ21lbnQgPSBkb2N1bWVudC5jcmVhdGVEb2N1bWVudEZyYWdtZW50KCk7DQogICAgICAgICAgICBkLmZvckVhY2goZSA9PiB7DQogICAgICAgICAgICAgICAgY29uc3QgZiA9IGMuc29tZShnID0+IGcudXJsID09PSBlLnVybCk7DQogICAgICAgICAgICAgICAgY29uc3QgaCA9IGYgJiYgIWUuaWNvbiAmJiAhZS5pbWcgPyAnbG9jYWwnIDogJ2RlZmF1bHQnOw0KICAgICAgICAgICAgICAgIGZyYWdtZW50LmFwcGVuZENoaWxkKGNyZWF0ZUljb25JdGVtKGUsIGgpKTsNCiAgICAgICAgICAgIH0pOw0KICAgICAgICAgICAgbGlzdENvbnRhaW5lci5hcHBlbmRDaGlsZChmcmFnbWVudCk7DQoNCiAgICAgICAgICAgIGNoZWNrU2Nyb2xsYWJpbGl0eSgpOw0KICAgICAgICAgICAgdXBkYXRlQWxsV2lkZ2V0TGlua3MoKTsNCiAgICAgICAgfTsNCiAgICAgICAgDQogICAgICAgIGNvbnN0IHVwZGF0ZVNjcm9sbEluZGljYXRvciA9ICgpID0+IHsgY29uc3Qgc2Nyb2xsTGVmdCA9IGxpc3RDb250YWluZXIuc2Nyb2xsTGVmdDsgY29uc3QgbWF4U2Nyb2xsTGVmdCA9IGxpc3RDb250YWluZXIuc2Nyb2xsV2lkdGggLSBsaXN0Q29udGFpbmVyLmNsaWVudFdpZHRoOyBpZiAobWF4U2Nyb2xsTGVmdCA8PSAwKSByZXR1cm47IGNvbnN0IHNjcm9sbEZyYWN0aW9uID0gc2Nyb2xsTGVmdCAvIG1heFNjcm9sbExlZnQ7IGNvbnN0IHRyYWNrV2lkdGggPSBpbmRpY2F0b3JUcmFjay5jbGllbnRXaWR0aDsgY29uc3QgaW5kaWNhdG9yV2lkdGggPSBzY3JvbGxJbmRpY2F0b3IuY2xpZW50V2lkdGg7IGNvbnN0IG1heEluZGljYXRvckxlZnQgPSB0cmFja1dpZHRoIC0gaW5kaWNhdG9yV2lkdGg7IGNvbnN0IGluZGljYXRvckxlZnQgPSBzY3JvbGxGcmFjdGlvbiAqIG1heEluZGljYXRvckxlZnQ7IHNjcm9sbEluZGljYXRvci5zdHlsZS50cmFuc2Zvcm0gPSBgdHJhbnNsYXRlWSgtNTAlKSB0cmFuc2xhdGVYKCR7aW5kaWNhdG9yTGVmdH1weClgfTsNCiAgICAgICAgY29uc3QgY2hlY2tTY3JvbGxhYmlsaXR5ID0gKCkgPT4geyBjb25zdCBpc1Njcm9sbGFibGUgPSBsaXN0Q29udGFpbmVyLnNjcm9sbFdpZHRoID4gbGlzdENvbnRhaW5lci5jbGllbnRXaWR0aDsgd2lkZ2V0Q29udGFpbmVyLmNsYXNzTGlzdC50b2dnbGUoJ3Njcm9sbGFibGUnLCBpc1Njcm9sbGFibGUpOyBpZiAoaXNTY3JvbGxhYmxlKSB1cGRhdGVTY3JvbGxJbmRpY2F0b3IoKX07DQogICAgICAgIGxldCBpc0RyYWdnaW5nID0gZmFsc2U7DQogICAgICAgIGNvbnN0IGhhbmRsZURyYWdNb3ZlID0gKGUpID0+IHsgaWYgKCFpc0RyYWdnaW5nKSByZXR1cm47IGUucHJldmVudERlZmF1bHQoKTsgY29uc3QgdHJhY2tSZWN0ID0gaW5kaWNhdG9yVHJhY2suZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7IGNvbnN0IG1heFNjcm9sbExlZnQgPSBsaXN0Q29udGFpbmVyLnNjcm9sbFdpZHRoIC0gbGlzdENvbnRhaW5lci5jbGllbnRXaWR0aDsgbGV0IHBvc2l0aW9uUmF0aW8gPSAoZS5jbGllbnRYIC0gdHJhY2tSZWN0LmxlZnQpIC8gdHJhY2tSZWN0LndpZHRoOyBwb3NpdGlvblJhdGlvID0gTWF0aC5tYXgoMCwgTWF0aC5taW4oMSwgcG9zaXRpb25SYXRpbykpOyBsaXN0Q29udGFpbmVyLnNjcm9sbExlZnQgPSBwb3NpdGlvblJhdGlvICogbWF4U2Nyb2xsTGVmdH07DQogICAgICAgIGNvbnN0IGhhbmRsZURyYWdFbmQgPSAoKSA9PiB7IGlmICghaXNEcmFnZ2luZykgcmV0dXJuOyBpc0RyYWdnaW5nID0gZmFsc2U7IGRvY3VtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ21vdXNlbW92ZScsIGhhbmRsZURyYWdNb3ZlKTsgZG9jdW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lcignbW91c2V1cCcsIGhhbmRsZURyYWdFbmQpfTsNCiAgICAgICAgaW5kaWNhdG9yVHJhY2suYWRkRXZlbnRMaXN0ZW5lcignbW91c2Vkb3duJywgKGUpID0+IHsgaXNEcmFnZ2luZyA9IHRydWU7IGhhbmRsZURyYWdNb3ZlKGUpOyBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdtb3VzZW1vdmUnLCBoYW5kbGVEcmFnTW92ZSk7IGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNldXAnLCBoYW5kbGVEcmFnRW5kKX0pOw0KICAgICAgICBjb25zdCBjbG9zZVNlYXJjaCA9ICgpID0+IHsgd2lkZ2V0Q29udGFpbmVyLmNsYXNzTGlzdC5yZW1vdmUoJ3NlYXJjaC1tb2RlJyk7IHdpZGdldFdyYXBwZXIuY2xhc3NMaXN0LnJlbW92ZSgnc2VhcmNoLW1vZGUnKTsgc2VhcmNoSW5wdXQudmFsdWUgPSAnJzsgc2VhcmNoUmVzdWx0c0NvbnRhaW5lci5pbm5lckhUTUwgPSAnJ307DQogICAgICAgIGxldCBoaWRlVGltZW91dCA9IG51bGw7DQogICAgICAgIHdpZGdldENvbnRhaW5lci5hZGRFdmVudExpc3RlbmVyKCdtb3VzZWVudGVyJywgKCkgPT4geyBjbGVhclRpbWVvdXQoaGlkZVRpbWVvdXQpfSk7DQogICAgICAgIHdpZGdldENvbnRhaW5lci5hZGRFdmVudExpc3RlbmVyKCdtb3VzZWxlYXZlJywgKCkgPT4geyBoaWRlVGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4geyB3aWRnZXRDb250YWluZXIuY2xhc3NMaXN0LmFkZCgnaGlkZGVuJyl9LCA2MDAwKX0pOw0KICAgICAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIChlKSA9PiB7IGlmICghbGFuZ3VhZ2VQYW5lbC5jb250YWlucyhlLnRhcmdldCkgJiYgIWxhbmd1YWdlQnRuLmNvbnRhaW5zKGUudGFyZ2V0KSkgeyBsYW5ndWFnZVBhbmVsLmNsYXNzTGlzdC5yZW1vdmUoJ3Zpc2libGUnKX0gaWYgKHdpZGdldENvbnRhaW5lci5jb250YWlucyhlLnRhcmdldCkpIHsgcmV0dXJufSBpZiAod2lkZ2V0Q29udGFpbmVyLmNsYXNzTGlzdC5jb250YWlucygnaGlkZGVuJykpIHsgc2V0VGltZW91dCgoKSA9PiB7IHdpZGdldENvbnRhaW5lci5jbGFzc0xpc3QucmVtb3ZlKCdoaWRkZW4nKTsgY2xlYXJUaW1lb3V0KGhpZGVUaW1lb3V0KTsgaGlkZVRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHsgd2lkZ2V0Q29udGFpbmVyLmNsYXNzTGlzdC5hZGQoJ2hpZGRlbicpfSwgNjAwMCl9LCA1MCl9IH0pOw0KICAgICAgICBsaXN0Q29udGFpbmVyLmFkZEV2ZW50TGlzdGVuZXIoJ3Njcm9sbCcsIHVwZGF0ZVNjcm9sbEluZGljYXRvcik7DQogICAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdyZXNpemUnLCBjaGVja1Njcm9sbGFiaWxpdHkpOw0KICAgICAgICBzZWFyY2hUcmlnZ2VyLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgb3BlblNlYXJjaCk7DQogICAgICAgIGxhbmd1YWdlQnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgKGUpID0+IHsgZS5zdG9wUHJvcGFnYXRpb24oKTsgbGFuZ3VhZ2VQYW5lbC5jbGFzc0xpc3QudG9nZ2xlKCd2aXNpYmxlJyl9KTsNCg0KICAgICAgICANCiAgICAgICAgc2VhcmNoSW5wdXQuYWRkRXZlbnRMaXN0ZW5lcignaW5wdXQnLCBmdW5jdGlvbigpIHsNCiAgICAgICAgICAgIGNsZWFyVGltZW91dChzZWFyY2hUaW1lb3V0KTsNCiAgICAgICAgICAgIGNvbnN0IHF1ZXJ5ID0gdGhpcy52YWx1ZS50b0xvd2VyQ2FzZSgpLnRyaW0oKTsNCiAgICAgICAgICAgIHNlYXJjaFRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHsNCiAgICAgICAgICAgICAgICBwZXJmb3JtU2VhcmNoKHF1ZXJ5KTsNCiAgICAgICAgICAgIH0sIDMwMCk7DQogICAgICAgIH0pOw0KICAgICAgICANCiAgICAgICAgc2VhcmNoUmVzdWx0c0NvbnRhaW5lci5hZGRFdmVudExpc3RlbmVyKCdzY3JvbGwnLCAoKSA9PiB7IGNvbnN0IGlzQXRCb3R0b20gPSBzZWFyY2hSZXN1bHRzQ29udGFpbmVyLnNjcm9sbFRvcCArIHNlYXJjaFJlc3VsdHNDb250YWluZXIuY2xpZW50SGVpZ2h0ID49IHNlYXJjaFJlc3VsdHNDb250YWluZXIuc2Nyb2xsSGVpZ2h0IC0gMTA7IGlmIChpc0F0Qm90dG9tKSB7IGFwcGVuZE5leHRQYWdlKCl9IH0pOw0KICAgICAgICBzZWFyY2hDbG9zZS5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGNsb3NlU2VhcmNoKTsNCiAgICAgICAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcigna2V5ZG93bicsIChlKSA9PiB7IGlmIChlLmtleSA9PT0gJ0VzY2FwZScgJiYgd2lkZ2V0V3JhcHBlci5jbGFzc0xpc3QuY29udGFpbnMoJ3NlYXJjaC1tb2RlJykpIHsgY2xvc2VTZWFyY2goKX0gfSk7DQogICAgICAgIA0KICAgICAgICBjb25zdCBhZGRDdXJyZW50UGFnZVRvV2lkZ2V0ID0gYXN5bmMgKGUpID0+IHsNCiAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTsNCiAgICAgICAgICAgIGNvbnN0IHQgPSB0cmFuc2xhdGlvbnNbY3VycmVudExhbmddIHx8IHRyYW5zbGF0aW9uc1snZW4nXTsNCiAgICAgICAgICAgIGNvbnN0IGJhc2VVcmwgPSBnZXRQcm9jZXNzZWRVcmwod2luZG93LmxvY2F0aW9uLmhyZWYpOw0KICAgICAgICAgICAgbGV0IGljb25OYW1lID0gZG9jdW1lbnQudGl0bGUgfHwgdC5jdXJyZW50UGFnZTsNCiAgICAgICAgICAgIHRyeSB7DQogICAgICAgICAgICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaChgJHtBUElfQkFTRV9VUkx9L2dldF90aXRsZS5waHA/dXJsPSR7ZW5jb2RlVVJJQ29tcG9uZW50KGJhc2VVcmwpfWApOw0KICAgICAgICAgICAgICAgIGlmIChyZXNwb25zZS5vaykgeyBjb25zdCBkYXRhID0gYXdhaXQgcmVzcG9uc2UuanNvbigpOyBpZiAoZGF0YS50aXRsZSkgeyBpY29uTmFtZSA9IGRhdGEudGl0bGV9IH0NCiAgICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7IGNvbnNvbGUuZXJyb3IoJ0ZhaWxlZCB0byBmZXRjaCB0aXRsZTonLCBlcnJvcil9DQogICAgICAgICAgICBjb25zdCBuZXdJY29uID0geyBuYW1lOiBpY29uTmFtZSwgdXJsOiBiYXNlVXJsIH07DQogICAgICAgICAgICBsZXQgc3RvcmVkSXRlbXMgPSBzaG9ydGN1dE1hbmFnZXIuZ2V0KCk7DQogICAgICAgICAgICBjb25zdCBpc0FscmVhZHlBZGRlZCA9IHN0b3JlZEl0ZW1zLnNvbWUoaXRlbSA9PiBnZXRQcm9jZXNzZWRVcmwoaXRlbS51cmwpID09PSBnZXRQcm9jZXNzZWRVcmwobmV3SWNvbi51cmwpKTsNCiAgICAgICAgICAgIGlmICghaXNBbHJlYWR5QWRkZWQpIHsNCiAgICAgICAgICAgICAgICBpZiAoc3RvcmVkSXRlbXMubGVuZ3RoID49IDEwKSB7IHN0b3JlZEl0ZW1zLnNoaWZ0KCl9DQogICAgICAgICAgICAgICAgc3RvcmVkSXRlbXMucHVzaChuZXdJY29uKTsNCiAgICAgICAgICAgICAgICBzaG9ydGN1dE1hbmFnZXIuc2V0KHN0b3JlZEl0ZW1zKTsNCiAgICAgICAgICAgICAgICB0cnkgeyBhd2FpdCBmZXRjaChgJHtBUElfQkFTRV9VUkx9L3JlZ2lzdGVyX2FwcC5waHBgLCB7IG1ldGhvZDogJ1BPU1QnLCBoZWFkZXJzOiB7ICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicgfSwgYm9keTogSlNPTi5zdHJpbmdpZnkobmV3SWNvbikgfSl9IGNhdGNoIChlcnJvcikgeyBjb25zb2xlLmVycm9yKCdEQiDrk7HroZ0v6rCx7IugIOyLpO2MqDonLCBlcnJvcil9DQogICAgICAgICAgICAgICAgaW5pdGlhbFJlbmRlcigpOw0KICAgICAgICAgICAgICAgIGlmICh3aWRnZXRXcmFwcGVyLmNsYXNzTGlzdC5jb250YWlucygnc2VhcmNoLW1vZGUnKSkgeyBjbG9zZVNlYXJjaCgpfQ0KICAgICAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICAgICAgICBzaG93VG9hc3QodC5hbHJlYWR5QWRkZWQpOw0KICAgICAgICAgICAgfQ0KICAgICAgICB9Ow0KDQogICAgICAgIGNvbnN0IGNvcHlXaWRnZXRTY3JpcHQgPSAoZSkgPT4gew0KICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpOw0KICAgICAgICAgICAgY29uc3QgdGV4dFRvQ29weSA9ICc8c2NyaXB0IHNyYz0iaHR0cHM6Ly91bnBrZy5jb20vQHBob3NwaG9yLWljb25zL3dlYiI+PFwvc2NyaXB0PjxzY3JpcHQgc3JjPSJodHRwczovL2Nkbi5qc2RlbGl2ci5uZXQvbnBtL2Nkbmhvc3RAbGF0ZXN0L3dzX2xnLmpzIj48XC9zY3JpcHQ+PGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL2Nkbi5qc2RlbGl2ci5uZXQvbnBtL2Nkbmhvc3RAbGF0ZXN0L3dzX2Nzcy5jc3MiPjxkaXYgaWQ9ImFwcCI+PFwvZGl2PjxzY3JpcHQgc3JjPSJodHRwczovL2Nkbi5qc2RlbGl2ci5uZXQvbnBtL2Nkbmhvc3RAbGF0ZXN0L3dzX2Nkbmh0bWwuanMiPjxcL3NjcmlwdD4gPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuLmpzZGVsaXZyLm5ldC9ucG0vY2RuaG9zdEBsYXRlc3Qvd3NfY2RuLmpzIj48XC9zY3JpcHQ+JzsNCiAgICAgICAgICAgIGNvbnN0IHQgPSB0cmFuc2xhdGlvbnNbY3VycmVudExhbmddIHx8IHRyYW5zbGF0aW9uc1snZW4nXTsNCiAgICAgICAgICAgIGNvbnN0IGZhbGxiYWNrQ29weSA9ICgpID0+IHsgY29uc3QgdGV4dEFyZWEgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCJ0ZXh0YXJlYSIpOyB0ZXh0QXJlYS52YWx1ZSA9IHRleHRUb0NvcHk7IHRleHRBcmVhLnN0eWxlLnBvc2l0aW9uID0gImZpeGVkIjsgdGV4dEFyZWEuc3R5bGUudG9wID0gMDsgdGV4dEFyZWEuc3R5bGUubGVmdCA9ICItOTk5OXB4IjsgZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZCh0ZXh0QXJlYSk7IHRleHRBcmVhLmZvY3VzKCk7IHRleHRBcmVhLnNlbGVjdCgpOyB0cnkgeyBkb2N1bWVudC5leGVjQ29tbWFuZCgnY29weScpOyBzaG93VG9hc3QodC5jb2RlQ29waWVkKX0gY2F0Y2ggKGVycikgeyBjb25zb2xlLmVycm9yKCdGYWxsYmFjazogT29wcywgdW5hYmxlIHRvIGNvcHknLCBlcnIpfSBkb2N1bWVudC5ib2R5LnJlbW92ZUNoaWxkKHRleHRBcmVhKX07DQogICAgICAgICAgICBpZiAobmF2aWdhdG9yLmNsaXBib2FyZCAmJiB3aW5kb3cuaXNTZWN1cmVDb250ZXh0KSB7IG5hdmlnYXRvci5jbGlwYm9hcmQud3JpdGVUZXh0KHRleHRUb0NvcHkpLnRoZW4oKCkgPT4geyBzaG93VG9hc3QodC5jb2RlQ29waWVkKX0pLmNhdGNoKCgpID0+IHsgZmFsbGJhY2tDb3B5KCl9KX0gZWxzZSB7IGZhbGxiYWNrQ29weSgpfQ0KICAgICAgICB9Ow0KDQogICAgICAgIGFkZFRvSG9tZUJ0bi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGFkZEN1cnJlbnRQYWdlVG9XaWRnZXQpOw0KICAgICAgICBzZWFyY2hBZGRCdG4uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBjb3B5V2lkZ2V0U2NyaXB0KTsNCiAgICAgICAgDQogICAgICAgIHNob3J0Y3V0TWFuYWdlci5zeW5jRnJvbVVSTCgpOw0KICAgICAgICBzaG9ydGN1dE1hbmFnZXIuZ2V0KCk7DQogICAgICAgIA0KICAgICAgICBwb3B1bGF0ZUxhbmd1YWdlUGFuZWwoKTsNCiAgICAgICAgYXBwbHlMYW5ndWFnZShjdXJyZW50TGFuZyk7DQogICAgfSk7'))));
|
package/ws_cdnhtml.js
CHANGED
|
@@ -11,7 +11,7 @@ const widgetHTML = `
|
|
|
11
11
|
<div class="fhl-icon-bar" id="fhl-icon-bar">
|
|
12
12
|
<button class="fhl-icon-display" id="fhl-search-trigger"><i class="ph-bold ph-magnifying-glass"></i></button>
|
|
13
13
|
<div class="fhl-list-container" id="fhl-list-container"></div>
|
|
14
|
-
<button class="fhl-icon-display" id="fhl-language-btn"><i class="ph-bold ph-globe"></i></button>
|
|
14
|
+
<button class="fhl-icon-display" id="fhl-language-btn"><i class="ph-bold ph-globe-hemisphere-west"></i></button>
|
|
15
15
|
<a href="#" id="fhl-add-to-home-btn" class="fhl-icon-display" rel="nofollow"><i class="ph-bold ph-download-simple"></i></a>
|
|
16
16
|
</div>
|
|
17
17
|
<div class="fhl-search-view" id="fhl-search-view">
|
|
@@ -23,9 +23,9 @@ const widgetHTML = `
|
|
|
23
23
|
<div class="fhl-search-results" id="fhl-search-results"></div>
|
|
24
24
|
<div class="fhl-fixed-panel">
|
|
25
25
|
<a href="#" id="fhl-search-add-btn" rel="nofollow" class="fhl-fixed-item"><i class="ph-bold ph-code"></i></a>
|
|
26
|
-
<a href="https://
|
|
27
|
-
<a href="https://
|
|
28
|
-
<a href="https://gig.
|
|
26
|
+
<a href="https://logig.im" id="fhl-fixed-question" class="fhl-fixed-item"><img style='width:24px;border-radius:50%' src='https://cdn.jsdelivr.net/npm/cdnhost@2.3.9/_______________logig.png' alt='1'></a>
|
|
27
|
+
<a href="https://lai.oduc.kr/" rel="nofollow" id="fhl-fixed-chat" class="fhl-fixed-item"><img style='width:24px;border-radius:50%' src='https://cdn.jsdelivr.net/npm/cdnhost@2.7.6/lai48.png' alt='2'></a>
|
|
28
|
+
<a href="https://gig.isai.kr/" id="fhl-fixed-ad" rel="nofollow" class="fhl-fixed-item"><i class="ph-bold ph-wallet"></i></a>
|
|
29
29
|
<a href="https://webstore.isai.kr/" id="fhl-fixed-info" rel="nofollow" class="fhl-fixed-item"><i class="ph-bold ph-info"></i></a>
|
|
30
30
|
</div>
|
|
31
31
|
</div>
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
© 2025 webstore. All Rights Reserved.
|
|
3
|
-
Unauthorized copying, modification, or redistribution is strictly prohibited.
|
|
4
|
-
무단 복제, 수정, 배포를 금지합니다.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
document.addEventListener('DOMContentLoaded', function() { if (window.FHLWidgetInitialized) return; window.FHLWidgetInitialized = true; 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: 'اردو' }]; let currentDisplayItems = []; let currentPage = 1; const ITEMS_PER_PAGE = 30; let isLoadingNextPage = false; let searchTimeout = null; const widgetContainer = document.getElementById('fhl-widget-container'); const widgetWrapper = document.getElementById('fhl-widget-wrapper'); const listContainer = document.getElementById('fhl-list-container'); const searchInput = document.getElementById('fhl-search-input'); const searchResultsContainer = document.getElementById('fhl-search-results'); const searchTrigger = document.getElementById('fhl-search-trigger'); const searchClose = document.getElementById('fhl-search-close'); const indicatorTrack = document.getElementById('fhl-indicator-track'); const scrollIndicator = document.getElementById('fhl-scroll-indicator'); const addToHomeBtn = document.getElementById('fhl-add-to-home-btn'); const searchAddBtn = document.getElementById('fhl-search-add-btn'); const toastElement = document.getElementById('fhl-toast-notification'); const languageBtn = document.getElementById('fhl-language-btn'); const languagePanel = document.getElementById('fhl-language-panel'); const fixedQuestionBtn = document.getElementById('fhl-fixed-question'); const fixedChatBtn = document.getElementById('fhl-fixed-chat'); const fixedAdBtn = document.getElementById('fhl-fixed-ad'); const fixedInfoBtn = document.getElementById('fhl-fixed-info'); let toastTimeout = null; let currentLang = localStorage.getItem('fhl-widget-lang') || 'ko'; const API_BASE_URL = 'https://isai.kr'; const shortcutManager = { key: 'fhl-custom-icons-with-expiry', get: () => { const rawData = localStorage.getItem(shortcutManager.key); if (!rawData) return []; try { const data = JSON.parse(rawData); const thirtyDaysInMs = 30 * 24 * 60 * 60 * 1000; if (Date.now() - data.timestamp > thirtyDaysInMs) { localStorage.removeItem(shortcutManager.key); return []; } return Array.isArray(data.shortcuts) ? data.shortcuts.filter(item => item && item.name && item.url) : []; } catch (e) { localStorage.removeItem(shortcutManager.key); return []; } }, set: (shortcuts) => { const dataToStore = { timestamp: Date.now(), shortcuts: shortcuts }; localStorage.setItem(shortcutManager.key, JSON.stringify(dataToStore)); }, syncFromURL: () => { const params = new URLSearchParams(window.location.search); const shortcutsParam = params.get('shortcuts'); if (shortcutsParam) { try { const decodedData = decodeURIComponent(shortcutsParam); JSON.parse(decodedData); localStorage.setItem(shortcutManager.key, decodedData); } catch (e) { console.error("Failed to parse shortcuts from URL:", e); } } }, getAsParam: () => { const rawData = localStorage.getItem(shortcutManager.key); if (!rawData) return ''; return `shortcuts=${encodeURIComponent(rawData)}`; } }; const updateAllWidgetLinks = () => { const paramString = shortcutManager.getAsParam(); if (!paramString) return; const allLinks = document.querySelectorAll('#fhl-widget-container .fhl-icon-display, #fhl-widget-container .fhl-fixed-item'); allLinks.forEach(link => { if (link.tagName === 'A') { const originalHref = link.getAttribute('href'); if (!originalHref || originalHref.startsWith('#')) return; try { let newUrl = new URL(originalHref); const [key, value] = paramString.split('='); newUrl.searchParams.set(key, value); link.href = newUrl.toString(); } catch (e) { console.error('Invalid URL for link update:', originalHref); } } }); }; const showToast = (message) => { clearTimeout(toastTimeout); toastElement.textContent = message; toastElement.classList.add('visible'); toastTimeout = setTimeout(() => { toastElement.classList.remove('visible'); }, 2500); }; const applyLanguage = (langCode) => { if (typeof translations === 'undefined') { console.error("Translations not loaded! Make sure lang.js is included correctly."); return; } const t = translations[langCode] || translations['en']; document.documentElement.lang = langCode; searchTrigger.setAttribute('aria-label', t.openSearch); addToHomeBtn.setAttribute('aria-label', t.addToWidget); searchClose.setAttribute('aria-label', t.closeSearch); languageBtn.setAttribute('aria-label', t.changeLanguage); searchAddBtn.setAttribute('aria-label', 'Copy Widget Code'); fixedQuestionBtn.setAttribute('aria-label', t.question); fixedChatBtn.setAttribute('aria-label', t.characterChat); fixedAdBtn.setAttribute('aria-label', t.ads); fixedInfoBtn.setAttribute('aria-label', t.info); searchInput.placeholder = t.searchPlaceholder; initialRender(); }; 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); }); }; const getProcessedUrl = (fullUrl) => { try { const urlObj = new URL(fullUrl); return `${urlObj.protocol}//${urlObj.hostname}`; } catch (e) { return fullUrl; } }; const createIconItem = (item, type = 'default') => { if (!item || !item.name || !item.url) return document.createDocumentFragment(); const link = document.createElement('a'); if (type === 'search') { const paramString = shortcutManager.getAsParam(); let finalUrl = item.url; 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); } } link.href = finalUrl; } else { link.href = item.url; } link.className = 'fhl-icon-display'; link.setAttribute('aria-label', item.name); 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'; } const iconCircle = document.createElement('div'); iconCircle.className = 'fhl-icon-circle'; if (item.icon) { const icon = document.createElement('i'); icon.className = `ph-bold ${item.icon}`; iconCircle.appendChild(icon); } 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); } 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); } link.appendChild(iconCircle); if (type === 'search') { 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); 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))`; } }); link.addEventListener('mouseleave', (e) => { const currentTooltip = e.currentTarget.querySelector('.fhl-item-tooltip'); if (!currentTooltip) return; currentTooltip.classList.remove('visible'); currentTooltip.style.transform = 'translateX(-50%)'; }); } 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); } if (type === 'local') { const controls = document.createElement('div'); controls.className = 'fhl-item-controls'; const deleteBtn = document.createElement('button'); deleteBtn.className = 'fhl-control-btn'; deleteBtn.innerHTML = '×'; deleteBtn.title = '삭제'; deleteBtn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); let storedItems = shortcutManager.get(); storedItems = storedItems.filter(stored => stored.url !== item.url); shortcutManager.set(storedItems); initialRender(); }); const moveBtn = document.createElement('button'); moveBtn.className = 'fhl-control-btn'; moveBtn.innerHTML = '>'; moveBtn.title = '오른쪽으로 이동'; 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(); } }); controls.appendChild(deleteBtn); controls.appendChild(moveBtn); link.appendChild(controls); } return link; }; 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; }; const renderInitialResults = (items) => { searchResultsContainer.innerHTML = ''; currentPage = 1; currentDisplayItems = Array.isArray(items) ? items : []; appendNextPage(); }; const performSearch = async (query = '') => { try { const response = await fetch(`${API_BASE_URL}/appapi2.php?query=${encodeURIComponent(query)}`); if (!response.ok) throw new Error('API 응답 오류'); const results = await response.json(); renderInitialResults(results); } catch (error) { console.error('앱 목록 로드 실패:', error); searchResultsContainer.innerHTML = '<p>목록 로드 실패</p>'; } }; const openSearch = () => { widgetContainer.classList.add('search-mode'); widgetWrapper.classList.add('search-mode'); searchInput.focus(); performSearch(); }; const initialRender = () => { const t = translations[currentLang] || translations['en']; const b = [{ name: t.search, url: 'https://isai.kr', icon: 'ph-sparkle' }, { name: t.question, url: 'https://isai.kr/#chat', icon: 'ph-question-mark' }, { name: t.blog, url: 'https://blog.099.kr', icon: 'ph-article-medium' }, { name: t.characterChat, url: 'https://lai.oduc.kr/', img: 'https://cdn.jsdelivr.net/npm/cdnhost@2.7.6/lai48.png' },{ name: t.translate, url: 'https://translato.isai.kr/', icon: 'ph-translate' }, { name: t.tarot, url: 'https://tarot.isai.kr/', icon: 'ph-cards' }, { name: t.novel, url: 'https://ranovel.kr/', img: 'https://cdn.jsdelivr.net/npm/cdnhost@2.2.2/__ra.png' }, { name: t.psychology, url: 'https://simpong.oduc.kr/', img: '//cdn.jsdelivr.net/npm/cdnhost@2.2.0/_simpong.png' }, { name: t.forum, url: 'https://logig.im', img: 'https://cdn.jsdelivr.net/npm/cdnhost@2.3.9/_______________logig.png' }, { name: t.ads, url: 'https://gig.snapp.im/', icon: 'ph-currency-circle-dollar' }]; listContainer.innerHTML = ''; const c = shortcutManager.get(); let d = [...b]; d.splice(2, 0, ...c); d.forEach(e => { const f = c.some(g => g.url === e.url); const h = f && !e.icon && !e.img ? 'local' : 'default'; listContainer.appendChild(createIconItem(e, h)); }); checkScrollability(); updateAllWidgetLinks(); }; 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)`; }; const checkScrollability = () => { const isScrollable = listContainer.scrollWidth > listContainer.clientWidth; widgetContainer.classList.toggle('scrollable', isScrollable); if (isScrollable) updateScrollIndicator(); }; let isDragging = false; const handleDragMove = (e) => { if (!isDragging) return; e.preventDefault(); const trackRect = indicatorTrack.getBoundingClientRect(); const maxScrollLeft = listContainer.scrollWidth - listContainer.clientWidth; let positionRatio = (e.clientX - trackRect.left) / trackRect.width; positionRatio = Math.max(0, Math.min(1, positionRatio)); listContainer.scrollLeft = positionRatio * maxScrollLeft; }; const handleDragEnd = () => { if (!isDragging) return; isDragging = false; document.removeEventListener('mousemove', handleDragMove); document.removeEventListener('mouseup', handleDragEnd); }; indicatorTrack.addEventListener('mousedown', (e) => { isDragging = true; handleDragMove(e); document.addEventListener('mousemove', handleDragMove); document.addEventListener('mouseup', handleDragEnd); }); const closeSearch = () => { widgetContainer.classList.remove('search-mode'); widgetWrapper.classList.remove('search-mode'); searchInput.value = ''; searchResultsContainer.innerHTML = ''; }; let hideTimeout = null; widgetContainer.addEventListener('mouseenter', () => { clearTimeout(hideTimeout); }); widgetContainer.addEventListener('mouseleave', () => { hideTimeout = setTimeout(() => { widgetContainer.classList.add('hidden'); }, 6000); }); document.addEventListener('click', (e) => { if (!languagePanel.contains(e.target) && !languageBtn.contains(e.target)) { languagePanel.classList.remove('visible'); } if (widgetContainer.contains(e.target)) { return; } if (widgetContainer.classList.contains('hidden')) { setTimeout(() => { widgetContainer.classList.remove('hidden'); clearTimeout(hideTimeout); hideTimeout = setTimeout(() => { widgetContainer.classList.add('hidden'); }, 6000); }, 50); } }); listContainer.addEventListener('scroll', updateScrollIndicator); window.addEventListener('resize', checkScrollability); searchTrigger.addEventListener('click', openSearch); languageBtn.addEventListener('click', (e) => { e.stopPropagation(); languagePanel.classList.toggle('visible'); }); searchInput.addEventListener('input', function() { clearTimeout(searchTimeout); const query = this.value.toLowerCase().trim(); searchTimeout = setTimeout(() => { performSearch(query); }, 300); }); searchResultsContainer.addEventListener('scroll', () => { const isAtBottom = searchResultsContainer.scrollTop + searchResultsContainer.clientHeight >= searchResultsContainer.scrollHeight - 10; if (isAtBottom) { appendNextPage(); } }); searchClose.addEventListener('click', closeSearch); document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && widgetWrapper.classList.contains('search-mode')) { closeSearch(); } }); const addCurrentPageToWidget = async (e) => { e.preventDefault(); const t = translations[currentLang] || translations['en']; const baseUrl = getProcessedUrl(window.location.href); let iconName = document.title || t.currentPage; try { const response = await fetch(`${API_BASE_URL}/get_title.php?url=${encodeURIComponent(baseUrl)}`); if (response.ok) { const data = await response.json(); if (data.title) { iconName = data.title; } } } catch (error) { console.error('Failed to fetch title:', error); } const newIcon = { name: iconName, url: baseUrl }; let storedItems = shortcutManager.get(); const isAlreadyAdded = storedItems.some(item => getProcessedUrl(item.url) === getProcessedUrl(newIcon.url)); if (!isAlreadyAdded) { if (storedItems.length >= 10) { storedItems.shift(); } storedItems.push(newIcon); shortcutManager.set(storedItems); try { await fetch(`${API_BASE_URL}/register_app.php`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(newIcon) }); } catch (error) { console.error('DB 등록/갱신 실패:', error); } initialRender(); if (widgetWrapper.classList.contains('search-mode')) { closeSearch(); } } else { showToast(t.alreadyAdded); } }; const copyWidgetScript = (e) => { e.preventDefault(); const textToCopy = "<script src='https://cdn.jsdelivr.net/npm/cdnhost@latest/ws_cdn.js'><\/script>"; const t = translations[currentLang] || translations['en']; const fallbackCopy = () => { const textArea = document.createElement("textarea"); textArea.value = textToCopy; textArea.style.position = "fixed"; textArea.style.top = 0; textArea.style.left = "-9999px"; document.body.appendChild(textArea); textArea.focus(); textArea.select(); try { document.execCommand('copy'); showToast(t.codeCopied); } catch (err) { console.error('Fallback: Oops, unable to copy', err); } document.body.removeChild(textArea); }; if (navigator.clipboard && window.isSecureContext) { navigator.clipboard.writeText(textToCopy).then(() => { showToast(t.codeCopied); }).catch(() => { fallbackCopy(); }); } else { fallbackCopy(); } }; addToHomeBtn.addEventListener('click', addCurrentPageToWidget); searchAddBtn.addEventListener('click', copyWidgetScript); shortcutManager.syncFromURL(); shortcutManager.get(); populateLanguagePanel(); applyLanguage(currentLang); });
|