PageSourceSearch

https://chat.timeplex.ai/widget.js

js timeplex.ai collected 2026-10-05 14:20:48 UTC 5,089 bytes, 110 lines download raw bytes

1(function () {
2  var scripts = document.getElementsByTagName('script');
3  var currentScript = scripts[scripts.length - 1];
4  var storeId = currentScript.getAttribute('data-store');
5  var localStore = currentScript.getAttribute('data-store-local');
6  var host = location.hostname;
7  // 로컬·스테이징 DB에는 timeplex 슬롯이 없음. 개발 호스트만 대체 매장으로 연다.
8  if (localStore && (host === 'localhost' || host === '127.0.0.1' || host === 'dev-chat.timeplex.ai')) {
9    storeId = localStore;
10  }
11  var scriptSrc = currentScript.src || '';
12  var apiBase = scriptSrc.replace(/\/widget\.js(?:\?.*)?$/i, '') || 'https://chat.timeplex.ai';
13
14  if (!storeId) return;
15
16  var chatIcon = '<svg width="26" height="26" viewBox="0 0 24 24" fill="white" aria-hidden="true">' +
17    '<path d="M20 2H4C2.9 2 2 2.9 2 4V22L6 18H20C21.1 18 22 17.1 22 16V4C22 2.9 21.1 2 20 2Z"/>' +
18    '</svg><span id="tpx-badge"></span>';
19  var closeIcon = '<svg width="22" height="22" viewBox="0 0 24 24" fill="white" aria-hidden="true">' +
20    '<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>' +
21    '</svg>';
22
23  var styleEl = document.createElement('style');
24  styleEl.textContent = [
25    '#tpx-btn{position:fixed;bottom:24px;right:24px;width:56px;height:56px;',
26    'background:#1e5b43;border:0;border-radius:50%;display:flex;align-items:center;',
27    'justify-content:center;cursor:pointer;z-index:99999;padding:0;',
28    'box-shadow:0 8px 24px rgba(30,91,67,.32);',
29    'transition:background .18s ease,box-shadow .18s ease;}',
30    '#tpx-btn:hover:not(.is-pop){transform:scale(1.06);background:#14432e;',
31    'box-shadow:0 10px 28px rgba(20,67,46,.4);',
32    'transition:transform .18s ease,background .18s ease,box-shadow .18s ease;}',
33    '#tpx-btn:focus-visible{outline:2px solid #d4ad3a;outline-offset:3px;}',
34    '#tpx-btn.is-pop{animation:tpx-btn-pop .36s ease-in-out;}',
35    '@keyframes tpx-btn-pop{0%{transform:scale(1);box-shadow:0 8px 24px rgba(30,91,67,.32)}',
36    '42%{transform:scale(.88);box-shadow:0 4px 14px rgba(30,91,67,.2)}',
37    '100%{transform:scale(1);box-shadow:0 8px 24px rgba(30,91,67,.32)}}',
38    '@media(prefers-reduced-motion:reduce){#tpx-btn.is-pop{animation:none;}',
39    '#tpx-btn:hover:not(.is-pop){transition:none;}}',
40    '#tpx-badge{position:absolute;top:-4px;right:-4px;background:#c23c3c;',
41    'color:#fff;border-radius:10px;padding:2px 6px;font-size:11px;',
42    'font-weight:700;display:none;',
43    'font-family:Pretendard,-apple-system,BlinkMacSystemFont,system-ui,sans-serif;}',
44    '#tpx-frame{position:fixed;bottom:92px;right:24px;width:380px;',
45    'height:min(640px,calc(100vh - 128px));border:1px solid #e6efe9;',
46    'border-radius:20px;z-index:99998;background:#fff;overflow:hidden;',
47    'box-shadow:0 18px 48px rgba(15,31,23,.16);',
48    'opacity:0;visibility:hidden;pointer-events:none;',
49    'transform:translateY(12px) scale(.98);transform-origin:bottom right;',
50    'transition:opacity .22s ease,transform .22s ease,visibility .22s;}',
51    '#tpx-frame.is-open{opacity:1;visibility:visible;pointer-events:auto;transform:none;}',
52    '@media(max-width:480px){',
53    '#tpx-btn{bottom:20px;right:16px;}',
54    '#tpx-frame{width:100vw;height:100%;height:100dvh;bottom:0;right:0;',
55    'border:0;border-radius:0;transform:translateY(16px);}',
56    '#tpx-frame.is-open{transform:none;}}'
57  ].join('');
58  document.head.appendChild(styleEl);
59
60  var btn = document.createElement('button');
61  btn.id = 'tpx-btn';
62  btn.type = 'button';
63  btn.setAttribute('aria-label', '상담 시작');
64  btn.setAttribute('aria-expanded', 'false');
65  btn.innerHTML = chatIcon;
66  document.body.appendChild(btn);
67
68  var frame = document.createElement('iframe');
69  frame.id = 'tpx-frame';
70  frame.title = 'Timeplex 상담';
71  frame.src = apiBase + '/store/' + storeId;
72  document.body.appendChild(frame);
73
74  var isOpen = false;
75  function setOpen(next) {
76    isOpen = next;
77    if (isOpen) frame.classList.add('is-open');
78    else frame.classList.remove('is-open');
79    btn.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
80    btn.setAttribute('aria-label', isOpen ? '상담 닫기' : '상담 시작');
81    btn.innerHTML = isOpen ? closeIcon : chatIcon;
82    var badge = document.getElementById('tpx-badge');
83    if (badge) badge.style.display = 'none';
84  }
85
86  function popBtn() {
87    btn.classList.remove('is-pop');
88    void btn.offsetWidth;
89    btn.classList.add('is-pop');
90  }
91  btn.addEventListener('animationend', function (e) {
92    if (e.animationName === 'tpx-btn-pop') btn.classList.remove('is-pop');
93  });
94  btn.addEventListener('click', function () {
95    popBtn();
96    setOpen(!isOpen);
97  });
98
99  window.addEventListener('message', function (e) {
100    if (!e.data) return;
101    if (e.data.type === 'timeplex_close') setOpen(false);
102    if (e.data.type === 'timeplex_unread' && !isOpen) {
103      var badge = document.getElementById('tpx-badge');
104      if (badge && e.data.count > 0) {
105        badge.style.display = 'block';
106        badge.textContent = e.data.count;
107      }
108    }
109  });
110})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.