<%
'######################################################################
'## Dao.Cls.asp
'## -------------------------------------------------------------------
'## Feature     :   App Dao
'## Version     :   v0.1
'## Author      :   Lajox (lajox@19www.com)
'## Update Date :   2014/03/26 14:58
'## HomePage	:   http://www.19www.com
'## -------------------------------------------------------------------
'## Description :   App Dao Core
'## -------------------------------------------------------------------
'## Demo
'Dim Dao
'Set Dao = Easp.Ext("dao")
''--数据测试：
'Dim oRs
'Set oRs = Easp.db.GRS("Select Top 20 a.Id,a.Title,a.AddTime From HL_News a Left Join HL_Type b On a.ClassId = b.Id")
'Set oRs = Dao.LimitRs(oRs, 0, 12) '对oRs结果集进行Limit限制, 取第 （5+1）~（5+10）条， 共10条
'If Dao.RsCount(oRs)>0 Then
'    Easp.WN "共有"& Dao.RsCount(oRs) & "条记录："
'    Do While Not oRs.EOF
'        Easp.WN "Id:" & oRs("id") & "、" & oRs("title") & " - " & oRs("addtime") & " "
'        oRs.MoveNext
'    Loop
'Else
'    Easp.WN "暂无记录！"
'End If
'Dao.Reset() '清空查询条件
'Set Dao = Nothing
'######################################################################
Class EasyAsp_Dao

    Private s_dictName
    Private s_selectSql, s_querySql, s_execType, s_execSql
    Private o_ds, o_fs
    Public ComFun

    Private Sub Class_Initialize()
        On Error Resume Next
        Set ComFun = New Cls_ComFunc
        s_dictName = "Scripting.Dictionary"
        s_selectSql = "Select%TOP% %FIELD% From %TABLE%%ALIAS%%JOIN%%ON%%WHERE%%GROUP%%HAVING%%ORDER%%UNION%"
        s_querySql = ""
        s_execType = 1
        Set o_ds = Server.CreateObject(s_dictName)
        o_ds.CompareMode = 1
        Set o_fs = Server.CreateObject(s_dictName)
        o_fs.CompareMode = 1
        o_ds("table") = "TABLE"
        On Error GoTo 0
    End Sub

    Private Sub Class_Terminate()
        Set o_ds = Nothing
        Set o_fs = Nothing
    End Sub

    Public Function [New]()
        Set [New] = New EasyAsp_Dao
    End Function

    Public Property Let Table(ByVal s)
        o_ds("table") = Trim(s)
        If InStr(s, " ")>0 Then
            o_ds("table") = Trim(Easp.CLeft(s, " "))
            o_ds("alias") = Trim(Easp.CRight(s, " "))
        End If
    End Property

    Public Property Get Table()
        Table = o_ds("table")
    End Property

    Public Function T(ByVal s)
        If Not Easp.IsN(Trim(s)) Then
            o_ds("table") = Trim(s)
        End If
        If InStr(o_ds("table"), " ")>0 Then
            o_ds("table") = Trim(Easp.CLeft(o_ds("table"), " "))
            o_ds("alias") = Trim(Easp.CRight(o_ds("table"), " "))
        End If
        Set T = Me
    End Function

    Public Function From(ByVal s)
        Set From = Me.T(s)
    End Function

    Public Function [Alias](ByVal s)
        If Not Easp.IsN(Trim(s)) Then
            o_ds("alias") = Trim(s)
        End If
        Set [Alias] = Me
    End Function

    Public Function GetAlias()
        Dim s
        If o_ds.Exists("alias") Then
            s = Trim(o_ds("alias"))
        Else
            If InStr(o_ds("table"), " ")>0 Then
                o_ds("table") = Trim(Easp.CLeft(o_ds("table"), " "))
                o_ds("alias") = Trim(Easp.CRight(o_ds("table"), " "))
            End If
            s = Trim(o_ds("alias"))
        End If
        GetAlias = s
    End Function

    Public Function [Select](ByVal s)
        Set [Select] = Me.Field(s)
    End Function

    Public Function Field(ByVal s)
        If Not Easp.IsN(s) Then
            If IsObject(s) Then
                Set o_ds("field") = s
            Else
                o_ds("field") = s
            End If
        Else
            o_ds("field") = "*"
        End If
        Set Field = Me
    End Function

    Public Function Where(ByVal s)
        If ComFun.IsString(s) Then
            s = Trim(s)
        End If
        If Not Easp.IsN(s) Then
            If IsObject(s) Then
                Set o_ds("where") = s
            Else
                o_ds("where") = s
            End If
        End If
        Set Where = Me
    End Function

    Public Function Order(ByVal s)
        If Not Easp.IsN(s) Then
            o_ds("order") = s
        End If
        Set Order = Me
    End Function

    Public Function Group(ByVal s)
        If Not Easp.IsN(s) Then
            o_ds("group") = s
        End If
        Set Group = Me
    End Function

    Public Function Having(ByVal s)
        If Not Easp.IsN(s) Then
            o_ds("having") = s
        End If
        Set Having = Me
    End Function

    Public Function [Join](ByVal s)
        If Not Easp.IsN(s) Then
            If IsObject(s) Then
                Set o_ds("join") = s
            Else
                o_ds("join") = s
            End If
        End If
        Set [Join] = Me
    End Function

    Public Function [On](ByVal s)
        If Not Easp.IsN(s) Then
            o_ds("on") = s
        End If
        Set [On] = Me
    End Function

    Public Function Union(ByVal s)
        If Not Easp.IsN(s) Then
            o_ds("union") = s
        End If
        Set Union = Me
    End Function

    Public Function Top(ByVal s)
        If Trim(s)<>"" Then
            o_ds("top") = s
        End If
        Set Top = Me
    End Function

    '编译解析

    Public Sub Compile()
        Dim Matches, Match, t
        s_execSql = ""
        o_ds("table") = Trim(o_ds("table"))
        If InStr(o_ds("table"), " ")>0 Then
            o_ds("table") = Trim(Easp.CLeft(o_ds("table"), " "))
            o_ds("alias") = Trim(Easp.CRight(o_ds("table"), " "))
        End If
        Select Case s_execType
            Case 1
                s_execSql = parseSql(s_selectSql, o_ds)
            Case Else
                s_execSql = parseSql(s_querySql, "")
        End Select
        '让其支持类似{rq:id}获取Request.QueryString("id")数据
        Set Matches = Easp.RegMatch(s_execSql, "\{rq\:(\w+)\}")
        For Each Match In Matches
            t = Trim(Match.SubMatches(0))
            s_execSql = ComFun.RP(s_execSql, Match.Value, Request.QueryString(t))
        Next
        '让其支持类似{$id}获取全局变量id的数据
        s_execSql = ComFun.RP(s_execSql, Array("\\", "\$"), Array(Chr(15), Chr(16)))
        Set Matches = Easp.RegMatch(s_execSql, "\{\$(\w+)\}")
        For Each Match In Matches
            t = Trim(Match.SubMatches(0))
            s_execSql = ComFun.RP(s_execSql, Match.Value, Eval(t))
        Next
        s_execSql = ComFun.RP(s_execSql, Array(Chr(15), Chr(16)), Array("\", "$"))
        Set Matches = Nothing
    End Sub

    '重置数据查询（清空上次查询）

    Public Function Reset()
        o_ds.RemoveAll()
        o_ds("table") = "TABLE"
        s_execType = 1
        s_querySql = ""
        Set Reset = Me
    End Function

    Public Function Query(ByVal s)
        If Trim(s)<>"" Then
            s_querySql = s
            s_execType = -1
        End If
        Set Query = Me
    End Function

    Public Function getSQL()
        Me.Compile() '自编译
        getSQL = s_execSql
    End Function


    Public Function lastSQL()
        lastSQL = Me.getSQL()
    End Function

    Public Function RsCount(ByVal Rs)
        On Error Resume Next
        Dim n
        n = 0
        If ComFun.IsRecordset(Rs) Then
            n = Rs.RecordCount
            If n = -1 Then
                n = UBound(Rs.GetRows(), 2) + 1
            End If
        End If
        RsCount = n
        On Error GoTo 0
    End Function

    '对Rs结果集进行Limit限制
    'LimitRs(rs,0,0) 取全部数据（第1条(0+1=1)数据开始到结束的数据）
    'LimitRs(rs,0,1) 取从（第1条(0+1=1)数据开始的1条数据，即：第1~1条）（共1条）
    'LimitRs(rs,3,0) 取从（第4条(3+1=4)数据开始到结束的数据，即：第4~最后一条）
    'LimitRs(rs,2,5) 表示 第3条(2+1=3)数据开始的5条数据，即：第3~第7条）（共5条）
    'LimitRs(rs,1,2) 表示 第2条(1+1=2)数据开始的2条数据，即：第2~第3条）（共2条）
    'LimitRs(rs,4,6) 可以这么算：表示 第4+1=5条 到 第4+6=10条）（共6条）

    Public Function LimitRs(ByVal Rs, ByVal offset, ByVal rows)
        'On Error Resume Next
        On Error GoTo 0
        Dim start, over, n, i
        offset = CLng(offset)
        rows = CLng(rows)
        If ComFun.IsRecordset(Rs) Then
            Set LimitRs = Rs
            If Not ( Easp.IsN(Rs) Or (offset<= 0 And rows<= 0) ) Then
                If offset<0 Then
                    offset = 0
                End If
                If rows<0 Then
                    rows = 0
                End If
                n = Me.RsCount(Rs) 'Rs记录数
                If rows = 0 Then
                    rows = n
                End If
                start = offset + 1
                over = offset + rows
                If over>n Then
                    over = n
                End If
                Set LimitRs = Me.CloneRs(Rs)
                i = 1
                If start>n Then
                    Do While Not LimitRs.EOF
                        i = i + 1
                        LimitRs.Delete
                        LimitRs.MoveNext
                    Loop
                    If i > 1 Then
                        LimitRs.UpdateBatch()
                        LimitRs.MoveFirst()
                    End If
                Else
                    Do While Not LimitRs.EOF
                        If i<start Then
                            LimitRs.Delete
                        End If
                        If i>over Then
                            LimitRs.Delete
                        End If
                        i = i + 1
                        LimitRs.MoveNext
                    Loop
                    If i > 1 Then
                        LimitRs.UpdateBatch()
                        If Me.RsCount(LimitRs) > 0 Then
                            LimitRs.MoveFirst()
                        End If
                    End If
                End If
            End If
        Else
            If IsObject(Rs) Then
                Set LimitRs = Rs
            Else
                LimitRs = Rs
            End If
        End If
        On Error GoTo 0
    End Function

    '克隆Rs数据集

    Public Function CloneRs(ByVal Rs)
        On Error Resume Next
        Dim oRs, i, j, f, v, a, b, pos
        pos = 0
        If ComFun.IsRecordset(Rs) Then
            pos = Rs.AbsolutePosition '记录游标位置
            Set oRs = Server.CreateObject("Adodb.Recordset")
            oRs.CursorLocation = 1
            oRs.CursorType = 3
            For i = 0 To Rs.Fields.Count -1
                f = Rs.Fields(i).Name
                v = Rs.Fields(i).Value
                oRs.Fields.Append Rs.Fields(i).Name, Rs.Fields(i).Type, Rs.Fields(i).DefinedSize, Rs.Fields(i).Attributes
            Next
            oRs.Open
            n = Me.RsCount(Rs) 'Rs的记录数
            If n>0 Then
                Rs.MoveFirst()
            End If
            Do While Not Rs.EOF
                a = Array()
                b = Array()
                For j = 0 To Rs.Fields.Count -1
                    f = Rs.Fields(j).Name
                    v = Rs.Fields(j).Value
                    If Not ComFun.InArray(Rs.Fields(j).Type, Array( 7, 135 )) Then
                        'v = Easp.IIF(Easp.IsN(v),"",v)
                    End If
                    a = ComFun.Push(a, f)
                    b = ComFun.Push(b, v)
                Next
                oRs.AddNew a, b
                oRs.Update()
                Rs.MoveNext
            Loop
            n = Me.RsCount(oRs) 'oRs的记录数
            If n>0 Then
                oRs.MoveFirst()
            End If
            If pos>0 Then
                Rs.MoveFirst()
                Rs.Move(pos -1)
            End If
            Set CloneRs = oRs
        End If
        On Error GoTo 0
    End Function

    '-- 替换SQL语句中表达式

    Private Function parseSql(ByVal sql, ByVal opt)
        sql = ComFun.RP(sql, Array("\\", "\@"), Array(Chr(17), Chr(18)))
        sql = ComFun.RP(sql, Array(Chr(17), Chr(18)), Array("\", "@"))
        If ComFun.IsDictionary(opt) Then
            sql = ComFun.RP(sql, _
                  Array("%TABLE%", "%ALIAS%", "%FIELD%", "%TOP%", "%JOIN%", "%ON%", "%WHERE%", "%GROUP%", "%HAVING%", "%ORDER%", "%UNION%"), _
                  Array( _
                  parseTable(Easp.IIf(Not(Easp.IsN(opt("table"))), opt("table"), "TABLE")), _
                  parseAlias(Easp.IIf(Not(Easp.IsN(opt("alias"))), opt("alias"), "")), _
                  parseField(Easp.IIf(Not(Easp.IsN(opt("field"))), opt("field"), "*")), _
                  parseTop(Easp.IIf(Not(Easp.IsN(opt("top"))), Easp.IIf(CLng(opt("top"))>0, opt("top"), ""), "")), _
                  parseJoin(Easp.IIf(Not(Easp.IsN(opt("join"))), opt("join"), "")), _
                  parseOn(Easp.IIf(Not(Easp.IsN(opt("on"))), opt("on"), "")), _
                  parseWhere(Easp.IIf(Not(Easp.IsN(opt("where"))), opt("where"), "")), _
                  parseGroup(Easp.IIf(Not(Easp.IsN(opt("group"))), opt("group"), "")), _
                  parseHaving(Easp.IIf(Not(Easp.IsN(opt("having"))), opt("having"), "")), _
                  parseOrder(Easp.IIf(Not(Easp.IsN(opt("order"))), opt("order"), "")), _
                  parseUnion(Easp.IIf(Not(Easp.IsN(opt("union"))), opt("union"), "")) _
                  ))
        End If
        parseSql = sql
    End Function

    '-- table分析

    Private Function parseTable(ByVal tables)
        Dim arr
        arr = Array()
        If Not Easp.IsN(tables) Then
            If IsArray(tables) Then
                For Each j in tables
                    arr = ComFun.Push(arr, j)
                Next
                tables = ComFun.Join(arr, ", ")
            End If
        End If
        If Easp.IsN(tables) Then
            tables = ""
        End If
        parseTable = tables
    End Function

    '-- 表别名Alias分析
    'e.g. parseAlias("a.cid = b.cid")

    Private Function parseAlias(ByVal Str)
        If Not Easp.IsN(Str) Then
            Str = " " & Str
        Else
            Str = ""
        End If
        parseAlias = Str
    End Function

    '-- field分析

    Private Function parseField(ByVal field)
        Dim a, i
        If Not Easp.IsN(field) Then
            If IsArray(field) Then
                a = Array()
                For Each i In a
                    If Trim(i)<>"" Then
                        a = ComFun.Push(a, i)
                    End If
                Next
                field = Join(a, ",")
            End If
            If Trim(field) = "" Then
                field = "*"
            End If
        Else
            field = "*"
        End If
        parseField = field
    End Function

    '-- where分析

    Private Function parseWhere(ByVal where)
        Dim s_where, arr, Match, Matches, i, j, f, v, c, t, a
        If Not Easp.IsN(where) Then
            If IsArray(where) Then '数组
                arr = Array()
                For Each i in where
                    If Trim(i)<>"" Then
                        arr = ComFun.Push(arr, i)
                    End If
                Next
                s_where = "1=1"
                For Each j in arr
                    If Left(LCase(Trim(j)), 3) = "or " Or Left(LCase(Trim(j)), 4) = "and " Then
                        s_where = s_where & " " & j
                    Else
                        s_where = s_where & " And " & j
                    End If
                Next
            ElseIf ComFun.IsDictionary(where) Then
                arr = Array()
                For Each i In where
                    i = Trim(i)
                    f = ""
                    c = ""
                    v = ""
                    If InStr(i, ":")>0 Then
                        f = Easp.CLeft(i, ":")
                        c = "="
                    ElseIf InStr(i, ">=")>0 Then
                        f = Easp.CLeft(i, ">=")
                        c = ">="
                    ElseIf InStr(i, "<=")>0 Then
                        f = Easp.CLeft(i, "<=")
                        c = "<="
                    ElseIf InStr(i, "<>")>0 Then
                        f = Easp.CLeft(i, "<>")
                        c = "<>"
                    ElseIf InStr(i, "!=")>0 Then
                        f = Easp.CLeft(i, "!=")
                        c = "<>"
                    ElseIf InStr(i, "=")>0 Then
                        f = Easp.CLeft(i, "=")
                        c = "="
                    ElseIf InStr(i, ">")>0 Then
                        f = Easp.CLeft(i, ">")
                        c = ">"
                    ElseIf InStr(i, "<")>0 Then
                        f = Easp.CLeft(i, "<")
                        c = "<"
                    ElseIf InStr(i, "*")>0 Then '星号匹配like
                        f = Easp.CLeft(i, "*")
                        c = " LIKE "
                    ElseIf InStr(i, "[]")>0 Then '[]号匹配in
                        f = Easp.CLeft(i, "[]")
                        c = " IN "
                    ElseIf Easp.Test(Trim(i), "^\w+$") Then
                        f = Trim(i)
                        c = "="
                    Else
                        f = ComFun.RP(i, Array("=", ">", "<", "!"), Array("", "", "", ""))
                        c = "="
                    End If
                    v = where(i)
                    f = Trim(f)
                    ''自由语句,如 o_ds("")="id <> 6" 、o_ds("+1")="name like '%jack%'" 、 o_ds("+2")="email like '%@gmail.com%'"
                    If UCase(Trim(c)) = "IN" Or IsArray(v) Then
                        If IsArray(v) Then
                            a = Array()
                            For Each t In v
                                If ComFun.IsInt(t) Then
                                    a = ComFun.Push(a, t)
                                Else
                                    a = ComFun.Push(a, "'"& t & "'")
                                End If
                            Next
                            v = ComFun.Join(a, ",")
                        End If
                        If Trim(f)<>"" Then
                            arr = ComFun.Push(arr, f & " IN(" & v & ")")
                        End If
                    ElseIf (f = "" Or InStr(f, "+")>0) And Trim(v)<>"" Then
                        arr = ComFun.Push(arr, v)
                    ElseIf f = Me.getPk() And c = "=" Then '当为主键
                        If Trim(f)<>"" Then
                            arr = ComFun.Push(arr, f & " IN(" & v & ")")
                        End If
                    Else
                        If Not ComFun.IsNum(v) Then
                            If Easp.Test(v, "^#(.+?)#$") Then
                                t = Easp.RegReplace(v, "^#(.+?)#$", "$1")
                                If Not IsDate(t) Then
                                    v = "'" & v & "'"
                                End If
                            Else
                                v = "'" & v & "'"
                            End If
                        End If
                        If Trim(f)<>"" Then
                            arr = ComFun.Push(arr, f & c & v)
                        End If
                    End If
                Next
                s_where = "1=1"
                For Each j In arr
                    If Left(LCase(Trim(j)), 3) = "or " Or Left(LCase(Trim(j)), 4) = "and " Then
                        s_where = s_where & " " & j
                    Else
                        s_where = s_where & " And " & j
                    End If
                Next
            Else '字符串
                s_where = where
            End If
            If Trim(s_where)<>"" Then
                where = " Where "& s_where
            End If
        Else
            where = ""
        End If
        parseWhere = where
    End Function

    '-- top分析

    Private Function parseTop(ByVal top)
        If Trim(top)<>"" Then
            If InStr(LCase(top), "top ")>0 Then
                top = UCase(top)
            Else
                top = " Top " & top
            End If
        Else
            top = ""
        End If
        parseTop = top
    End Function

    '-- join分析
    'e.g. parseJoin("join __TEST__")

    Private Function parseJoin(ByVal Join)
        Dim joinStr
        joinStr = ""
        If Not Easp.IsN(Join) Then
            If IsArray(Join) Then
                For Each j in Join
                    If Easp.Test(LCase(j), "^\s*join\s+") Then
                        joinStr = joinStr & " " & Easp.RegReplace(j, "^\s*join\s+", "Join ")
                    ElseIf Easp.Test(LCase(j), "^\s*left\s+join\s+") Then
                        joinStr = joinStr & " " & Easp.RegReplace(j, "^\s*left\s+join\s+", "Left Join ")
                    ElseIf Easp.Test(LCase(j), "^\s*right\s+join\s+") Then
                        joinStr = joinStr & " " & Easp.RegReplace(j, "^\s*right\s+join\s+", "Right Join ")
                    Else
                        joinStr = joinStr & " Left Join " & j
                    End If
                Next
            Else
                If Easp.Test(LCase(Join), "^\s*join\s+") Then
                    joinStr = joinStr & " " & Easp.RegReplace(Join, "^\s*join\s+", "Join ")
                ElseIf Easp.Test(LCase(Join), "^\s*left\s+join\s+") Then
                    joinStr = joinStr & " " & Easp.RegReplace(Join, "^\s*left\s+join\s+", "Left Join ")
                ElseIf Easp.Test(LCase(Join), "^\s*right\s+join\s+") Then
                    joinStr = joinStr & " " & Easp.RegReplace(Join, "^\s*right\s+join\s+", "Right Join ")
                Else
                    joinStr = joinStr & " Left Join " & Join
                End If
            End If
        End If
        parseJoin = joinStr
    End Function

    '-- on分析
    'e.g. parseOn("a.cid = b.cid")

    Private Function parseOn(ByVal Str)
        If Not Easp.IsN(Str) Then
            Str = " On " & Str
        Else
            Str = ""
        End If
        parseOn = Str
    End Function

    '-- order分析
    'e.g. parseOrder("listnum desc, id asc")

    Private Function parseOrder(ByVal order)
        If IsArray(order) Then
            Dim arr
            arr = Array()
            For Each j in order
                arr = ComFun.Push(arr, j)
            Next
            order = ComFun.Join(arr, ", ")
        End If
        If Not Easp.IsN(order) Then
            order = " Order By " & order
        Else
            order = ""
        End If
        parseOrder = order
    End Function

    '-- group分析
    'e.g. parseGroup("name")

    Private Function parseGroup(ByVal group)
        Dim arr
        arr = Array()
        If IsArray(group) Then
            For Each j in group
                arr = ComFun.Push(arr, j)
            Next
            group = ComFun.Join(arr, ", ")
        End If
        If Not Easp.IsN(group) Then
            group = " Group By " & group
        Else
            group = ""
        End If
        parseGroup = group
    End Function

    '-- having分析
    'e.g. parseHaving("name")

    Private Function parseHaving(ByVal having)
        If Not Easp.IsN(having) Then
            having = " Having " & having
        Else
            having = ""
        End If
        parseHaving = having
    End Function

    '-- union分析

    Private Function parseUnion(ByVal union)
        If Not Easp.IsN(union) Then
            If Easp.Test(LCase(union), "^([ |]?)union\s+") Then
                union = " " & union
            Else
                union = " Union " & union
            End If
        Else
            union = ""
        End If
        parseUnion = union
    End Function

End Class

Class Cls_ComFunc

    Private o_Json

    Private Sub Class_Initialize()
        Set o_Json = New Cls_Json
    End Sub

    Private Sub Class_Terminate()
        Set o_Json = Nothing
    End Sub

    Public Function IsInt(Byval s)
        Dim stype
        stype = LCase(TypeName(s))
        If stype = "integer" Or stype = "long" Or stype = "double" Or stype = "single" Then
            IsInt = True
        Else
            IsInt = False
        End If
    End Function

    Public Function IsNum(Byval s)
        On Error Resume Next
        IsNum = Easp.Test(s, "^\d+$")
        On Error GoTo 0
    End Function

    Public Function IsString(Byval s)
        IsString = False
        If Not IsObject(s) And VarType(s) = vbString Then
            IsString = True
        End If
    End Function

    Public Function IsDictionary(Byval o)
        IsDictionary = False
        If IsObject(o) And TypeName(o) = "Dictionary" Then
            IsDictionary = True
        End If
    End Function

    Public Function IsRecordset(Byval o)
        IsRecordset = False
        If IsObject(o) And TypeName(o) = "Recordset" Then
            IsRecordset = True
        End If
    End Function

    Public Function RP(Byval Str, Byval FindStr, Byval RepStr)
        On Error Resume Next
        If Easp.IsN(Str) Then
            RP = ""
            Exit Function
        End If
        If IsArray(FindStr) Then
            Dim i, x, y, m
            i = 0
            m = UBound(FindStr)
            If Err Then
                Err.Clear
                RP = Str
                Exit Function
            End If
            If m > 0 Then
                If IsArray(RepStr) Then
                    Dim n
                    n = UBound(RepStr)
                    If Err Then
                        Err.Clear
                        RP = Str
                        Exit Function
                    End If
                    If m >= n Then
                        For i = 0 To n
                            Str = Replace(Str, FindStr(i), RepStr(i))
                        Next
                        For i = n + 1 To m
                            Str = Replace(Str, FindStr(i), "")
                        Next
                    Else
                        For i = 0 To m
                            Str = Replace(Str, FindStr(i), RepStr(i))
                        Next
                    End If
                Else
                    For i = 0 To m
                        Str = Replace(Str, FindStr(i), RepStr)
                    Next
                End If
                RP = Str
            End If
        Else
            If IsArray(RepStr) Then
                RP = Str
                Exit Function
            End If
            RP = Replace(Str, FindStr, RepStr)
        End If
        If Err Then
            Err.Clear
            RP = Str
        End If
        On Error GoTo 0
    End Function

    Public Function [Join](Byval p, Byval s)
        [Join] = o_Json.GetJoin(p, s)
    End Function

    Public Function Push(ByVal arr, ByVal s)
        Dim i, a
        a = Me.Clone(arr)
        If Me.Len(arr)<= 0 Then

            ReDim Preserve a(0)
            If IsObject(s) Then Set a(0) = s Else a(0) = s
            Push = a
            Exit Function
        End If
        ReDim Preserve a(UBound(arr) + 1)
        If IsObject(s) Then Set a(UBound(arr) + 1) = s Else a(UBound(arr) + 1) = s
        Push = a
    End Function

    Public Function Clone(ByVal arr)
        Dim i, a()
        If Me.Len(arr)<= 0 Then
            Clone = arr
            Exit Function
        End If
        For i = LBound(arr) To UBound(arr)
            ReDim Preserve a(i)
            If IsObject(arr(i)) Then Set a(i) = arr(i) Else a(i) = arr(i)
        Next
        Clone = a
    End Function

    Public Function [Len](Byval arr)
        On Error Resume Next
        If Not IsArray(arr) Then
            [Len] = -1
        End If
        Dim temp
        temp = UBound(arr)
        If Err Or temp<0 Then
            [Len] = 0
            Err.Clear
            Exit Function
        End If
        Dim i, iCount
        iCount = 0
        For i = LBound(arr) To UBound(arr)
            iCount = iCount + 1
        Next
        [Len] = iCount
        On Error GoTo 0
    End Function

    Public Function InArray(Byval s, Byval arr)
        If Not IsArray(arr) Then
            InArray = False
            Exit Function
        End If
        Dim x
        InArray = False
        For Each x In arr
            If Not IsObject(x) And Not IsObject(s) Then
                If x = s Then
                    InArray = True
                    Exit For
                End If
            ElseIf IsObject(x) And IsObject(s) Then
                If TypeName(x) = TypeName(s) And VarType(x) = VarType(s) Then
                    InArray = True
                    Exit For
                End If
            End If
        Next
    End Function

End Class

Class Cls_Json

    Public Function GetJoin(Byval p, Byval s)
        GetJoin = Join(p, s)
    End Function

End Class
%>