
'------------------------------------------------------------
Function HL_NewToken()
    Dim hl_i, hl_n, hl_s
    Randomize Timer
    hl_s = ""
    For hl_i = 1 To 32
        hl_n = Int(Rnd() * 16)
        hl_s = hl_s & Mid("0123456789abcdef", hl_n + 1, 1)
    Next
    HL_NewToken = hl_s
End Function

Function HL_EnsureMsgToken()
    Dim hl_t
    On Error Resume Next
    hl_t = Trim(CStr(Session("HL_MsgToken") & ""))
    If hl_t = "" Then
        hl_t = HL_NewToken()
        Session("HL_MsgToken") = hl_t
    End If
    Err.Clear
    On Error GoTo 0
    HL_EnsureMsgToken = hl_t
End Function

Function HL_OwnHost()
    Dim hl_h, hl_p
    hl_h = LCase(Trim(CStr(Request.ServerVariables("HTTP_HOST"))))
    If hl_h = "" Then hl_h = LCase(Trim(CStr(Request.ServerVariables("SERVER_NAME"))))
    hl_p = InStr(hl_h, ":")
    If hl_p > 0 Then hl_h = Left(hl_h, hl_p - 1)
    HL_OwnHost = hl_h
End Function

Function HL_IsSameSiteUrl(ByVal hl_u)
    Dim hl_host
    HL_IsSameSiteUrl = False
    hl_u = Trim(CStr(hl_u))
    If hl_u = "" Then Exit Function
    hl_host = HL_GetHostFromUrl(hl_u)
    If hl_host = "" Then Exit Function
    HL_IsSameSiteUrl = (StrComp(hl_host, HL_OwnHost(), vbTextCompare) = 0)
End Function

Function HL_MsgRateAllow(ByVal hl_ip)
    Dim hl_key, hl_last
    HL_MsgRateAllow = False
    hl_ip = Trim(CStr(hl_ip))
    If hl_ip = "" Then hl_ip = "unknown"
    hl_key = "HL_MsgT_" & hl_ip
    On Error Resume Next
    Application.Lock
    hl_last = Application(hl_key)
    If IsDate(hl_last) Then
        If DateDiff("s", CDate(hl_last), Now) < HL_MSG_COOLDOWN_SEC Then
            Application.Unlock
            Err.Clear
            On Error GoTo 0
            Exit Function
        End If
    End If
    Application(hl_key) = Now
    Application.Unlock
    Err.Clear
    On Error GoTo 0
    HL_MsgRateAllow = True
End Function

Function HL_MsgFormFields()
    Dim hl_tok
    hl_tok = HL_HtmlEnc(HL_EnsureMsgToken())
    HL_MsgFormFields = "<input type=""hidden"" name=""im_token"" value=""" & hl_tok & """>" & _
        "<div style=""position:absolute;left:-10000px;top:auto;width:1px;height:1px;overflow:hidden;"" aria-hidden=""true"">" & _
        "<label for=""im_website"">website</label>" & _
        "<input type=""text"" id=""im_website"" name=""im_website"" value="""" tabindex=""-1"" autocomplete=""off"">" & _
        "</div>"
End Function

Function HL_SaveMessage(ByVal hl_submitUrl)
    Dim hl_body, hl_token, hl_hp, hl_path, hl_old, hl_entry, hl_fso, hl_stm, hl_now
    Dim hl_ip, hl_locked, hl_ref, hl_ori, hl_header

    HL_SaveMessage = "提交无效，请刷新页面后重试。"

    If UCase(Trim(CStr(Request.ServerVariables("REQUEST_METHOD")))) <> "POST" Then Exit Function
    If Trim(CStr(Request.Form("im_do") & "")) <> "msg" Then Exit Function

    ' 蜜罐：机器人常会填隐藏域，直接丢弃并假装成功
    hl_hp = Trim(CStr(Request.Form("im_website") & ""))
    If hl_hp <> "" Then
        HL_SaveMessage = ""
        Exit Function
    End If

    hl_token = Trim(CStr(Request.Form("im_token") & ""))
    If hl_token = "" Or StrComp(hl_token, CStr(Session("HL_MsgToken") & ""), 0) <> 0 Then
        Exit Function
    End If

    hl_ori = Trim(CStr(Request.ServerVariables("HTTP_ORIGIN") & ""))
    hl_ref = Trim(CStr(Request.ServerVariables("HTTP_REFERER") & ""))
    If hl_ori <> "" Then
        If Not HL_IsSameSiteUrl(hl_ori) Then Exit Function
    End If
    If hl_ref <> "" Then
        If Not HL_IsSameSiteUrl(hl_ref) Then Exit Function
    End If

    hl_body = Trim(CStr(Request.Form("im_body") & ""))
    hl_body = Replace(hl_body, vbCrLf, vbLf)
    hl_body = Replace(hl_body, vbCr, vbLf)
    If hl_body = "" Then
        HL_SaveMessage = "请填写留言内容。"
        Exit Function
    End If
    If Len(hl_body) > HL_MSG_MAX_LEN Then hl_body = Left(hl_body, HL_MSG_MAX_LEN)
    hl_body = Replace(hl_body, "=====================", "-----")
    hl_body = HL_HtmlEnc(hl_body)
    hl_body = Replace(hl_body, vbLf, vbCrLf)

    hl_ip = Trim(CStr(Request.ServerVariables("REMOTE_ADDR") & ""))
    If Not HL_MsgRateAllow(hl_ip) Then
        HL_SaveMessage = "提交过于频繁，请稍后再试。"
        Exit Function
    End If

    hl_now = Now
    hl_entry = "时间：" & Year(hl_now) & "-" & Right("0" & Month(hl_now), 2) & "-" & Right("0" & Day(hl_now), 2) & " " & _
               Right("0" & Hour(hl_now), 2) & ":" & Right("0" & Minute(hl_now), 2) & ":" & Right("0" & Second(hl_now), 2) & vbCrLf
    hl_entry = hl_entry & "提交网址：" & HL_HtmlEnc(CStr(hl_submitUrl & "")) & vbCrLf
    hl_entry = hl_entry & "留言内容：" & vbCrLf & hl_body & vbCrLf & "=====================" & vbCrLf

    hl_path = HL_MapRoot(HL_MSG_FILE)
    hl_header = "<!DOCTYPE html>" & vbCrLf & _
                "<html lang=""zh-CN""><head><meta charset=""utf-8"">" & _
                "<meta name=""robots"" content=""noindex,nofollow"">" & _
                "<title>Message</title></head><body><pre>" & vbCrLf

    hl_locked = False
    On Error Resume Next
    Application.Lock
    hl_locked = True

    Set hl_fso = Server.CreateObject("Scripting.FileSystemObject")
    hl_old = ""
    If hl_fso.FileExists(hl_path) Then
        Set hl_stm = Server.CreateObject("ADODB.Stream")
        hl_stm.Type = 2
        hl_stm.Charset = "utf-8"
        hl_stm.Open
        hl_stm.LoadFromFile hl_path
        hl_old = hl_stm.ReadText
        hl_stm.Close
        Set hl_stm = Nothing
        If Err.Number <> 0 Then
            Err.Clear
            If hl_locked Then Application.Unlock
            Set hl_fso = Nothing
            On Error GoTo 0
            HL_SaveMessage = "读取留言文件失败。"
            Exit Function
        End If
    Else
        hl_old = hl_header
    End If

    If Len(hl_old) > 0 And Right(hl_old, 2) <> vbCrLf Then hl_old = hl_old & vbCrLf

    Set hl_stm = Server.CreateObject("ADODB.Stream")
    hl_stm.Type = 2
    hl_stm.Charset = "utf-8"
    hl_stm.Open
    hl_stm.WriteText hl_old & hl_entry
    hl_stm.SaveToFile hl_path, 2
    hl_stm.Close
    Set hl_stm = Nothing
    Set hl_fso = Nothing

    If Err.Number <> 0 Then
        Err.Clear
        If hl_locked Then Application.Unlock
        On Error GoTo 0
        HL_SaveMessage = "保存失败，请给站点目录写入权限。"
        Exit Function
    End If

    If hl_locked Then Application.Unlock
    Session("HL_MsgToken") = HL_NewToken()
    Err.Clear
    On Error GoTo 0
    HL_SaveMessage = ""
End Function

'------------------------------------------------------------
' 简易 URL 解码
'------------------------------------------------------------
Function HL_UrlDecode(ByVal hl_s)
    Dim hl_i, hl_c, hl_h, hl_t
    hl_s = Replace(CStr(hl_s), "+", " ")
    hl_t = ""
    hl_i = 1
    Do While hl_i <= Len(hl_s)
        hl_c = Mid(hl_s, hl_i, 1)
        If hl_c = "%" And hl_i + 2 <= Len(hl_s) Then
            hl_h = Mid(hl_s, hl_i + 1, 2)
            If IsNumeric("&H" & hl_h) Then
                hl_t = hl_t & Chr(CLng("&H" & hl_h))
                hl_i = hl_i + 3
            Else
                hl_t = hl_t & hl_c
                hl_i = hl_i + 1
            End If
        Else
            hl_t = hl_t & hl_c
            hl_i = hl_i + 1
        End If
    Loop
    HL_UrlDecode = hl_t
End Function

'------------------------------------------------------------
' 规范化网址：去空白、小写、https→http、去掉末尾 /（保留 www）
'------------------------------------------------------------
Function HL_NormalizeUrl(ByVal hl_s)
    Dim hl_p
    hl_s = Trim(CStr(hl_s))
    hl_s = Replace(hl_s, "\", "/")
    hl_p = InStr(hl_s, "#")
    If hl_p > 0 Then hl_s = Left(hl_s, hl_p - 1)
    hl_s = LCase(hl_s)
    If Left(hl_s, 8) = "https://" Then hl_s = "http://" & Mid(hl_s, 9)
    Do While Len(hl_s) > 8 And Right(hl_s, 1) = "/"
        hl_s = Left(hl_s, Len(hl_s) - 1)
    Loop
    HL_NormalizeUrl = hl_s
End Function

'------------------------------------------------------------
' 从网址取出主机名（保留 www.，不与无 www 合并）
'------------------------------------------------------------
Function HL_GetHostFromUrl(ByVal hl_url)
    Dim hl_s, hl_p
    hl_s = Trim(CStr(hl_url))
    hl_p = InStr(hl_s, "://")
    If hl_p > 0 Then hl_s = Mid(hl_s, hl_p + 3)
    hl_p = InStr(hl_s, "/")
    If hl_p > 0 Then hl_s = Left(hl_s, hl_p - 1)
    hl_p = InStr(hl_s, "?")
    If hl_p > 0 Then hl_s = Left(hl_s, hl_p - 1)
    hl_p = InStr(hl_s, ":")
    If hl_p > 0 Then hl_s = Left(hl_s, hl_p - 1)
    HL_GetHostFromUrl = LCase(Trim(hl_s))
End Function

'------------------------------------------------------------
' 字符按 GBK 习惯计字节：ASCII 1，其它（中文等）2
'------------------------------------------------------------
Function HL_CharByteLen(ByVal hl_ch)
    Dim hl_a
    If Len(hl_ch) = 0 Then
        HL_CharByteLen = 0
        Exit Function
    End If
    hl_a = AscW(Left(hl_ch, 1))
    If hl_a < 0 Then hl_a = hl_a + 65536
    If hl_a <= 127 Then
        HL_CharByteLen = 1
    Else
        HL_CharByteLen = 2
    End If
End Function

Function HL_TextByteLen(ByVal hl_s)
    Dim hl_i, hl_n
    hl_n = 0
    hl_s = CStr(hl_s)
    For hl_i = 1 To Len(hl_s)
        hl_n = hl_n + HL_CharByteLen(Mid(hl_s, hl_i, 1))
    Next
    HL_TextByteLen = hl_n
End Function

'------------------------------------------------------------
' 从标题随机位置截取 HL_TITLE_CLIP_BYTES 个字节，不拆双字节字符
'------------------------------------------------------------

Function HL_ClipTitle(ByVal hl_s)
    Dim hl_n, hl_total, hl_startByte, hl_i, hl_acc, hl_b, hl_out, hl_ch

    hl_s = Trim(CStr(hl_s))
    hl_n = Len(hl_s)

    If hl_n = 0 Then
        HL_ClipTitle = ""
        Exit Function
    End If

    hl_total = HL_TextByteLen(hl_s)

    If hl_total <= HL_TITLE_CLIP_BYTES Then
        HL_ClipTitle = hl_s
        Exit Function
    End If

    ' 根据字符串总字节数固定截取位置
    hl_startByte = hl_total Mod (hl_total - HL_TITLE_CLIP_BYTES + 1)

    hl_acc = 0
    hl_i = 1

    Do While hl_i <= hl_n
        hl_b = HL_CharByteLen(Mid(hl_s, hl_i, 1))

        If hl_acc + hl_b > hl_startByte Then Exit Do

        hl_acc = hl_acc + hl_b
        hl_i = hl_i + 1
    Loop

    hl_out = ""
    hl_acc = 0

    Do While hl_i <= hl_n
        hl_ch = Mid(hl_s, hl_i, 1)
        hl_b = HL_CharByteLen(hl_ch)

        If hl_acc + hl_b > HL_TITLE_CLIP_BYTES Then Exit Do

        hl_out = hl_out & hl_ch
        hl_acc = hl_acc + hl_b
        hl_i = hl_i + 1
    Loop

    HL_ClipTitle = hl_out
End Function



Function HL_ClipTitle1(ByVal hl_s)
    Dim hl_n, hl_total, hl_startByte, hl_i, hl_acc, hl_b, hl_out, hl_ch
    Randomize

    hl_s = Trim(CStr(hl_s))
    hl_n = Len(hl_s)
    If hl_n = 0 Then
        HL_ClipTitle1 = ""
        Exit Function
    End If

    hl_total = HL_TextByteLen(hl_s)
    If hl_total <= HL_TITLE_CLIP_BYTES Then
        HL_ClipTitle1 = hl_s
        Exit Function
    End If

    hl_startByte = Int(Rnd() * (hl_total - HL_TITLE_CLIP_BYTES + 1))

    hl_acc = 0
    hl_i = 1
    Do While hl_i <= hl_n
        hl_b = HL_CharByteLen(Mid(hl_s, hl_i, 1))
        If hl_acc + hl_b > hl_startByte Then Exit Do
        hl_acc = hl_acc + hl_b
        hl_i = hl_i + 1
    Loop

    hl_out = ""
    hl_acc = 0
    Do While hl_i <= hl_n
        hl_ch = Mid(hl_s, hl_i, 1)
        hl_b = HL_CharByteLen(hl_ch)
        If hl_acc + hl_b > HL_TITLE_CLIP_BYTES Then Exit Do
        hl_out = hl_out & hl_ch
        hl_acc = hl_acc + hl_b
        hl_i = hl_i + 1
    Loop
    HL_ClipTitle1 = hl_out
End Function

'------------------------------------------------------------
' 取出网址参数：url / u / href；也支持 ?http://...
'------------------------------------------------------------
Function HL_GetUrlParam()
    Dim hl_v, hl_k, hl_qs, hl_p
    hl_v = Trim(CStr(Request.QueryString("url")))
    If hl_v = "" Then hl_v = Trim(CStr(Request.QueryString("u")))
    If hl_v = "" Then hl_v = Trim(CStr(Request.QueryString("href")))

    If hl_v = "" Then
        hl_qs = Trim(CStr(Request.ServerVariables("QUERY_STRING")))
        If Left(LCase(hl_qs), 4) = "http" Then
            hl_v = HL_UrlDecode(hl_qs)
        ElseIf Left(LCase(hl_qs), 4) = "url=" Then
            hl_v = HL_UrlDecode(Mid(hl_qs, 5))
        ElseIf Left(LCase(hl_qs), 2) = "u=" Then
            hl_v = HL_UrlDecode(Mid(hl_qs, 3))
        Else
            For Each hl_k In Request.QueryString
                If InStr(1, hl_k, "http://", 1) = 1 Or InStr(1, hl_k, "https://", 1) = 1 Then
                    hl_v = hl_k
                    Exit For
                End If
            Next
        End If
    End If

    hl_p = InStr(1, hl_v, "&url=", 1)
    If hl_p > 0 Then hl_v = Left(hl_v, hl_p - 1)

    HL_GetUrlParam = Trim(hl_v)
End Function

'------------------------------------------------------------
' 以 GBK 读取清单，再转成 Unicode
' 调用页若是 CODEPAGE=65001，直接 Stream.Charset=gb2312 会读成乱码（如 ?????）
' 先按二进制读入，再在代码页 936 下用 gb2312 解码
'------------------------------------------------------------
Function HL_ReadListText(ByVal hl_filePath)
    Dim hl_binStm, hl_txtStm, hl_bin, hl_text
    Dim hl_oldSess, hl_oldResp

    ' 无论传入相对名还是当前目录物理路径，都改读网站根目录下的同名文件
    hl_filePath = HL_MapRoot(hl_filePath)

    Set hl_binStm = Server.CreateObject("ADODB.Stream")
    hl_binStm.Type = 1
    hl_binStm.Open
    hl_binStm.LoadFromFile hl_filePath
    hl_bin = hl_binStm.Read
    hl_binStm.Close
    Set hl_binStm = Nothing

    hl_oldSess = 0
    hl_oldResp = 0
    On Error Resume Next
    hl_oldSess = Session.CodePage
    hl_oldResp = Response.CodePage
    Session.CodePage = 936
    Response.CodePage = 936
    Err.Clear
    On Error GoTo 0

    Set hl_txtStm = Server.CreateObject("ADODB.Stream")
    hl_txtStm.Type = 1
    hl_txtStm.Open
    hl_txtStm.Write hl_bin
    hl_txtStm.Position = 0
    hl_txtStm.Type = 2
    hl_txtStm.Charset = "gb2312"
    hl_text = hl_txtStm.ReadText
    hl_txtStm.Close
    Set hl_txtStm = Nothing

    On Error Resume Next
    If hl_oldSess <> 0 Then Session.CodePage = hl_oldSess
    If hl_oldResp <> 0 Then Response.CodePage = hl_oldResp
    Err.Clear
    On Error GoTo 0

    HL_ReadListText = hl_text
End Function

'------------------------------------------------------------
' 取一行最右侧标题
'------------------------------------------------------------
Function HL_GetLastField(hl_parts)
    Dim hl_i, hl_s
    HL_GetLastField = ""
    If Not IsArray(hl_parts) Then Exit Function
    For hl_i = UBound(hl_parts) To 0 Step -1
        hl_s = Trim(CStr(hl_parts(hl_i)))
        If hl_s <> "" Then
            HL_GetLastField = hl_s
            Exit Function
        End If
    Next
End Function

'------------------------------------------------------------
' 收集结果：返回 Dictionary
'   ok, err, url_param
'   found_idx, found_url, found_title, found_host, total
'   front_need, tail_need  本次使用的前N / 后M
'   count, same_got, other_got
'   u_0..  t_0..  k_0..  h_0..
'------------------------------------------------------------
Const HL_LIST_FILE = "ClientHtml.html"
Const HL_TITLE_CLIP_BYTES = 7

Function HL_Collect(ByVal hl_targetUrl)
    Dim hl_fso, hl_listPath, hl_listText, hl_lines
    Dim hl_i, hl_line, hl_parts, hl_locUrl, hl_locTitle
    Dim hl_allUrl(), hl_allTitle(), hl_n, hl_found, hl_needle, hl_cand
    Dim hl_curHost, hl_seen, hl_key, hl_host
    Dim hl_sameUrl(), hl_sameTitle(), hl_nSame
    Dim hl_otherUrl(), hl_otherTitle(), hl_nOther
    Dim hl_sameUsed, hl_otherUsed
    Dim hl_reserved(), hl_reservedCount, hl_reservedHosts
    Dim hl_upUrl(), hl_upTitle(), hl_upKind(), hl_upCount
    Dim hl_sameGot, hl_otherGot
    Dim hl_dic
    Dim hl_frontNeed, hl_tailNeed, hl_upNeed

    On Error Resume Next
    hl_targetUrl = Trim(CStr(hl_targetUrl & ""))
    If Err.Number <> 0 Then
        Err.Clear
        hl_targetUrl = ""
    End If
    On Error GoTo 0

    ' 本次条数：优先用 HL_SetCounts 改过的运行时值
    hl_frontNeed = HL_NormCount(HL_CfgFrontCount, HL_FRONT_COUNT)
    hl_tailNeed = HL_NormCount(HL_CfgTailCount, HL_TAIL_COUNT)
    hl_upNeed = hl_frontNeed + hl_tailNeed

    Set hl_dic = Server.CreateObject("Scripting.Dictionary")
    hl_dic.CompareMode = 1
    hl_dic("ok") = False
    hl_dic("err") = ""
    hl_dic("url_param") = Trim(CStr(hl_targetUrl))
    hl_dic("found_idx") = -1
    hl_dic("found_url") = ""
    hl_dic("found_title") = ""
    hl_dic("found_host") = ""
    hl_dic("total") = 0
    hl_dic("count") = 0
    hl_dic("same_got") = 0
    hl_dic("other_got") = 0
    hl_dic("front_need") = hl_frontNeed
    hl_dic("tail_need") = hl_tailNeed

    Set hl_fso = Server.CreateObject("Scripting.FileSystemObject")
    hl_listPath = HL_MapRoot(HL_LIST_FILE)
    If Not hl_fso.FileExists(hl_listPath) Then
        hl_dic("err") = "找不到清单文件：" & HL_LIST_FILE
        Set hl_fso = Nothing
        Set HL_Collect = hl_dic
        Exit Function
    End If
    Set hl_fso = Nothing

    hl_listText = HL_ReadListText(hl_listPath)
    hl_listText = Replace(hl_listText, vbCrLf, vbLf)
    hl_listText = Replace(hl_listText, vbCr, vbLf)
    hl_lines = Split(hl_listText, vbLf)

    ReDim hl_allUrl(UBound(hl_lines))
    ReDim hl_allTitle(UBound(hl_lines))
    hl_n = -1

    For hl_i = 0 To UBound(hl_lines)
        hl_line = Trim(hl_lines(hl_i))
        If hl_line <> "" And InStr(hl_line, vbTab) > 0 Then
            hl_parts = Split(hl_line, vbTab)
            hl_locUrl = Trim(CStr(hl_parts(0)))
            'hl_locTitle = HL_GetLastField(hl_parts)
            hl_locTitle = Trim(CStr(hl_parts(3)))
            If hl_locUrl <> "" Then
                hl_n = hl_n + 1
                hl_allUrl(hl_n) = hl_locUrl
                If hl_locTitle = "" Or hl_locTitle = hl_locUrl Then
                    hl_allTitle(hl_n) = hl_locUrl
                Else
                    hl_allTitle(hl_n) = hl_locTitle
                End If
            End If
        End If
    Next

    If hl_n < 0 Then
        hl_dic("err") = "清单文件中没有有效记录。"
        Set HL_Collect = hl_dic
        Exit Function
    End If

    hl_dic("total") = hl_n + 1
    hl_needle = HL_NormalizeUrl(hl_targetUrl)
    hl_found = -1

    For hl_i = 0 To hl_n
        If HL_NormalizeUrl(hl_allUrl(hl_i)) = hl_needle Then
            hl_found = hl_i
            Exit For
        End If
    Next

    If hl_found < 0 Then
        For hl_i = 0 To hl_n
            hl_cand = HL_NormalizeUrl(hl_allUrl(hl_i))
            If hl_cand = hl_needle Or Right(hl_cand, Len(hl_needle)) = hl_needle Then
                hl_found = hl_i
                Exit For
            End If
            If Len(hl_needle) >= 8 And InStr(1, hl_cand, hl_needle, 1) > 0 Then
                hl_found = hl_i
                Exit For
            End If
        Next
    End If

    If hl_found < 0 Then
        hl_dic("err") = "清单中未找到该网址。"
        Set HL_Collect = hl_dic
        Exit Function
    End If

    hl_dic("found_idx") = hl_found
    hl_dic("found_url") = hl_allUrl(hl_found)
    hl_dic("found_title") = hl_allTitle(hl_found)
    hl_curHost = HL_GetHostFromUrl(hl_allUrl(hl_found))
    hl_dic("found_host") = hl_curHost
    hl_needle = HL_NormalizeUrl(hl_allUrl(hl_found))

    ReDim hl_sameUrl(hl_n)
    ReDim hl_sameTitle(hl_n)
    ReDim hl_otherUrl(hl_n)
    ReDim hl_otherTitle(hl_n)
    hl_nSame = -1
    hl_nOther = -1
    Set hl_seen = Server.CreateObject("Scripting.Dictionary")
    hl_seen.CompareMode = 1

    For hl_i = hl_found - 1 To 0 Step -1
        hl_key = HL_NormalizeUrl(hl_allUrl(hl_i))
        If hl_key <> "" And hl_key <> hl_needle Then
            If Not hl_seen.Exists(hl_key) Then
                hl_seen.Add hl_key, 1
                hl_host = HL_GetHostFromUrl(hl_allUrl(hl_i))
                If hl_host <> "" And hl_host = hl_curHost Then
                    hl_nSame = hl_nSame + 1
                    hl_sameUrl(hl_nSame) = hl_allUrl(hl_i)
                    hl_sameTitle(hl_nSame) = hl_allTitle(hl_i)
                Else
                    hl_nOther = hl_nOther + 1
                    hl_otherUrl(hl_nOther) = hl_allUrl(hl_i)
                    hl_otherTitle(hl_nOther) = hl_allTitle(hl_i)
                End If
            End If
        End If
    Next
    For hl_i = hl_n To hl_found + 1 Step -1
        hl_key = HL_NormalizeUrl(hl_allUrl(hl_i))
        If hl_key <> "" And hl_key <> hl_needle Then
            If Not hl_seen.Exists(hl_key) Then
                hl_seen.Add hl_key, 1
                hl_host = HL_GetHostFromUrl(hl_allUrl(hl_i))
                If hl_host <> "" And hl_host = hl_curHost Then
                    hl_nSame = hl_nSame + 1
                    hl_sameUrl(hl_nSame) = hl_allUrl(hl_i)
                    hl_sameTitle(hl_nSame) = hl_allTitle(hl_i)
                Else
                    hl_nOther = hl_nOther + 1
                    hl_otherUrl(hl_nOther) = hl_allUrl(hl_i)
                    hl_otherTitle(hl_nOther) = hl_allTitle(hl_i)
                End If
            End If
        End If
    Next
    Set hl_seen = Nothing

    '------------------------------------------------------------
    ' 先为「后 M 条」预留 M 个互不相同的异域主机
    ' 预留的是整域，不只是某条 URL，这样前 N 条补齐时也不会再出现这些域名
    '------------------------------------------------------------
    Set hl_reservedHosts = Server.CreateObject("Scripting.Dictionary")
    hl_reservedHosts.CompareMode = 1
    hl_reservedCount = 0
    If hl_tailNeed > 0 Then
        ReDim hl_reserved(hl_tailNeed - 1)
        For hl_i = 0 To hl_tailNeed - 1
            hl_reserved(hl_i) = -1
        Next
        For hl_i = 0 To hl_nOther
            hl_host = HL_GetHostFromUrl(hl_otherUrl(hl_i))
            If hl_host <> "" Then
                If Not hl_reservedHosts.Exists(hl_host) Then
                    hl_reserved(hl_reservedCount) = hl_i
                    hl_reservedHosts.Add hl_host, True
                    hl_reservedCount = hl_reservedCount + 1
                    If hl_reservedCount >= hl_tailNeed Then Exit For
                End If
            End If
        Next
    End If

    ' 结果槽：前 N + 后 M；为 0 时仍返回成功（count=0）
    If hl_upNeed < 1 Then
        hl_dic("ok") = True
        Set hl_reservedHosts = Nothing
        Set HL_Collect = hl_dic
        Exit Function
    End If

    ReDim hl_upUrl(hl_upNeed - 1)
    ReDim hl_upTitle(hl_upNeed - 1)
    ReDim hl_upKind(hl_upNeed - 1)
    hl_upCount = 0
    hl_sameUsed = 0
    hl_otherUsed = 0
    hl_sameGot = 0
    hl_otherGot = 0

    ' 前 N 条：先填同域名
    Do While hl_upCount < hl_frontNeed And hl_sameUsed <= hl_nSame
        hl_upUrl(hl_upCount) = hl_sameUrl(hl_sameUsed)
        hl_upTitle(hl_upCount) = hl_sameTitle(hl_sameUsed)
        hl_upKind(hl_upCount) = "same"
        hl_upCount = hl_upCount + 1
        hl_sameUsed = hl_sameUsed + 1
        hl_sameGot = hl_sameGot + 1
    Loop

    ' 前 N 条不够：用异域名补，但跳过已预留给后 M 条的整域
    Do While hl_upCount < hl_frontNeed And hl_otherUsed <= hl_nOther
        hl_host = HL_GetHostFromUrl(hl_otherUrl(hl_otherUsed))
        If hl_host <> "" And hl_reservedHosts.Exists(hl_host) Then
            hl_otherUsed = hl_otherUsed + 1
        Else
            hl_upUrl(hl_upCount) = hl_otherUrl(hl_otherUsed)
            hl_upTitle(hl_upCount) = hl_otherTitle(hl_otherUsed)
            hl_upKind(hl_upCount) = "other"
            hl_upCount = hl_upCount + 1
            hl_otherUsed = hl_otherUsed + 1
            hl_otherGot = hl_otherGot + 1
        End If
    Loop

    ' 后 M 条：追加预留项（域名互不相同，且已保证不出现在前 N 条里）
    For hl_i = 0 To hl_reservedCount - 1
        If hl_upCount >= hl_upNeed Then Exit For
        If hl_reserved(hl_i) >= 0 Then
            hl_upUrl(hl_upCount) = hl_otherUrl(hl_reserved(hl_i))
            hl_upTitle(hl_upCount) = hl_otherTitle(hl_reserved(hl_i))
            hl_upKind(hl_upCount) = "other"
            hl_upCount = hl_upCount + 1
            hl_otherGot = hl_otherGot + 1
        End If
    Next
    Set hl_reservedHosts = Nothing

    hl_dic("ok") = True
    hl_dic("count") = hl_upCount
    hl_dic("same_got") = hl_sameGot
    hl_dic("other_got") = hl_otherGot
    For hl_i = 0 To hl_upCount - 1
        hl_dic("u_" & hl_i) = hl_upUrl(hl_i)
        hl_dic("t_" & hl_i) = hl_upTitle(hl_i)
        hl_dic("k_" & hl_i) = hl_upKind(hl_i)
        hl_dic("h_" & hl_i) = HL_GetHostFromUrl(hl_upUrl(hl_i))
    Next

    Set HL_Collect = hl_dic
End Function

'------------------------------------------------------------
' 按指定前N/后M收集（不影响页面里 HL_SetCounts 的当前值）
'   Set hl_data = HL_CollectEx(url, 3, 2)
'------------------------------------------------------------
Function HL_CollectEx(ByVal hl_targetUrl, ByVal hl_front, ByVal hl_tail)
    Dim hl_oldFront, hl_oldTail
    hl_oldFront = HL_CfgFrontCount
    hl_oldTail = HL_CfgTailCount
    Call HL_SetCounts(hl_front, hl_tail)
    Set HL_CollectEx = HL_Collect(hl_targetUrl)
    HL_CfgFrontCount = hl_oldFront
    HL_CfgTailCount = hl_oldTail
End Function

'------------------------------------------------------------
' 外部推荐：把结果写入 ByRef 字典，不必在调用处写 Set
'   Dim hl_data
'   Call HL_CollectInto("http://www.b8d.net/travel/25.html", hl_data)
'------------------------------------------------------------
Sub HL_CollectInto(ByVal hl_targetUrl, ByRef hl_outDic)
    Set hl_outDic = HL_Collect(hl_targetUrl)
End Sub

'------------------------------------------------------------
' 取单个字段（返回普通值，不用 Set）
'   HL_CollectValue(url, "found_url")
'   HL_CollectValue(url, "u_0")   第 1 条网址
'------------------------------------------------------------
Function HL_CollectValue(ByVal hl_targetUrl, ByVal hl_key)
    Dim hl_data, hl_name
    hl_name = Trim(CStr(hl_key))
    Set hl_data = HL_Collect(hl_targetUrl)
    If hl_name <> "" And hl_data.Exists(hl_name) Then
        HL_CollectValue = hl_data(hl_name)
    Else
        HL_CollectValue = Empty
    End If
    Set hl_data = Nothing
End Function

'------------------------------------------------------------
' 片段样式（class 一律 hl- 前缀，避免污染宿主页）
'------------------------------------------------------------
Function HL_Css()
    Dim hl_styleBlock
    hl_styleBlock = "<style type=""text/css"">" & _
        ".hl-box{margin:0;padding:0;font-family:Segoe UI,PingFang SC,Microsoft YaHei,sans-serif;}" & _
        ".hl-list{margin:0;padding:0;list-style:none;}" & _
        ".hl-item{padding:10px 0;border-top:1px solid #ddd;}" & _
        ".hl-item:first-child{border-top:0;}" & _
        ".hl-kind{display:inline-block;font-size:11px;padding:1px 7px;border-radius:999px;margin:0 6px 4px 0;}" & _
        ".hl-kind-same{color:#0a7a4a;background:#e8f7ef;border:1px solid #b7e0c8;}" & _
        ".hl-kind-other{color:#8a5a00;background:#fff4d6;border:1px solid #e6c97a;}" & _
        ".hl-host{font-weight:700;font-size:13px;margin:2px 0 4px;word-break:break-all;}" & _
        ".hl-title{color:#123;text-decoration:none;font-weight:700;font-size:15px;}" & _
        ".hl-title:hover{text-decoration:underline;}" & _
        ".hl-url{display:block;color:#667;font-size:12px;word-break:break-all;text-decoration:none;margin-top:4px;}" & _
        ".hl-msg{color:#a60;font-size:13px;}" & _
        "</style>"
    HL_Css = hl_styleBlock
End Function

'------------------------------------------------------------
' 生成列表 HTML 片段（不含完整页面）
' hl_targetUrl 为空时自动读 ?url=
'------------------------------------------------------------
Function HL_BuildHtml(ByVal hl_targetUrl)
    Dim hl_dic, hl_html, hl_i, hl_url, hl_title, hl_kind, hl_host, hl_kindClass, hl_kindText

    Randomize
    hl_targetUrl = Trim(CStr(hl_targetUrl))
    If hl_targetUrl = "" Then hl_targetUrl = HL_GetUrlParam()

    If hl_targetUrl = "" Then
        HL_BuildHtml = HL_Css() & "<div class=""hl-box hl-msg"">请传入网址参数。</div>"
        Exit Function
    End If

    Set hl_dic = HL_Collect(hl_targetUrl)
    If hl_dic("ok") <> True Then
        HL_BuildHtml = HL_Css() & "<div class=""hl-box hl-msg"">" & HL_HtmlEnc(hl_dic("err")) & "</div>"
        Set hl_dic = Nothing
        Exit Function
    End If

    hl_html = HL_Css() & "<div class=""hl-box""><ol class=""hl-list"">"
    If CLng(hl_dic("count")) <= 0 Then
        hl_html = hl_html & "<li class=""hl-msg"">没有可列出的网址。</li>"
    Else
        For hl_i = 0 To CLng(hl_dic("count")) - 1
            hl_url = CStr(hl_dic("u_" & hl_i))
            hl_title = CStr(hl_dic("t_" & hl_i))
            hl_kind = CStr(hl_dic("k_" & hl_i))
            hl_host = CStr(hl_dic("h_" & hl_i))
            If hl_kind = "same" Then
                hl_kindClass = "hl-kind hl-kind-same"
                hl_kindText = "同域名"
            Else
                hl_kindClass = "hl-kind hl-kind-other"
                hl_kindText = "其他域名"
            End If
            hl_html = hl_html & "<li class=""hl-item"">"
            hl_html = hl_html & "<span class=""" & hl_kindClass & """>" & hl_kindText & "</span>"
            hl_html = hl_html & "<div class=""hl-host"">" & HL_HtmlEnc(hl_host) & "</div>"
            hl_html = hl_html & "<a class=""hl-title"" href=""" & HL_HtmlEnc(hl_url) & """ title=""" & HL_HtmlEnc(hl_title) & """>"
            hl_html = hl_html & HL_HtmlEnc(HL_ClipTitle(hl_title)) & "</a>"
            hl_html = hl_html & "<a class=""hl-url"" href=""" & HL_HtmlEnc(hl_url) & """>" & HL_HtmlEnc(hl_url) & "</a>"
            hl_html = hl_html & "</li>"
        Next
    End If
    hl_html = hl_html & "</ol></div>"

    Set hl_dic = Nothing
    HL_BuildHtml = hl_html
End Function

'------------------------------------------------------------
' 直接输出列表 HTML
'------------------------------------------------------------
Sub HL_Render(ByVal hl_targetUrl)
    Response.Write HL_BuildHtml(hl_targetUrl)
End Sub

'------------------------------------------------------------
' 是否正在直接访问本文件（被 include 时为 False）
'------------------------------------------------------------
Function HL_IsOwnPage()
    Dim hl_sn
    hl_sn = Replace(LCase(CStr(Request.ServerVariables("SCRIPT_NAME"))), "\", "/")
    If Right(hl_sn, 14) = "/hyperlink.asp" Then
        HL_IsOwnPage = True
    ElseIf hl_sn = "hyperlink.asp" Then
        HL_IsOwnPage = True
    Else
        HL_IsOwnPage = False
    End If
End Function

'------------------------------------------------------------
' 仅直接打开 Hyperlink.asp 时输出完整演示页（变量都在 Sub 内，不泄漏）
'------------------------------------------------------------
Sub HL_WriteStandalonePage()
    Dim hl_demoUrl, hl_demoHtml
    Response.Buffer = True
    Response.CodePage = 65001
    Response.Charset = "utf-8"
    Response.ContentType = "text/html; charset=utf-8"
    hl_demoUrl = HL_GetUrlParam()
    hl_demoHtml = HL_BuildHtml(hl_demoUrl)
    Response.Write "<!DOCTYPE html>" & vbCrLf
    Response.Write "<html lang=""zh-CN""><head><meta charset=""utf-8"">"
    Response.Write "<meta name=""viewport"" content=""width=device-width, initial-scale=1"">"
    Response.Write "<title>相关 " & CStr(HL_NormCount(HL_CfgFrontCount, HL_FRONT_COUNT) + HL_NormCount(HL_CfgTailCount, HL_TAIL_COUNT)) & " 个网址</title></head><body>"
    Response.Write "<form method=""get"" action=""Hyperlink.asp"" style=""margin:12px 0;"">"
    Response.Write "<input type=""text"" name=""url"" value=""" & HL_HtmlEnc(hl_demoUrl) & """ style=""width:70%;max-width:640px;"" placeholder=""http://example.com/page.html"">"
    Response.Write " <button type=""submit"">查找</button></form>"
    Response.Write hl_demoHtml
    Response.Write "</body></html>"
End Sub

If HL_IsOwnPage() Then
    Call HL_WriteStandalonePage()
End If
%>
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>http://sitemap.i5gg.com/</loc>
    <lastmod>2026/9/7 0:00:03</lastmod>
    <changefreq>weekly</changefreq>
    <priority>1.0</priority>
  </url>

  <url>
    <loc>http://sitemap.i5gg.com/wenzhang/factor/2023/0127/540.php</loc>
    <lastmod>2026-09-02T09:42:18</lastmod>
    <changefreq>weekly</changefreq>
    <priority>0.5</priority>
  </url>
</urlset>