PageSourceSearch

https://recastia.com/login/

html recastia.com collected 2026-10-04 20:58:12 UTC 85,041 bytes, 1,810 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en" dir="ltr">
3<head>
4    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5    <meta http-equiv="Content-Language" content="en" />
6    <title>Log in to Recastia</title>
7    <meta name="author" content="Recastia" />
8    <meta name="description" content="Recastia Login" />
9    <meta name="keywords" content="Recastia, Login" />
10    <meta name="viewport" content="width=device-width, initial-scale=1">
11    <meta name="robots" content="index,follow,max-image-preview:large">
12
13    <link rel="icon" href="/favicon.ico" type="image/x-icon" />
14
15        
15<script>
16    (function() {
17        fetch('/api/user/get-user', { method: 'GET', credentials: 'include' })
18            .then(function(res) { return res.json(); })
19            .then(function(data) {
20                if (data && data.code && data.code.toUpperCase() === 'OK') {
21                    window.location.href = '/dashboard/home';
22                }
23            })
24            .catch(function(err) {
25                console.log(err);
26            });
27    })();
28    </script>
28
29    
30    <!-- hreflang 多语言 SEO -->
31        <link rel="alternate" hreflang="en" href="https://recastia.com/login/" />
32    <link rel="alternate" hreflang="de-DE" href="https://recastia.com/de/login/" />
33    <link rel="alternate" hreflang="fr-FR" href="https://recastia.com/fr/login/" />
34    <link rel="alternate" hreflang="es-ES" href="https://recastia.com/es/login/" />
35    <link rel="alternate" hreflang="pt-PT" href="https://recastia.com/pt/login/" />
36    <link rel="alternate" hreflang="it-IT" href="https://recastia.com/it/login/" />
37    <link rel="alternate" hreflang="ar-SA" href="https://recastia.com/ar/login/" />
38    <link rel="alternate" hreflang="ja-JP" href="https://recastia.com/ja/login/" />
39    <link rel="alternate" hreflang="ko-KR" href="https://recastia.com/ko/login/" />
40    <link rel="alternate" hreflang="nl-NL" href="https://recastia.com/nl/login/" />
41    <link rel="alternate" hreflang="ru-RU" href="https://recastia.com/ru/login/" />
42    <link rel="alternate" hreflang="sv-SE" href="https://recastia.com/sv/login/" />
43    <link rel="alternate" hreflang="da-DK" href="https://recastia.com/da/login/" />
44    <link rel="alternate" hreflang="pl-PL" href="https://recastia.com/pl/login/" />
45    <link rel="alternate" hreflang="fi-FI" href="https://recastia.com/fi/login/" />
46    <link rel="alternate" hreflang="tr-TR" href="https://recastia.com/tr/login/" />
47    <link rel="alternate" hreflang="he-IL" href="https://recastia.com/he/login/" />
48    <link rel="alternate" hreflang="cs-CZ" href="https://recastia.com/cs/login/" />
49    <link rel="alternate" hreflang="hu-HU" href="https://recastia.com/hu/login/" />
50    <link rel="alternate" hreflang="ro-RO" href="https://recastia.com/ro/login/" />
51    <link rel="alternate" hreflang="uk-UA" href="https://recastia.com/uk/login/" />
52    <link rel="alternate" hreflang="el-GR" href="https://recastia.com/el/login/" />
53    <link rel="alternate" hreflang="th-TH" href="https://recastia.com/th/login/" />
54    <link rel="alternate" hreflang="hi-IN" href="https://recastia.com/hi/login/" />
55    <link rel="alternate" hreflang="ms-MY" href="https://recastia.com/ms/login/" />
56    <link rel="alternate" hreflang="id-ID" href="https://recastia.com/id/login/" />
57    <link rel="alternate" hreflang="vi-VN" href="https://recastia.com/vi/login/" />
58    <link rel="alternate" hreflang="x-default" href="https://recastia.com/login/" />
59
60    <!-- Google Tag Manager -->
61<script>
vendor: 327 bytes, lines 61-65
61(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
62new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
63j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
64'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
65})(window,document,'script','dataLayer','
65GTM-543ZSS9C
vendor: 3 bytes, line 65
65');
65</script>
65
66<!-- End Google Tag Manager -->
67
68<link rel="preload" href="/common-scss/common-dist/font/inter-variable.woff2" as="font" type="font/woff2" crossorigin />
69<link rel="stylesheet" href="/common-scss/common-dist/common.min.css?5eb777068ed5961fc6c52587c898e893" />
70<link rel="stylesheet" href="/langTool/css/langTool.css?73cd0f86430d3589c581e61f6fa6ea4c" />
71
72<script src="/js/lib/jquery-4.0.0.min.js?6b005ce95c0d83055f9b837e7c534974"></script>
vendor: 1 bytes, line 72
72
73<script src="/js/lib/jquery-migrate-4.0.2.min.js?02df650e15b204ae42950a3fab94f370"></script>
vendor: 1 bytes, line 73
73
74<script src="/js/utils/utils.js?4c1b5850f864b15833617cafd27d4430"></script>
vendor: 1 bytes, line 74
74
75<script src="/langTool/js/langTool.js?e239f1f64941dc98e309d6ebdf8defa0"></script>
75
76
77    <!-- 浏览器版本检测 -->
78    
78<script src="/js/utils/getExplore.js?e3929c6fb22a5b5b2b0ea5e10cb729d4"></script>
78
79
80    <!-- CSS -->
81    <link rel="stylesheet" href="/css/lib/fa.css?d6a508f82ef700e9fa5ace6e9f525247">
82    <link rel="stylesheet" href="/css/cores/redirectingBox.css?c069582381122c1a8cb40a874bf15f60">
83    <link rel="stylesheet" href="/login/login-dist/login.min.css?e665103ded606349290d16ec7a69d0ef">
84
85    <!-- i18n 运行时 -->
86    
86<script src="/js/i18n/md5/md5.min.js?a24e827c082bbcf53d524c97d8eb6907"></script>
86
87    
87<script src="/js/i18n/hf_i18n.js?478eb852223499700339b6d3df3f11b2"></script>
87
88    
88<script>hf_i18n.disablePreload = true;</script>
88
89
90    <!-- fh5 全局对象 + 窗口变量 -->
91    
91<script>
92        var fh5 = {
93            uId: '',
94            uType: 0,
95            isLoggedIn: false,
96            uLink: '',
97            uEmail: '',
98            uName: '',
99            website: '//localhost',
100            bookwebsite: '//localhost',
101            sessionId: 'session-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9),
102            sessionDefaultCId: '',
103            PrimaryAccountId: '',
104            loginType: 1,
105            userInfo: {},
106            lang: 'en',
107            AS3BucketName: '',
108            bookGlobal_isOpenEmail: false,
109            pageData: {
110                tips: {
111                    VerificationCodeError: 'Verification code error.',
112                    UnexpectedError2: 'An unexpected error occurred. Please try again.'
113                }
114            }
115        };
116
117        window.h5LoginType = 1;
118        window._RecaptchaResponse = '';
119        window.client_id = '';
120        window.ip_country = '';
121        window.h5SlsTracker = { send: function() {} };
122    </script>
122
123
124    <!-- 业务 JS:先安装 i18n 兜底覆写,再加载 login.min.js,确保其顶层 _gettext 调用也能命中覆写 -->
125        
125<script>
126    (function() {
127        var data = {};
128        // 预填充 downloadLangs,使原生 _gettext 在栈解析失败时也能命中缓存
129        // 避免 getCallerPath 返回空字符串导致 loadFileSync('') 向当前页发 XHR
130        var lang = hf_i18n.getLang();
131        if (lang !== 'en') {
132            if (lang === 'zh_cn' || lang === 'zh_tw') {
133                lang = lang.replace('_', '-');
134            }
135            var loginI18nPath = '/login/login-dist/i18n/login.min_' + lang.toLowerCase() + '.js';
136            hf_i18n.downloadLangs[loginI18nPath] = data;
137            // 兜底:getCallerPath 对 inline script 调用者可能返回 '',同时填充空串 key
138            // 这会全局覆盖 downloadLangs[''],但其他代码路径不会以空串查找,风险可忽略
139            hf_i18n.downloadLangs[''] = data;
140        }
141        var origGettext = hf_i18n._gettext.bind(hf_i18n);
142        hf_i18n._gettext = function(text) {
143            // 优先查内联 JSON(零延迟,Safari 兼容)
144            var key = md5(text);
145            if (data.hasOwnProperty(key)) return data[key];
146            // 兜底走原生 _gettext(覆盖动态注册的翻译条目)
147            return origGettext(text);
148        };
149    })();
150    </script>
150
151    
151<script src="/login/login-dist/login.min.js?1657c50f6e4346ee4916e8e47c1e33b3"></script>
151
152
153    
153<script>
154        function setHtmlSize() {
155            const minFont = 18;
156            const maxFont = 24;
157            const scaleFactor = 50;
158            const fontSize = Math.min(maxFont, Math.max(minFont, window.innerWidth / scaleFactor));
159            document.documentElement.style.fontSize = fontSize + 'px';
160        }
161        setHtmlSize();
162    </script>
162
163</head>
164
165<body>
166
167<!-- Google Tag Manager (noscript) -->
vendor: 68 bytes, lines 167-168
167
168<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=
168GTM-543ZSS9C
vendor: 83 bytes, lines 168-169
168"
169height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
170<!-- End Google Tag Manager (noscript) -->
171
172<div class="h5-login-container ">
173    <div class="h5-login-layout flex">
174                <aside class="h5-login-aside flex justify-center items-center">
175            <div class="h5-login-showcase">
176                <a class="showcase-logo" href="/">
177                    <img src="/login/images/svg/showcase-logo.svg" height="30" alt="showcase-logo">
178                </a>
179                <div class="showcase-content">
180                    <img src="/login/images/png/showcase-img.png?1" width="100%" alt="showcase-img">
181                    <span>AI Content Repurposing from Any PDF</span>
182                </div>
183            </div>
184            <style>
185            .h5-login-showcase{
186                position: relative;
187                width: 100%;
188                height: 100%;
189                background-color: #596FFF;
190                background-image: url("/login/images/png/showcase-bg.png");
191                background-size: cover;
192                background-repeat: no-repeat;
193                background-position: center;
194                display: flex;
195                justify-content: center;
196                align-items: center;
197            }
198            .showcase-logo{
199                position: absolute;
200                top: 24px;
201                left: 24px;
202            }
203            .showcase-content{
204                max-width: 436px;
205                width: 90%;
206                font-family: var(--public-html-font-family);
207                font-weight: 500;
208                font-size: 0.75rem;
209                line-height: 100%;
210                text-align: center;
211                color: #FFFFFF;
212            }
213            .showcase-content img{
214                margin-bottom: 16px;
215            }
216            .showcase-content span {
217                font-weight: 500;
218                font-size: 18px;
219                line-height: 1.2;
220                text-align: center;
221                color: #FFFFFF;
222            }
223            </style>
224        </aside>
225                <main class="h5-login-content flex flex-col flex-center">
226
227            <div class="h5-login-header flex justify-between items-center">
228                <div class="cc-login-header-left flex items-center gap-10">
229                                        <div class="header-go-back flex flex-center">
230                        <img src="/login/images/svg/back.svg" alt="back">
231                    </div>
232                                        <a class="header-logo" href="/">
233                        <img src="/header/img/png/header-logo.png" alt="Recastia" width="146" height="30">
234                    </a>
235                </div>
236                <div class="cc-login-header-right">
237                    <div class="header-lang-tool">
238                        
239<div id="fh5-lang-tool"
240  class="cc-lang-tool cc-dropdown-trigger box "
241  data-lang-tool
242  data-lang-type="box"
243  data-cur-label="English"
244  data-cur-value="EN"
245  data-basic-lang="0">
246  <div id="lang-dropdown-btn"
247    class="cc-lang-label cc-lang-flex cc-dropdown-label "
248    data-toggle="">
249    <div class="cc-lang-flex">
250              <span class="cc-lang-earth" aria-label="English"></span>
251            <span class="cc-lang-text" data-text="en">EN</span>
252    </div>
253    <span class="cc-dropdown-arrow"></span>
254  </div>
255  <div id="lang-dropdown-menu" class="cc-lang-menu cc-dropdown-menu cc-scrollbar">
256    <div class="cc-dropdown-menu-fixed">
257      <ul class="cc-lang-list">
258                      <li class="cc-lang-item is-active" data-lang="en">
259                <a class="cc-lang-flex item-href" href="/login/">
260                  <span>English</span>
261                                  </a>
262              </li>
263                          <li class="cc-lang-item" data-lang="de">
264                <a class="cc-lang-flex item-href" href="/de/login/">
265                  <span>Deutsch</span>
266                                  </a>
267              </li>
268                          <li class="cc-lang-item" data-lang="fr">
269                <a class="cc-lang-flex item-href" href="/fr/login/">
270                  <span>Français</span>
271                                  </a>
272              </li>
273                          <li class="cc-lang-item" data-lang="es">
274                <a class="cc-lang-flex item-href" href="/es/login/">
275                  <span>Español</span>
276                                  </a>
277              </li>
278                          <li class="cc-lang-item" data-lang="pt">
279                <a class="cc-lang-flex item-href" href="/pt/login/">
280                  <span>Português</span>
281                                  </a>
282              </li>
283                          <li class="cc-lang-item" data-lang="it">
284                <a class="cc-lang-flex item-href" href="/it/login/">
285                  <span>Italiano</span>
286                                  </a>
287              </li>
288                          <li class="cc-lang-item" data-lang="ar">
289                <a class="cc-lang-flex item-href" href="/ar/login/">
290                  <span>عربي</span>
291                                  </a>
292              </li>
293                          <li class="cc-lang-item" data-lang="ja">
294                <a class="cc-lang-flex item-href" href="/ja/login/">
295                  <span>日本語</span>
296                                  </a>
297              </li>
298                          <li class="cc-lang-item" data-lang="ko">
299                <a class="cc-lang-flex item-href" href="/ko/login/">
300                  <span>한국어</span>
301                                  </a>
302              </li>
303                          <li class="cc-lang-item" data-lang="nl">
304                <a class="cc-lang-flex item-href" href="/nl/login/">
305                  <span>Nederlands</span>
306                                  </a>
307              </li>
308                          <li class="cc-lang-item" data-lang="ru">
309                <a class="cc-lang-flex item-href" href="/ru/login/">
310                  <span>Русский</span>
311                                  </a>
312              </li>
313                          <li class="cc-lang-item" data-lang="sv">
314                <a class="cc-lang-flex item-href" href="/sv/login/">
315                  <span>Svenska</span>
316                                  </a>
317              </li>
318                          <li class="cc-lang-item" data-lang="da">
319                <a class="cc-lang-flex item-href" href="/da/login/">
320                  <span>Dansk</span>
321                                  </a>
322              </li>
323                          <li class="cc-lang-item" data-lang="pl">
324                <a class="cc-lang-flex item-href" href="/pl/login/">
325                  <span>Polski</span>
326                                  </a>
327              </li>
328                          <li class="cc-lang-item" data-lang="fi">
329                <a class="cc-lang-flex item-href" href="/fi/login/">
330                  <span>Suomi</span>
331                                  </a>
332              </li>
333                          <li class="cc-lang-item" data-lang="tr">
334                <a class="cc-lang-flex item-href" href="/tr/login/">
335                  <span>Türkçe</span>
336                                  </a>
337              </li>
338                          <li class="cc-lang-item" data-lang="he">
339                <a class="cc-lang-flex item-href" href="/he/login/">
340                  <span>עִברִית</span>
341                                  </a>
342              </li>
343                          <li class="cc-lang-item" data-lang="cs">
344                <a class="cc-lang-flex item-href" href="/cs/login/">
345                  <span>Čeština</span>
346                                  </a>
347              </li>
348                          <li class="cc-lang-item" data-lang="hu">
349                <a class="cc-lang-flex item-href" href="/hu/login/">
350                  <span>Magyar</span>
351                                  </a>
352              </li>
353                          <li class="cc-lang-item" data-lang="ro">
354                <a class="cc-lang-flex item-href" href="/ro/login/">
355                  <span>Română</span>
356                                  </a>
357              </li>
358                          <li class="cc-lang-item" data-lang="uk">
359                <a class="cc-lang-flex item-href" href="/uk/login/">
360                  <span>Українська</span>
361                                  </a>
362              </li>
363                          <li class="cc-lang-item" data-lang="el">
364                <a class="cc-lang-flex item-href" href="/el/login/">
365                  <span>Ελληνικά</span>
366                                  </a>
367              </li>
368                          <li class="cc-lang-item" data-lang="th">
369                <a class="cc-lang-flex item-href" href="/th/login/">
370                  <span>ไทย</span>
371                                  </a>
372              </li>
373                          <li class="cc-lang-item" data-lang="hi">
374                <a class="cc-lang-flex item-href" href="/hi/login/">
375                  <span>Hindi</span>
376                                  </a>
377              </li>
378                          <li class="cc-lang-item" data-lang="ms">
379                <a class="cc-lang-flex item-href" href="/ms/login/">
380                  <span>Bahasa Melayu</span>
381                                  </a>
382              </li>
383                          <li class="cc-lang-item" data-lang="id">
384                <a class="cc-lang-flex item-href" href="/id/login/">
385                  <span>Bahasa Indonesia</span>
386                                  </a>
387              </li>
388                          <li class="cc-lang-item" data-lang="vi">
389                <a class="cc-lang-flex item-href" href="/vi/login/">
390                  <span>Tiếng Việt</span>
391                                  </a>
392              </li>
393                  </ul>
394    </div>
395  </div>
396</div>
397                    </div>
398                </div>
399            </div>
400
401                        <!-- ========== 登录面板 ========== -->
402            <div class="sign-in-box flex flex-col flex-center">
403                <div class="cc-login-form">
404                    <div class="cc-login-form-title mrb-36">Log in</div>
405                    <div class="cc-login-third-party flex items-center mrb-16 gap-12">
406                        <div id="login-google" class="login-google cc-login-other-btn">
407                            <div class="cc-login-other-item">
408                                <img src="/login/images/svg/google.svg" alt="google">
409                                <span class="text-google login-other-text">Continue with Google</span>
410                            </div>
411                            <div class="tips">
412                                Continue with Google                                <span class="triangle"></span>
413                            </div>
414                        </div>
415                        <div id="login-facebook" class="login-facebook cc-login-other-btn">
416                            <div class="cc-login-other-item">
417                                <img src="/login/images/svg/facebook.svg" alt="facebook">
418                            </div>
419                            <div class="tips">
420                                Continue with Facebook                                <span class="triangle"></span>
421                            </div>
422                        </div>
423                        <div id="login-microsoft" class="login-microsoft cc-login-other-btn">
424                            <div class="cc-login-other-item">
425                                <img src="/login/images/svg/microsoft.svg" alt="microsoft">
426                            </div>
427                            <div class="tips">
428                                Continue with Microsoft                                <span class="triangle"></span>
429                            </div>
430                        </div>
431                        <div id="login-apple" class="login-apple cc-login-other-btn">
432                            <div class="cc-login-other-item">
433                                <img src="/login/images/svg/apple.svg" alt="apple">
434                            </div>
435                            <div class="tips">
436                                Continue with Apple                                <span class="triangle"></span>
437                            </div>
438                        </div>
439                    </div>
440                    <div class="cc-login-divider mrb-24">
441                        <div class="line"></div>
442                        <div class="text">Or</div>
443                        <div class="line"></div>
444                    </div>
445                    <div class="cc-login-item-group mrb-16" data-type="email">
446                        <div class="cc-login-group-label mrb-8">Email</div>
447                        <input class="cc-login-group-input" name="login-email" type="text" placeholder="">
448                        <div class="cc-login-group-help mrt-10"></div>
449                    </div>
450                    <div class="cc-login-item-group mrb-8" data-type="password">
451                        <div class="cc-login-group-label mrb-8">Password</div>
452                        <input class="cc-login-group-input" name="login-password" type="password" placeholder="">
453                        <div class="eye-box"><span class="eye_icon"></span></div>
454                        <div class="cc-login-group-help mrt-10"></div>
455                    </div>
456                    <div class="cc-login-actions">
457                        <div class="cc-login-action-forget mrb-24">
458                            <button type="button" class="action-forget-link cc-hover-underline">Forgot password?</button>
459                        </div>
460                        <div class="cc-login-actions-btn mrb-12">Log in</div>
461                                                <div class="cc-action-sign-up mrb-24">
462                            <div>
463                                Don't have an account?                                <a class="cc-hover-underline" href="/login/?register">Sign up</a>
464                            </div>
465                        </div>
466                        <div class="cc-login-actions-service">
467                            By signing up, you agree to our                            <a href="/terms-of-service.php" class="underline cc-hover-text-blue" target="_blank">Terms of Service</a>
468                             and                             <a href="/privacy-policy.php" class="underline cc-hover-text-blue" target="_blank">Privacy Policy</a>
469                        </div>
470                    </div>
471                    <div class="cc-login-form-help mrt-10"></div>
472                </div>
473            </div>
474            
475            <!-- ========== 自定义 URL 面板(第三方登录后) ========== -->
476            <div class="customize-box flex flex-col flex-center">
477                <div class="cc-login-form">
478                    <div class="cc-login-form-title mrb-30">Customize Your URL</div>
479                    <div class="login-account mrb-7">
480                        <div class="login-account-avatar flex items-center justify-center"></div>
481                        <div class="login-account-name"></div>
482                        <div class="login-account-email"></div>
483                    </div>
484                    <div class="login-sub-title mrb-30 text-center">
485                        Want to change account?                        <sp
485an class="to-logout">
486                            <a class="cc-hover-underline cc-text-blue"
487                               href="/api/user/sign-out?from=/login/"
488                               data-logout>
489                                Log out                            </a>
490                        </span>
491                    </div>
492                    <div class="cc-login-item-group mrb-30" data-type="ulink">
493                        <div class="cc-login-group-label mrb-10">Brand URL <span>(optional)</span></div>
494                        <span class="cc-login-group-input-prefix">recastia.com /</span>
495                        <input class="cc-login-group-input link" name="custom-ulink" type="text" placeholder="your brand name">
496                        <div class="cc-login-group-suggest flex flex-col">
497                            <div class="suggest-title flex items-center gap-8">
498                                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
499                                    <circle cx="8" cy="8" r="8" fill="#FF5962" />
500                                    <path d="M8.75387 8.80156L8.99801 5.19766C9.03399 4.54865 8.5768 4 8 4C7.4232 4 6.96601 4.54865 7.00199 5.19766L7.24613 8.80156C7.27101 9.25036 7.60113 9.6 8 9.6C8.39887 9.6 8.72899 9.25036 8.75387 8.80156Z" fill="white" />
501                                    <path d="M8.70961 11.2C8.70961 11.6418 8.3919 12 8 12C7.6081 12 7.29039 11.6418 7.29039 11.2C7.29039 10.7582 7.6081 10.4 8 10.4C8.3919 10.4 8.70961 10.7582 8.70961 11.2Z" fill="white" />
502                                </svg>
503                                <span>The URL is already taken. Try these:</span>
504                            </div>
505                            <div class="suggest-loading">
506                                <div class="loading-box">
507                                    <div class="cc-loading-spin">
508                                        <i class="fa fa-spinner fa-spin"></i>
509                                    </div>
510                                </div>
511                            </div>
512                            <div class="suggest-list flex-1"></div>
513                        </div>
514                        <div class="cc-login-group-input-status">
515                            <div class="status-icon success">
516                                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="17" viewBox="0 0 16 17" fill="none">
517                                    <circle cx="8" cy="8.5" r="8" fill="#30C28B" />
518                                    <path d="M4 8L7 11.5L12 5.5" stroke="white" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" />
519                                </svg>
520                            </div>
521                            <div class="status-icon loading">
522                                <svg xmlns="http://www.w3.org/2000/svg" width="12" height="14" viewBox="0 0 12 14" fill="none">
523                                    <path d="M4.65789 2.20625C4.65789 2.65878 4.83256 3.09277 5.14348 3.41275C5.4544 3.73273 5.87609 3.9125 6.31579 3.9125C6.75549 3.9125 7.17718 3.73273 7.4881 3.41275C7.79901 3.09277 7.97368 2.65878 7.97368 2.20625C7.97368 1.75372 7.79901 1.31973 7.4881 0.999749C7.17718 0.679765 6.75549 0.5 6.31579 0.5C5.87609 0.5 5.4544 0.679765 5.14348 0.999749C4.83256 1.31973 4.65789 1.75372 4.65789 2.20625Z" fill="#888888" />
524                                </svg>
525                            </div>
526                        </div>
527                        <div class="cc-login-group-tips">Input your brand name to personalize the book and homepage link. This URL can not be changed once it's set.</div>
528                        <div class="cc-login-group-help mrt-10"></div>
529                    </div>
530                    <div class="cc-login-actions">
531                        <div class="cc-login-actions-btn">Skip and Sign Up</div>
532                    </div>
533                    <div class="cc-login-form-help mrt-10"></div>
534                </div>
535            </div>
536
537            <!-- ========== 忘记密码面板 ========== -->
538            <div class="forget-box flex flex-center"></div>
539
540            <!-- ========== 注册成功后输入邮件验证码面板 ========== -->
541            <div class="verify-box flex flex-center flex-1" style="display:none;"></div>
542
543
544            
545            <!-- ========== 重置密码面板 ========== -->
546            <div class="reset-password-box flex flex-center" style="display:none;"></div>
547        </main>
548    </div>
549</div>
550<script>
551(function($) {
552    $(function() {
553        const $toSignIn = $(".sign-up-box .to-sign-up a");
554        const $toSignUp = $(".sign-in-box .to-sign-up a");
555        const $forgetLink = $(".sign-in-box .action-forget-link");
556        const $backToSign = $(".header-go-back");
557        const $logoutLink = $("a[data-logout]");
558        const $signInEye = $(".sign-in-box .eye-box");
559        const $signUpEye = $(".sign-up-box .eye-box");
560        const $codeImg = $(".cc-sign-up-code");
561
562        const $signinBox = $(".sign-in-box");
563        const $loginEmailGroup = $signinBox.find(".cc-login-item-group[data-type='email']");
564        const $loginPwdGroup = $signinBox.find(".cc-login-item-group[data-type='password']");
565        const $loginEmail = $signinBox.find(".cc-login-item-group[data-type='email'] .cc-login-group-input");
566        const $loginPwd = $signinBox.find(".cc-login-item-group[data-type='password'] .cc-login-group-input");
567        const $loginEmailHelp = $signinBox.find(".cc-login-item-group[data-type='email'] .cc-login-group-help");
568        const $loginPwdHelp = $signinBox.find(".cc-login-item-group[data-type='password'] .cc-login-group-help");
569        const $loginFormHelp = $signinBox.find(".cc-login-form-help");
570
571        const $signupBox = $(".sign-up-box");
572        const $signupEmailGroup = $signupBox.find(".cc-login-item-group[data-type='email']");
573        const $signupPwdGroup = $signupBox.find(".cc-login-item-group[data-type='password']");
574        const $signupUlinkGroup = $signupBox.find(".cc-login-item-group[data-type='ulink']");
575        const $signupRobotGroup = $signupBox.find(".cc-login-item-group[data-type='robot']");
576        const $signupEmail = $signupBox.find(".cc-login-item-group[data-type='email'] .cc-login-group-input");
577        const $signupPwd = $signupBox.find(".cc-login-item-group[data-type='password'] .cc-login-group-input");
578        const $signupUlink = $signupBox.find(".cc-login-item-group[data-type='ulink'] .cc-login-group-input");
579        const $signupUlinkState = $signupBox.find(".cc-login-item-group[data-type='ulink'] .cc-login-group-input-status");
580        const $signupCode = $signupBox.find(".cc-login-item-group[data-type='robot'] #signUpCode");
581        const $signupEmailHelp = $signupBox.find(".cc-login-item-group[data-type='email'] .cc-login-group-help");
582        const $signupPwdHelp = $signupBox.find(".cc-login-item-group[data-type='password'] .cc-login-group-help");
583        const $signupUlinkHelp = $signupBox.find(".cc-login-item-group[data-type='ulink'] .cc-login-group-help");
584        const $signupRobotHelp = $signupBox.find(".cc-login-item-group[data-type='robot'] .cc-login-group-help");
585        const $signupFormHelp = $signupBox.find(".cc-login-form-help");
586        const $signupPwdRules = $signupBox.find(".cc-login-item-group[data-type='password'] .cc-login-group-rules");
587
588        const $singupSuggestEmail = $signupBox.find(".cc-login-item-group[data-type='email'] .cc-login-group-suggest");
589        const $singupSuggestEmailYes = $signupBox.find(".cc-login-item-group[data-type='email'] .cc-login-group-suggest .yes");
590        const $singupSuggestEmailNo = $signupBox.find(".cc-login-item-group[data-type='email'] .cc-login-group-suggest .no");
591        const $signupSuggestUlink = $signupBox.find(".cc-login-item-group[data-type='ulink'] .cc-login-group-suggest");
592
593        const $loginBtn = $signinBox.find(".cc-login-actions-btn");
594        const $signupBtn = $signupBox.find(".cc-login-actions-btn");
595        const $fbLoginBtn = $("#login-facebook");
596        const $fbSignupBtn = $("#signup-facebook");
597        const $microsoftLoginBtn = $("#login-microsoft");
598        const $microsoftSignupBtn = $("#signup-microsoft");
599        const $appleLoginBtn = $("#login-apple");
600        const $appleSignupBtn = $("#signup-apple");
601
602        // Google 按钮:服务端 OAuth 跳转
603        const $googleLoginBtn = $("#login-google");
604        const $googleSignupBtn = $("#signup-google");
605
606        const $customUlinkBox = $(".customize-box");
607        let $customUlinkGroup = $customUlinkBox.find(".cc-login-item-group[data-type='ulink']");
608        let $customUlink = $customUlinkBox.find(".cc-login-item-group[data-type='ulink'] .cc-login-group-input");
609        let $customUlinkHelp = $customUlinkBox.find(".cc-login-item-group[data-type='ulink'] .cc-login-group-help");
610        let $customUlinkState = $customUlinkBox.find(".cc-login-item-group[data-type='ulink'] .cc-login-group-input-status");
611        let $customBtn = $customUlinkBox.find(".cc-login-actions-btn");
612        let $customFormHelp = $customUlinkBox.find(".cc-login-form-help");
613
614        const $window = $(window);
615
616        const isRegister = Number("0");
617
618        let toSignInLink;
619        let toSignUpLink;
620        let isLogging = false;
621        let isSigning = false;
622        let signUpSuggestEmail = "";
623        let needToAddConfirm = false;
624        let isConfirmUseEmail = false;
625        let loginThirdPartyData = {};
626
627        const sessionId = Math.random().toString(36).substring(2, 15);
628        const langdingTime = Date.now();
629        const $hiddenInput = $('.hp-wrap input');
630
631        const forgetPasswordPanel = new ForgetPasswordPanel({
632            type: "login",
633            target: $(".forget-box"),
634            email: null,
635            backCallback: function() {
636                toggleForgetBox(false);
637            }
638        });
639
640        const resetPasswordPanel = new ResetPasswordPanel({
641            type: "login",
642            target: $(".reset-password-box"),
643            userId: "",
644            resetTime: "",
645            resetHash: "",
646            backCallback: function() {
647                toggleResetBox(false);
648            }
649        });
650
651        $window.on('resize', function() {
652            setHtmlSize();
653        });
654
655        if (window.location.href.includes("custom-link")) {
656            try {
657                let localData = localStorage.getItem("loginThirdPartyData");
658                if (localData) {
659                    localData = JSON.parse(localData);
660                    goToLinkPage(localData.loginType, localData);
661                    // utils.createThirdParty({
662                    //     linkName: "",
663                    //     sessionId: localData.sessionId,
664                    //     loginType: localData.loginType
665                    // });
666                }
667            } catch (e) {
668                console.log(e);
669            }
670        }
671
672        initSignLink();
673        initEmailVal();
674        bindOAuthEvents();
675        initGroupRules();
676        initRecaptcha();
677
678        let currentPanel = null;
679
680        // 根据 panel 参数自动进入对应面板
681        if (0) {
682            toggleResetBox(true);
683        } else if (0) {
684            toggleForgetBox(true);
685        } else if (0) {
686            if (!window.__verifyPanelInited) {
687                window.__verifyPanelInited = true;
688                toggleVerifyBox(true);
689            }
690        }
691
692        $signInEye.add($signUpEye).on("click", function() {
693            togglePasswordVisibility($(this));
694        });
695
696        $forgetLink.on("click", function(e) {
697            e.preventDefault();
698            toggleForgetBox(true);
699        });
700
701        $backToSign.on("click", function() {
702            if (currentPanel === 'reset') {
703                resetPasswordPanel.toSignIn();
704            } else if (currentPanel === 'forget') {
705                forgetPasswordPanel.toSignIn();
706            } else if (currentPanel === 'verify' || window.__currentPanel === 'verify') {
707                toggleVerifyBox(false);
708            }
709        });
710
711        $logoutLink.on("click", function(e) {
712            e.preventDefault();
713            logout();
714        });
715
716        $loginEmail.on("input", function() {
717            $loginEmailGroup.removeClass("success error");
718        });
719
720        $loginPwd.on("input", function() {
721            $loginPwdGroup.removeClass("success error");
722        });
723
724        $loginEmail.add($loginPwd).on("keypress", function(e) {
725            if (e.which === 13) {
726                login();
727            }
728        });
729
730        $loginBtn.on("click", function() {
731            login();
732        });
733
734        $signupEmail.on("input", function() {
735            $signupEmailGroup.removeClass("success error");
736        });
737
738        $signupPwd.on("input", function() {
739            $signupPwdGroup.removeClass("success error");
740            validateForm({type: "password", group: $signupPwdGroup, input: $signupPwd, help: $signupPwdHelp, rules: $signupPwdRules});
741        });
742
743        $signupPwd.on("blur", function() {
744            if (!$signupPwd.val().trim()) return;
745            var isValidate = validateForm({type: "password", group: $signupPwdGroup, input: $signupPwd, help: $signupPwdHelp, rules: $signupPwdRules, showError: true});
746        });
747
748        $signupUlink.on("input", function() {
749            var isValidate = validateForm({type: "ulink", group: $signupUlinkGroup, input
749: $signupUlink, help: $signupUlinkHelp});
750            $signupUlinkGroup.removeClass("exists no-exists error");
751        });
752
753        $signupUlink.on("blur", function(e) {
754            e.stopPropagation();
755            if (!$signupUlink.val().trim() || $signupUlinkGroup.hasClass("exists")) return;
756            var isValidate = validateForm({type: "ulink", group: $signupUlinkGroup, input: $signupUlink, help: $signupUlinkHelp, showError: true});
757            if (isValidate) {
758                $signupUlinkGroup.toggleClass("loading", true);
759                checkLinkExists($signupUlink.val().trim()).then(function(isUlinkExists) {
760                    $signupUlinkGroup
761                        .toggleClass("loading", false)
762                        .toggleClass("success", !isUlinkExists)
763                        .toggleClass("error", isUlinkExists)
764                        .toggleClass("exists", isUlinkExists)
765                        .toggleClass("no-exists", !isUlinkExists);
766
767                    if (isUlinkExists) {
768                        $signupUlink.focus();
769                        $signupSuggestUlink.find(".suggest-loading").css("display", "flex");
770                        return getLinkSuggest($signupUlink.val().trim());
771                    }
772                }).then(function(suggestLinks) {
773                    if (suggestLinks) {
774                        $signupSuggestUlink.find(".suggest-list").html(suggestLinks.map(function(link) { return '<div class="suggest-item">' + link + '</div>'; }).join(''));
775                        $signupSuggestUlink.find(".suggest-loading").css("display", "none");
776                    }
777                });
778            }
779        });
780
781        $signupSuggestUlink.on("mousedown", function(e) { selectSuggestUlink(e, $signupUlink, $signupUlinkGroup, $signupSuggestUlink); });
782
783        $singupSuggestEmailYes.on("click", function() {
784            $signupEmail.val(signUpSuggestEmail);
785            $signupEmailGroup.toggleClass("suggest", false);
786        });
787
788        $singupSuggestEmailNo.on("click", function() {
789            isConfirmUseEmail = true;
790            $signupEmailGroup.toggleClass("suggest", false);
791        });
792
793        // $signupEmail.add($signupPwd).add($signupUlink).on("keypress", function(e) {
794        $signupEmail.add($signupPwd).on("keypress", function(e) {
795            if (e.which === 13) {
796                signup();
797            }
798        });
799
800        $signupBtn.on("click", function() {
801            signup();
802        });
803
804        function initGroupRules() {
805            if (isRegister) {
806                validateForm({type: "password", group: $signupPwdGroup, input: $signupPwd, help: $signupPwdHelp, rules: $signupPwdRules});
807            }
808        }
809
810        function initEmailVal() {
811            try {
812                var email = utils.getParamValue("email");
813                email = email ? email.trim() : '';
814                if (email) {
815                    var $targetInput = isRegister ? $signupEmail : $loginEmail;
816                    $targetInput.val(email);
817
818                    if (isRegister) {
819                        var newUrl = window.location.href.replace('&email=' + email, '');
820                        history.replaceState(null, '', newUrl);
821                    }
822                }
823
824                var resetPassEmail = utils.getParamValue("reset_pass_email");
825                resetPassEmail = resetPassEmail ? resetPassEmail.trim() : '';
826                if (resetPassEmail) {
827                    $loginEmail.val(resetPassEmail);
828                }
829            } catch (error) {
830                console.error('init email error:', error);
831            }
832        }
833
834        function initSignLink() {
835            var currentUrl = new URL(window.location.href);
836            var baseLink = currentUrl.origin + currentUrl.pathname;
837            var search = currentUrl.search;
838            if (search.startsWith('?register')) {
839                search = search.replace('?register&', '?').replace('?register', '');
840            }
841            var registerLink = '?register' + (search ? '&' + search.slice(1) : '');
842
843            if (isRegister) {
844                toSignInLink = baseLink + search;
845                toSignUpLink = baseLink + registerLink;
846            } else {
847                toSignUpLink = baseLink + registerLink;
848                toSignInLink = baseLink + search;
849            }
850
851            $toSignIn.attr("href", toSignInLink);
852            $toSignUp.attr("href", toSignUpLink);
853        }
854
855        function togglePasswordVisibility($eyeElement) {
856            var $input = $eyeElement.siblings('input[type="password"], input[type="text"]');
857            var isPassword = $input.attr("type") === "password";
858            $eyeElement.toggleClass("open", isPassword);
859            $input.attr("type", isPassword ? "text" : "password");
860        }
861
862        function toggleForgetBox(show) {
863            if (show) {
864                $(".sign-in-box").hide();
865                $(".forget-box").css("display", "flex");
866                $backToSign.css("display", "flex");
867                currentPanel = "forget";
868                forgetPasswordPanel.setEmail($loginEmail.val());
869            } else {
870                $(".sign-in-box").show();
871                $(".forget-box").hide();
872                $backToSign.css("display", "none");
873                currentPanel = null;
874            }
875        }
876
877        function toggleResetBox(show) {
878            if (show) {
879                $(".sign-in-box").hide();
880                $(".sign-up-box").hide();
881                $(".forget-box").hide();
882                $(".reset-password-box").css("display", "flex");
883                $backToSign.css("display", "flex");
884                currentPanel = "reset";
885            } else {
886                $(".reset-password-box").hide();
887                if (isRegister) {
888                    $(".sign-up-box").show();
889                } else {
890                    $(".sign-in-box").show();
891                }
892                $backToSign.css("display", "none");
893                currentPanel = null;
894            }
895        }
896
897        function toggleVerifyBox(show) {
898            if (show) {
899                $(".sign-in-box").hide();
900                $(".sign-up-box").hide();
901                $backToSign.css("display", "flex");
902                currentPanel = "verify";
903
904                // 从 URL 读取 email 和 redirect 参数
905                var verifyEmail = utils.getParamValue("email") || '';
906                var redirectUrl = utils.getParamValue("redirect") || '';
907
908                // 尝试从 session 获取用户信息(userId + email)
909                $.ajax({
910                    url: "/api/user/get-user",
911                    type: "GET",
912                    dataType: "json",
913                    success: function(data) {
914                        if (data && data.data) {
915                            // 已激活 或 第三方账号 → 跳过验证,直接跳转
916                            if (data.data.isActivate == 1 || (data.data.type && data.data.type != 0)) {
917                                window.location.href = redirectUrl || '/dashboard/home';
918                                return;
919                            }
920                            // 已登录未激活 → 显示验证面板
921                            fh5.uId = data.data.userId || '';
922                            fh5.isLoggedIn = true;
923                            if (!verifyEmail) {
924                                verifyEmail = data.data.uEmail || '';
925                            }
926                            renderVerifyPanel(verifyEmail, redirectUrl);
927                        } else {
928                            // 未登录 → 回退到登录表单
929                            toggleVerifyBox(false);
930                        }
931                    },
932                    error: function(xhr, status, err) {
933                        // API 异常(如 iPhone Safari cookie 受限)→ 仍渲染面板兜底
934                        renderVerifyPanel(verifyEmail, redirectUrl);
935                    }
936                });
937
938                function renderVerifyPanel(email, redirect) {
939                    // 设空字符串让 ActivateAccountPop 显示 Send 按钮,不自动倒计时
940                    localStorage.setItem('startTime', '');
941                    $(".verify-box").css("display", "flex");
942                    var activateOpt = {
943                        email: email,
944                        container: $(".verify-box"),
945                        onClose: function() {
946                            toggleVerifyBox(false);
947                        }
948                    };
949                    // 激活成功后跳回来源页面(如价格页)
950                    if (redirect) {
951                        activateOpt.callback = function() {
952                            window.location.href = redirect;
953                        };
954                    }
955                    new ActivateAccountPop(activateOpt);
956                }
957            } else {
958                $(".verify-box").hide();
959                if (isRegister) {
960                    $(".sign-up-box").css("display", "flex");
961                } else {
962                    $(".sign-in-box").css("display", "flex");
963                }
964                $backToSign.css("display", "none");
965                currentPanel = null;
966            }
967        }
968
969        function logout() {
970            $.ajax({
971                url: "/api/user/sign-out?auto=1",
972                type: "POST",
973                success: function(res) {
974                    setTimeout(function() {
975                        window.location.href = utils.parseMultiLink("/login/");
976                    }, 50);
977                },
978                error: function(res) {
979                    console.log("sign out error", res);
980                    window.location.href = "/api/user/sign-out?from=/login/";
981                }
982            });
983        }
984
985        function validateForm(params) {
986            var type = params.type;
987            var group = params.group;
988            var input = params.input;
989            var help = params.help;
990            var rules = params.rules;
991            var showError = params.showError || false;
992
993            var validateKeys = ["email", "password", "password_login", "ulink", "verificationCode"];
994            if (!validateKeys.includes(type)) return false;
995            var isValidate = utils.validate2({
996                type: type,
997                $input: input,
998                $help: help,
999                focus: false,
1000                errorMesage: "",
1001                matchValue: "",
1002                $rules: rules,
1003                showError: showError,
1004            });
1005            showError && group.toggleClass("success", isValidate).toggleClass("error", !isValidate);
1006            return isValidate;
1007        }
1008
1009        function randomDigits(n) {
1010            n = n || 4;
1011            var str = '';
1012            for (var i = 0; i < n; i++) {
1013                str += Math.floor(Math.random() * 10);
1014            }
1015            return str;
1016        }
1017
1018        function randomLetters(n) {
1019            n = n || 4;
1020            var str = '';
1021            var chars = 'abcdefghijklmnopqrstuvwxyz';
1022            for (var i = 0; i < n; i++) {
1023                str += chars[Math.floor(Math.random() * chars.length)];
1024            }
1025            return str;
1026        }
1027
1028        function checkLinkExists(link) {
1029            return new Promise(function(resolve) {
1030                $.ajax({
1031                    type: "POST",
1032                    url: "/api/object/check-link",
1033                    dataType: "json",
1034                    data: {
1035                        link: link,
1036                        model_type: "user",
1037                        is_system: 0,
1038                    },
1039                    success: function(response) {
1040                        try {
1041                            if (response && response.code && response.code.toLowerCase() === "ok") {
1042                                if (response.data) {
1043                                    resolve(response.data);
1044                                } else {
1045                                    resolve(false);
1046                                }
1047                            } else {
1048                                resolve(true);
1049                            }
1050                        } catch (e) {
1051                            resolve(true);
1052                        }
1053                    },
1054                    error: function() {
1055                        resolve(true);
1056                    }
1057                });
1058            });
1059        }
1060
1061        function getLinkSuggest(base) {
1062            return new Promise(function(resolve) {
1063                var candidates = [];
1064                for (var i = 0; i < 10; i++) {
1065                    candidates.push(base + randomDigits(4));
1066                }
1067                checkLinkExists(candidates.join(',')).then(function(existArr) {
1068                    var available = [];
1069                    if (Object.prototype.toString.call(existArr) === '[object Array]') {
1070                        for (var i = 0; i < candidates.length; i++) {
1071                            if (existArr.indexOf(candidates[i]) === -1) {
1072                                available.push(candidates[i]);
1073                            }
1074                        }
1075                    }
1076                    if (available.length >= 5) {
1077                        resolve(available.slice(0, 5));
1078                        return;
1079                    }
1080                    candidates = [];
1081                    for (var j = 0; j < 10; j++) {
1082                        candidates.push(base + randomDigits(4) + randomLetters(4));
1083                    }
1084                    checkLinkExists(candidates.join(',')).then(function(existArr2) {
1085                        var available2 = [];
1086                        if (Object.prototype.toString.call(existArr2) === '[object Array]') {
1087                            for (var k = 0; k < candidates.length; k++) {
1088                                if (existArr2.indexOf(candidates[k]) === -1) {
1089                                    available2.push(candidates[k]);
1090                                }
1091                            }
1092                        }
1093                        if (available2.length >= 5) {
1094                            resolve(available2.slice(0, 5));
1095                        } else {
1096                            resolve(candidates.slice(0, 5));
1097                        }
1098                    }).catch(function() {
1099                        resolve([]);
1100                    });
1101                }).catch(function() {
1102                    resolve([]);
1103                });
1104            });
1105        }
1106
1107        function selectSuggestUlink(e, $input, $group, $suggest) {
1108            e.preventDefault();
1109            e.stopPropagation();
1110
1111            var $target = $(e.target);
1112            var selectedText = "";
1113
1114            if ($target.hasClass("suggest-item")) {
1115                selectedText = $target.text().trim();
1116            } else {
1117                var $suggestItem = $target.closest(".suggest-item");
1118                if ($suggestItem.length > 0) {
1119                    selectedText = $suggestItem.text().trim();
1120                } else {
1121                    var $childSuggestItem = $target.find(".suggest-item").first();
1122                    if ($childSuggestItem.length > 0) {
1123                        selectedText = $childSuggestItem.text().trim();
1124                    }
1125                }
1126            }
1127
1128            if (selectedText) {
1129                $input.val(selectedText);
1130                $group.toggleClass("success", true)
1131                    .toggleClass("error", false)
1132                    .toggleClass("exists", false)
1133                    .toggleClass("no-exists", true);
1134                $input.trigger("input");
1135            }
1136        }
1137
1138        function login() {
1139            if (isLogging) return;
1140
1141            var isEmailValidate = validateForm({type: "email", group: $loginEmailGroup, input
1141: $loginEmail, help: $loginEmailHelp, showError: true});
1142            if (!isEmailValidate) return;
1143
1144            var isPwdValidate = validateForm({type: "password_login", group: $loginPwdGroup, input: $loginPwd, help: $loginPwdHelp, showError: true});
1145            if (!isPwdValidate) return;
1146
1147            var params = {
1148                account: $loginEmail.val().trim(),
1149                upass: $loginPwd.val().trim(),
1150            };
1151
1152            $.ajax({
1153                type: "POST",
1154                url: "/api/user/handle-user-login",
1155                dataType: "json",
1156                data: params,
1157                beforeSend: function() {
1158                    isLogging = true;
1159                    $loginBtn.html("Logging in...");
1160                    $loginEmailGroup.removeClass("success error");
1161                    $loginPwdGroup.removeClass("success error");
1162                    $loginFormHelp.hide();
1163                },
1164                success: function(data) {
1165                    if (typeof dataLayerFun != 'undefined' && dataLayerFun.uploadBook) {
1166                        dataLayerFun.login(function() {
1167                            handleLoginSuccess(data);
1168                        });
1169                    } else {
1170                        handleLoginSuccess(data);
1171                    }
1172                },
1173                error: function(jqXHR, textStatus, errorThrown) {
1174                    $loginFormHelp.html(fh5.pageData.tips.UnexpectedError2).show();
1175                    isLogging = false;
1176                    $loginBtn.html("Log in");
1177                }
1178            });
1179        }
1180
1181        function handleLoginSuccess(data) {
1182            var hasError = false;
1183            switch (data.code) {
1184                case "OK":
1185                    utils.setCookie('fh5_haved_login', {
1186                        value: '1',
1187                        maxAge: 10 * 365 * 24 * 60 * 60,
1188                        path: '/',
1189                    });
1190                    utils.updateFirstVisitChannel();
1191                    var referrerUrl = utils.getParamValue("referrerUrl");
1192                    var backUrl = utils.getParamValue("backurl");
1193                    if (referrerUrl === 'tools') {
1194                        utils.postLoginMessage('login', referrerUrl);
1195                        return;
1196                    }
1197                    if (backUrl !== '') {
1198                        if (backUrl.indexOf("/dashboard/index.php") !== -1) {
1199                            backUrl = backUrl.replace("/dashboard/index.php", "/dashboard/");
1200                        }
1201                        window.location.href = backUrl;
1202                    } else {
1203                        var back = utils.getParamValue("back");
1204                        if (back === "1") {
1205                            window.location.href = document.referrer;
1206                            return;
1207                        }
1208                        var autoTriggerNewDashboard = data.data && data.data.hasOwnProperty('autoTriggerNewDashboard') ? data.data.autoTriggerNewDashboard : 0;
1209                        window.location.href = utils.getLoginLink(autoTriggerNewDashboard);
1210                    }
1211                    break;
1212                case "PASSWORD_ERROR":
1213                    var forgotEmailUrl = "/login/?panel=forgot-password" + ($loginEmail.val() ? '&forgot_email=' + $loginEmail.val() : '');
1214
1215                    if (data.data && data.data.account_type === 'facebook') {
1216                        $loginEmailGroup.toggleClass("success", false).toggleClass("error", true);
1217                        $loginEmailHelp.html("This is a Facebook account. Please click the Facebook button above to continue or " + '<a class="cc-underline-default" href="' + forgotEmailUrl + '">' + "set a password" + '</a>' + " first.");
1218                    } else if (data.data && data.data.account_type === 'google') {
1219                        $loginEmailGroup.toggleClass("success", false).toggleClass("error", true);
1220                        $loginEmailHelp.html("This is a Google account. Please click the Google button above to continue or " + '<a class="cc-underline-default" href="' + forgotEmailUrl + '">' + "set a password" + '</a>' + " first.");
1221                    } else if (data.data && data.data.account_type === 'apple') {
1222                        $loginEmailGroup.toggleClass("error", true);
1223                        $loginEmailHelp.html("This is a Apple account. Please click the Apple button above to continue or " + '<a class="cc-underline-default" href="' + forgotEmailUrl + '">' + "set a password" + '</a>' + " first.");
1224                    } else if (data.data && data.data.account_type === 'microsoft') {
1225                        $loginEmailGroup.toggleClass("error", true);
1226                        $loginEmailHelp.html("This is a Microsoft account. Please click the Microsoft button above to continue or " + '<a class="cc-underline-default" href="' + forgotEmailUrl + '">' + "set a password" + '</a>' + " first.");
1227                    } else {
1228                        $loginPwdGroup.toggleClass("success", false).toggleClass("error", true);
1229                        $loginPwdHelp.html("Invalid email or password.");
1230                    }
1231
1232                    hasError = true;
1233                    break;
1234                case "ACCOUNT_NOT_EXISTS":
1235                    $loginEmailGroup.toggleClass("success", false).toggleClass("error", true);
1236                    $loginEmailHelp.html("The user doesn't exist. Please " + "<a class='cc-error-link cc-underline-default' href='" + toSignUpLink + "&email=" + $loginEmail.val().trim() + "'>" + "create an account" + "</a>" + " first.");
1237                    hasError = true;
1238                    break;
1239                case "LOGIN_LIMIT":
1240                    var unlockTime = data.data.time;
1241                    var currentTime = new Date().getTime();
1242                    try {
1243                        if (currentTime < unlockTime * 1000) {
1244                            var unlockTimeStr = getUnlockTime(unlockTime * 1000);
1245                            var limitMsg = "You have consecutively entered the wrong password over 5 times! Please try again at" + ' ' + unlockTimeStr + '.';
1246                            $loginPwdGroup.toggleClass("success", false).toggleClass("error", true);
1247                            $loginPwdHelp.html(limitMsg);
1248                        } else {
1249                            $loginPwdHelp.html(fh5.pageData.tips.UnexpectedError2);
1250                        }
1251                    } catch (error) {
1252                        console.log(error);
1253                    }
1254                    hasError = true;
1255                    break;
1256                case "4":
1257                case "5":
1258                    break;
1259                default:
1260                    $loginFormHelp.html(fh5.pageData.tips.UnexpectedError2).show();
1261                    hasError = true;
1262            }
1263
1264            if (hasError) {
1265                $loginBtn.html("Log in");
1266            }
1267            isLogging = false;
1268        }
1269
1270        // 提取 signup success 处理函数(供 AJAX 和测试 mock 共用)
1271        function handleSignupSuccess(data, redirectBox, isLoadGoggleCode) {
1272            var hasError = false;
1273            if (data.data) {
1274                signUpSuggestEmail = data.data.did_you_mean;
1275            }
1276            switch (data.code) {
1277                case "OK":
1278                        if (typeof ga == "function") {
1279                            ga('send', 'event', 'register', 'register.signup', document.location.pathname);
1280                        }
1281
1282                        // 更新全局登录状态(ActivateAccountPop 依赖 fh5.uId)
1283                        var global = {};
1284                        try {
1285                            global = (parent && parent.fh5 && parent.fh5.hasOwnProperty("isLoggedIn")) ? parent : window;
1286                        } catch (e) {
1287                            global = window;
1288                        }
1289
1290                        fh5.isLoggedIn = true;
1291                        fh5.uId = data.userId;
1292                        fh5.uLink = data.uLink;
1293                        fh5.uType = "0";
1294                        fh5.sessionDefaultCId = data.cId;
1295
1296                        global.fh5.isLoggedIn = fh5.isLoggedIn;
1297                        global.fh5.uId = fh5.uId;
1298                        global.fh5.uLink = fh5.uLink;
1299                        global.fh5.uType = fh5.uType;
1300                        global.fh5.sessionDefaultCId = fh5.sessionDefaultCId;
1301
1302                        var uMail = $signupEmail.val().trim();
1303
1304                        utils.setCookie('fh5_haved_login', {
1305                            value: '1',
1306                            maxAge: 10 * 365 * 24 * 60 * 60,
1307                            path: '/',
1308                        });
1309
1310                        var h5_activate_account = localStorage.getItem("startTime");
1311                        if (h5_activate_account != "undefined" && h5_activate_account != null) {
1312                            localStorage.removeItem("startTime");
1313                        }
1314
1315                        window.location.href = '/dashboard/home';
1316                        // var referrerUrl = utils.getParamValue("referrerUrl");
1317
1318                        // utils.changeTopBar().then(function (res) {
1319                        //     redirectBox.cancel();
1320                        //     var activateOpt = {
1321                        //         email: uMail,
1322                        //         container: $(".verify-box"),
1323                        //         onClose: function () {
1324                        //             // 用户关闭验证面板时恢复注册表单
1325                        //             $(".verify-box").hide();
1326                        //             $(".sign-up-box").css("display", "flex");
1327                        //         },
1328                        //     };
1329                        //     // Note: reg_from is read from URL params by ActivateAccountPop;
1330                        //     // inline signup does not have register_dataLayer to pass as option.
1331                        //     if (referrerUrl !== '') {
1332                        //         activateOpt.callback = function () {
1333                        //             window.location.href = utils.parseMultiLink(referrerUrl);
1334                        //         };
1335                        //     }
1336                        //     // 隐藏注册面板,显示验证面板
1337                        //     $(".sign-up-box").hide();
1338                        //     $(".verify-box").css("display", "flex");
1339                        //     new ActivateAccountPop(activateOpt);
1340                        // }).catch(function (err) {
1341                        //     redirectBox.cancel();
1342                        //     $signupBtn.html("Sign up");
1343                        //     isSigning = false;
1344                        //     console.error('changeTopBar failed:', err);
1345                        // });
1346                        break;
1347                    case "ACCOUNT_ALREADY_EXISTS":
1348                        $signupEmailGroup.toggleClass("success", false).toggleClass("error", true);
1349                        $signupEmail.focus();
1350                        $signupEmailHelp.html("User already exists.");
1351                        hasError = true;
1352                        break;
1353                    case "SYSTEM_BLACK_LIST":
1354                        $signupEmailGroup.toggleClass("success", false).toggleClass("error", true);
1355                        $signupEmail.focus();
1356                        $signupEmailHelp.html("The email can not be registered because your IP has been blacklisted.");
1357                        hasError = true;
1358                        break;
1359                    case "VERIFY_CODE_ERROR":
1360                        $signupRobotGroup.toggleClass("success", false).toggleClass("error", true);
1361                        $signupRobotHelp.html(fh5.pageData.tips.VerificationCodeError);
1362                        hasError = true;
1363                        break;
1364                    case "4":
1365                        $signupEmail.focus();
1366                        if (signUpSuggestEmail) {
1367                            $signupEmailGroup.toggleClass("suggest", true);
1368                            $singupSuggestEmail.find(".suggest-email").html(signUpSuggestEmail);
1369                        } else {
1370                            $signupEmailGroup.toggleClass("success", false).toggleClass("error", true);
1371                            $signupEmailHelp.html("This email address is suspected, it can not be registered.");
1372                        }
1373                        hasError = true;
1374                        break;
1375                    case "5":
1376                        redirectBox.cancel();
1377                        $signupEmail.focus();
1378                        $signupEmailGroup.toggleClass("suggest", true);
1379                        $singupSuggestEmail.find(".suggest-email").html(signUpSuggestEmail);
1380                        needToAddConfirm = true;
1381                        hasError = true;
1382                        break;
1383                    case "HP_VALUE_ERROR":
1384                        var message = "Registration failed." + " <a class='cc-hover-underline' style='color: #EF4E2F;' href='/contact-us.php' target='_blank'>Please contact our customer support.</a>";
1385                        $signupFormHelp.html(message).addClass('text-center').show();
1386                        hasError = true;
1387                        sendInterceptLog({type: 'hp_value_error'});
1388                        break;
1389                    default:
1390                        $signupFormHelp.html(fh5.pageData.tips.UnexpectedError2).show();
1391                        hasError = true;
1392                }
1393
1394            if (hasError) {
1395                redirectBox.cancel();
1396                refreshCode(isLoadGoggleCode);
1397                $signupBtn.html("Sign up");
1398            }
1399            isSigning = false;
1400        } // end handleSignupSuccess
1401
1402        function signup() {
1403            if (isSigning) return;
1404
1405            var isEmailValidate = validateForm({type: "email", group: $signupEmailGroup, input: $signupEmail, help: $signupEmailHelp, showError: true});
1406            if (!isEmailValidate) return;
1407
1408            var isPwdValidate = validateForm({type: "password", group: $signupPwdGroup, input: $signupPwd, help: $signupPwdHelp, rules: $signupPwdRules, showError: true});
1409            if (!isPwdValidate) {
1410                $signupPwd.focus();
1411                return;
1412            }
1413            // if ($signupUlink.val().trim()) {
1414            //     var isUlinkValidate = validateForm({type: "ulink", group: $signupUlinkGroup, input: $signupUlink, help: $signupUlinkHelp, showError: true});
1415            //     if (!isUlinkValidate) return;
1416
1417            //     if ($signupUlinkGroup.hasClass("exists")) return;
1418
1419            //     checkLinkExists($signupUlink.val().trim()).then(function(isUlinkExists) {
1420            //         $signupUlinkGroup.toggleClass("success", !isUlinkExists)
1421            //             .toggleClass("error", isUlinkExists)
1422            //             .toggleClass("exists", isUlinkExists)
1423            //             .toggleClass("no-exists", !isUlinkExists);
1424
1425            //         if (isUlinkExists) {
1426            //             $signupUlink.focus();
1427            //             $signupSuggestUlink.find(".suggest-loading").css("display", "flex");
1428            //             return getLinkSuggest($signupUlink.val().trim());
1429            //         } else {
1430            //             processSignup();
1431            //         }
1432            //     }).then(function(suggestLinks) {
1433            //         if (suggestLinks) {
1434            //             $signupSuggestUlink.find(".suggest-list").html(suggestLinks.map(function(link) { return '<div class="suggest-item">' + link + '</div>'; }).join(''));
1435            //             $signupSuggestUlink.find(".suggest-loading").css("display", "none");
1436            //         }
1437            //     });
1438            // } else {
1439            //     processSignup();
1440            // }
1441
1442            processSignup();
1443        }
1444
1445        function processSignup() {
1446            var isLoadGoggleCode = $(".signup-code").hasClass("cc-hide");
1447            if (isLoadGoggleCode && !window._RecaptchaResponse) {
1448                $signupRobotGroup.toggleClass("success", false).toggleClass("error", true);
1449                $signupRobotHelp.html("Please check the above box to let us know you're human.");
1450                return;
1451            }
1452
1453            if (!isLoadGoggleCode) {
1454                var isCodeValidate = validateForm({type: "verificationCode", group: $signupRobotGroup, input: $signupCode, help: $signupRobotHelp, showError: true});
1455                if (!isCodeValidate) return;
1456            }
1457
1458            var postTime = Date.now();
1459            if (postTime - langdingTime < 1000 * 2) {
1460                $signupFormHelp.html("You're submitting too quickly. Please wait a moment and try again.").show();
1461                sendInterceptLog({type: 'too_quickly'});
1462                return;
1463            }
1464
1465            var redirectBox = new RedirectingBox();
1466            redirectBox.show($("body"));
1467
1468            var params = {
1469                account: $signupEmail.val().trim(),
1470                upass: $signupPwd.val().trim(),
1471                // linkName: $signupUlink ? $signupUlink.val().trim() : "",
1472                codeType: isLoadGoggleCode ? 2 : 1,
1473                code: isLoadGoggleCode ? window._RecaptchaResponse : $signupCode.val().trim(),
1474            };
1475
1476            if (isConfirmUseEmail && needToAddConfirm) {
1477                params.isConfirm = 1;
1478            }
1479
1480            if ($hiddenInput && $hiddenInput.val().trim()) {
1481                params.hpValue = $hiddenInput.val().trim();
1482            }
1483
1484            // handleSignupSuccess 已提取至外层作用域
1485
1486            $.ajax({
1487                url: '/api/user/handle-user-signup',
1488                type: "POST",
1489                dataType: "json",
1490                data: params,
1491                beforeSend: function() {
1492                    $signupEmailGroup.removeClass("success error suggest");
1493                    $signupPwdGroup.removeClass("success error");
1494                    $signupUlinkGroup.removeClass("success error");
1495                    $signupRobotGroup.removeClass("success error");
1496                    isSigning = true;
1497                    $signupBtn.html("Signing up...");
1498                    $signupFormHelp.removeClass('text-center').hide();
1499                },
1500                success: function(data) {
1501                    handleSignupSuccess(data, redirectBox, isLoadGoggleCode);
1502                },
1503                error: function() {
1504                    redirectBox.cancel();
1505                    refreshCode(isLoadGoggleCode);
1506                    isSigning = false;
1507                    $signupBtn.html("Sign up");
1508                    $signupFormHelp.html(fh5.pageData.tips.UnexpectedError2).show();
1509                }
1510            });
1511        }
1512
1513        function getUnlockTime(timestamp) {
1514            var date = new Date(timestamp);
1515            return date.toLocaleTimeString(undefined, {
1516                hour: '2-digit',
1517                minute: '2-digit',
1518                hour12: false
1519            });
1520        }
1521
1522        function refreshCode(isLoadGoggleCode) {
1523            if (isLoadGoggleCode) {
1524                if (window.grecaptcha) {
1525                    window.grecaptcha.reset();
1526                }
1527                return;
1528            }
1529            var timestamp = new Date().getTime();
1530            var oldSrc = $codeImg.attr("src");
1531            var newSrc = oldSrc.replace(/\?\d+/, '?' + timestamp) || oldSrc + '?' + timestamp;
1532            $codeImg.attr("src", newSrc);
1533        }
1534
1535        // ---- OAuth 弹窗登录完成处理:postMessage(主)+ popup.closed 兜底 ----
1536        var oauthDone = false; // postMessage 到达后置 true,防止双重触发
1537
1538        window.addEventListener('message', function(e) {
1539            var msg = utils.validateLoginMessage(e);
1540            if (!msg) return;
1541            oauthDone = true;
1542            if (msg.target) {
1543                window.location.href = msg.target;
1544            } else {
1545                window.location.reload();
1546            }
1547        });
1548
1549        function openAndWatch(url) {
1550            var popup = utils.openOAuthPopup(url);
1551            if (!popup) return;
1552            var startTime = Date.now();
1553            var checkClosed = setInterval(function() {
1554                var closed = false;
1555                try {
1556                    closed = popup.closed;
1557                } catch(e) {
1558                    // COOP 阻止读取 popup.closed,停止轮询(postMessage 路径仍可用)
1559                    clearInterval(checkClosed);
1560                    return;
1561                }
1562                if (closed) {
1563                    clearInterval(checkClosed);
1564                    if (!oauthDone) {
1565                        window.location.reload();
1566                    }
1567                } else if (Date.now() - startTime > 180000) {
1568                    // 3 分钟超时,停止轮询
1569                    clearInterval(checkClosed);
1570                }
1571            }, 500);
1572        }
1573
1574        function bindOAuthEvents() {
1575            var redirectTarget = utils.getOAuthRedirect();
1576            if (redirectTarget === '') {
1577                redirectTarget = utils.getLoginLink(0);
1578            }
1579            var oauthCompleteUrl = '/login-external/oauth-complete.html?final=' + encodeURIComponent(redirectTarget);
1580            var redirect = encodeURIComponent(oauthCompleteUrl);
1581
1582            $googleLoginBtn.add($googleSignupBtn).on("click", function() {
1583                openAndWatch('/auth/google/login.php?redirect=' + redirect);
1584            });
1585
1586            $fbLoginBtn.add($fbSignupBtn).on("click", function() {
1587                openAndWatch('/auth/facebook/login.php?redirect=' + redirect);
1588            });
1589
1590            $microsoftLoginBtn.add($microsoftSignupBtn).on("click", function() {
1591                openAndWatch('/auth/microsoft/login.php?redirect=' + redirect);
1592            });
1593
1594            $appleLoginBtn.add($appleSignupBtn).on("click", function() {
1595                openAndWatch('/auth/apple/login.php?redirect=' + redirect);
1596            });
1597        }
1598
1599        // ---- reCAPTCHA 初始化 ----
1600        function initRecaptcha() {
1601            var $gRecaptcha = $("#g-recaptcha");
1602            if (!$gRecaptcha.length) return;
1603
1604            var ajaxTimeout = $.ajax({
1605                method: "GET",
1606                url: "https://www.google.com/recaptcha/api.js?onload=onGoogleRecaptchaLoad&render=explicit",
1607                timeout: 5000,
1608                dataType: "script",
1609                success: function(data) {
1610                    $(".code-loading").hide();
1611                },
1612                complete: function(XMLHttpRequest, status) {
1613                    if (status == 'timeout') {
1614                        ajaxTimeout.abort();
1615                        $(".code-loading").hide();
1616                        $(".code-wrap").toggleClass("code", true);
1617                        $(".signup-code").toggleClass("cc-hide", false);
1618                        $(".g-recaptcha").toggleClass("cc-hide", true);
1619                    }
1620                },
1621            });
1622        }
1623
1624        window.onGoogleRecaptchaLoad = function() {
1625            if (typeof grecaptcha === 'undefined') return;
1626            grecaptcha.render('g-recaptcha', {
1627                'sitekey': '6Ld_aEAtAAAAAJGV1sYiA3Zp1VytpE2sb1umtom_',
1628                'callback': function(token) {
1629                    $signupRobotGroup.toggleClass("success", true).toggleClass("error", false);
1630                    window._RecaptchaResponse = token;
1631                },
1632                'expired-callback': function() {
1633                    window._RecaptchaResponse = '';
1634                },
1635                'error-callback': function() {
1636                    window._RecaptchaResponse = '';
1637                }
1638            });
1639        };
1640
1641        function goToLinkPage(loginType, data) {
1642            $(".sign-up-box").hide();
1643            $(".sign-in-box").hide();
1644            $customUlinkBox.css("display", "flex");
1645            initCustomLinkForm(loginType, data);
1646            // utils.createThirdParty({
1647            //     linkName: "",
1648            //     sessionId: data.sessionId,
1649            //     loginType: loginType
1650            // });
1651        }
1652
1653        function initCustomLinkForm(loginType, userInfo) {
1654            $customUlinkGroup = $customUlinkBox.find(".cc-login-item-group[data-type='ulink']");
1655            $customUlink = $customUlinkGroup.find(".cc-login-group-input");
1656            $customUlinkHelp = $customUlinkGroup.find(".cc-login-group-help");
1657            $customUlinkState = $customUlinkGroup.find(".cc-login-group-input-status");
1658            $customUlinkSuggest = $customUlinkGroup.find(".cc-login-group-suggest");
1659            $customBtn = $customUlinkBox.find(".cc-login-actions-btn");
1660            $customFormHelp = $customUlinkBox.find(".cc-login-form-help");
1661
1662            loginThirdPartyData = Object.assign({}, userInfo, { loginType: loginType });
1663
1664            var defaultUserInfo = {
1665                picture: "",
1666                uName: "Protected username",
1667                uEmail: "Protected phone number\/email",
1668            };
1669            var finalUserInfo = Object.assign({}, defaultUserInfo, userInfo);
1670
1671            updateUserInfoUI(finalUserInfo);
1672            bindCustomLinkEvent($customUlinkGroup, $customUlink, $customUlinkHelp, $customUlinkState, $customUlinkSuggest);
1673        }
1674
1675        function updateUserInfoUI(userInfo) {
1676            var pictureStr = userInfo.picture ?
1677                "<img src='" + userInfo.picture + "' alt='avatar'/>" :
1678                "<span>" + userInfo.uName.slice(0, 1).toUpperCase() + "</span>";
1679
1680            $(".login-account-avatar").html(pictureStr);
1681            $(".login-account-name").text(userInfo.uName);
1682            $(".login-account-email").text(userInfo.uEmail);
1683
1684            if (!userInfo.picture) {
1685                $(".login-account-avatar").css('backgroundColor', getRandomColor());
1686            }
1687        }
1688
1689        function bindCustomLinkEvent($group, $input, $help, $state, $suggest) {
1690            $input.on("input", function() {
1691                var isValid = validateForm({type: "ulink", group: $group, input
1691: $input, help: $help});
1692                $group.removeClass("exists no-exists");
1693                var btnText = !$input.val().trim() ? "Skip and Sign Up" : "Sign Up";
1694                $customBtn.text(btnText);
1695            });
1696            $input.on("blur", function(e) {
1697                e.stopPropagation();
1698                if (!$input.val().trim() || $group.hasClass("exists")) return;
1699                var isValid = validateForm({type: "ulink", group: $group, input: $input, help: $help});
1700                if (isValid) {
1701                    $group.toggleClass("loading", true);
1702                    checkLinkExists($input.val().trim()).then(function(isUlinkExists) {
1703                        $group
1704                            .toggleClass("loading", false)
1705                            .toggleClass("success", !isUlinkExists)
1706                            .toggleClass("error", isUlinkExists)
1707                            .toggleClass("exists", isUlinkExists)
1708                            .toggleClass("no-exists", !isUlinkExists);
1709                        $state.toggle(!isUlinkExists);
1710
1711                        if (isUlinkExists) {
1712                            $input.focus();
1713                            $suggest.find(".suggest-loading").css("display", "flex");
1714                            return getLinkSuggest($input.val().trim());
1715                        }
1716                    }).then(function(suggestLinks) {
1717                        if (suggestLinks) {
1718                            $suggest.find(".suggest-list").html(suggestLinks.map(function(link) { return '<div class="suggest-item">' + link + '</div>'; }).join(''));
1719                            $suggest.find(".suggest-loading").css("display", "none");
1720                        }
1721                    });
1722                }
1723            });
1724            $input.on("keypress", function(e) {
1725                if (e.which === 13) {
1726                    createThirdParty();
1727                }
1728            });
1729            $customBtn.on("click", createThirdParty);
1730            $suggest.on("mousedown", function(e) { selectSuggestUlink(e, $input, $group, $suggest); });
1731        }
1732
1733        function createThirdParty() {
1734            if ($customUlink.val().trim()) {
1735                var isUlinkValidate = validateForm({type: "ulink", group: $customUlinkGroup, input: $customUlink, help: $customUlinkHelp, showError: true});
1736                if (!isUlinkValidate) return;
1737                if ($customUlinkGroup.hasClass("exists")) return;
1738                checkLinkExists($customUlink.val().trim()).then(function(isUlinkExists) {
1739                    $customUlinkGroup.toggleClass("success", !isUlinkExists)
1740                        .toggleClass("error", isUlinkExists)
1741                        .toggleClass("exists", isUlinkExists)
1742                        .toggleClass("no-exists", !isUlinkExists);
1743
1744                    if (isUlinkExists) {
1745                        $customUlink.focus();
1746                        $customUlinkSuggest.find(".suggest-loading").css("display", "flex");
1747                        return getLinkSuggest($customUlink.val().trim());
1748                    } else {
1749                        doCreateThirdParty();
1750                    }
1751                }).then(function(suggestLinks) {
1752                    if (suggestLinks) {
1753                        $customUlinkSuggest.find(".suggest-list").html(suggestLinks.map(function(link) { return "<div class='suggest-item'>" + link + "</div>"; }).join(''));
1754                        $customUlinkSuggest.find(".suggest-loading").css("display", "none");
1755                    }
1756                });
1757            } else {
1758                doCreateThirdParty();
1759            }
1760        }
1761
1762        function doCreateThirdParty() {
1763            utils.createThirdParty({
1764                helpId: "customFormHelp",
1765                linkName: $customUlink.val().trim(),
1766                sessionId: loginThirdPartyData.sessionId,
1767                loginType: loginThirdPartyData.loginType
1768            });
1769        }
1770
1771        function getRandomColor() {
1772            var letters = '0123456789ABCDEF';
1773            return '#' + Array.from({ length: 6 }, function() {
1774                return letters[Math.floor(Math.random() * 16)];
1775            }).join('');
1776        }
1777
1778        function sendInterceptLog(params) {
1779            var type = params.type || '';
1780            var resonMap = {
1781                too_quickly: 'TOO_FAST',
1782                hp_value_error: 'HONEYPOT',
1783            };
1784            var primary_rule = Object.values(resonMap).join(';');
1785
1786            var trackData = {
1787                event_type: 'register_blocked',
1788                event_time: new Date().toISOString(),
1789                reason: resonMap[type] || '',
1790                primary_rule: primary_rule,
1791                policy_version: '1.0.0',
1792                email: $signupEmail.val().trim(),
1793                client_ip: window.client_id,
1794                ip_country: window.ip_country,
1795                user_agent: navigator.userAgent,
1796                session_id: sessionId,
1797            };
1798
1799            try {
1800                window.h5SlsTracker.send('registerBlocked', trackData);
1801                console.log('send register_blocked event', trackData);
1802            } catch(e) {
1803                console.error('Failed to send register_blocked event:', e);
1804            }
1805        }
1806    });
1807})(window.jQuery);
1808</script>
1808
1809</body>
1810</html>

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.