<%
'#################################################################################
'## easp.vbsjson.asp
'## ------------------------------------------------------------------------------
'## Feature     : EasyAsp Variable VbsJson Plugin
'## Author      : Demon, Edit by Sky
'## Update Date : 2014/1/28

'Example.vbs
'
'    Dim fso, json, str, o, i
'    Set json = Easp.Ext("VbsJson")
'    str = Easp.Fso.Read("json.txt")
'    Set o = json.Decode(str)
'    Easp.W o("Image")("Width")
'    Easp.W o("Image")("Height")
'    Easp.W o("Image")("Title")
'    Easp.W o("Image")("Thumbnail")("Url")
'    For Each i In o("Image")("IDs")
'        Easp.W i
'    Next
'
'json.txt
'
'    {
'       "Image": {
'           "Width":  800,
'           "Height": 600,
'           "Title":  "View from 15th Floor",
'           "Thumbnail": {
'               "Url":    "http://www.example.com/image/481989943",
'               "Height": 125,
'               "Width":  "100"
'           },
'           "IDs": [116, 943, 234, 38793]
'         }
'    }

Class EasyAsp_VbsJson
    Private Whitespace, NumberRegex, StringChunk
    Private b, f, r, n, t

    Private Sub Class_Initialize
        Whitespace = " " & vbTab & vbCr & vbLf
        b = ChrW(8)
        f = vbFormFeed
        r = vbCr
        n = vbLf
        t = vbTab

        Set NumberRegex = New RegExp
        NumberRegex.Pattern = "(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?"
        NumberRegex.Global = False
        NumberRegex.MultiLine = True
        NumberRegex.IgnoreCase = True

        Set StringChunk = New RegExp
        StringChunk.Pattern = "([\s\S]*?)([""\\\x00-\x1f])"
        StringChunk.Global = False
        StringChunk.MultiLine = True
        StringChunk.IgnoreCase = True
    End Sub

    'Return a JSON string representation of a VBScript data structure
    'Supports the following objects and types
    '+-------------------+---------------+
    '| VBScript          | JSON          |
    '+===================+===============+
    '| Dictionary        | object        |
    '+-------------------+---------------+
    '| Array             | array         |
    '+-------------------+---------------+
    '| String            | string        |
    '+-------------------+---------------+
    '| Number            | number        |
    '+-------------------+---------------+
    '| True              | true          |
    '+-------------------+---------------+
    '| False             | false         |
    '+-------------------+---------------+
    '| Null              | null          |
    '+-------------------+---------------+

    Public Function Encode(ByRef obj)
        Dim buf, i, c, g
        Set buf = CreateObject("Scripting.Dictionary")
        Select Case VarType(obj)
            Case vbNull
                buf.Add buf.Count, "null"
            Case vbBoolean
                If obj Then
                    buf.Add buf.Count, "true"
                Else
                    buf.Add buf.Count, "false"
                End If
            Case vbInteger, vbLong, vbSingle, vbDouble
                buf.Add buf.Count, obj
            Case vbString
                buf.Add buf.Count, """"
                For i = 1 To Len(obj)
                    c = Mid(obj, i, 1)
                    Select Case c
                    Case """" buf.Add buf.Count, "\"""
                    Case "\" buf.Add buf.Count, "\\"
                    Case "/" buf.Add buf.Count, "/"
                    Case b buf.Add buf.Count, "\b"
                    Case f buf.Add buf.Count, "\f"
                    Case r buf.Add buf.Count, "\r"
                    Case n buf.Add buf.Count, "\n"
                    Case t buf.Add buf.Count, "\t"
                    Case Else
                        If AscW(c) >= 0 And AscW(c) <= 31 Then
                            c = Right("0" & Hex(AscW(c)), 2)
                            buf.Add buf.Count, "\u00" & c
                        Else
                            buf.Add buf.Count, c
                        End If
                End Select
            Next
            buf.Add buf.Count, """"
            Case vbArray + vbVariant
                g = True
                buf.Add buf.Count, "["
                For Each i In obj
                    If g Then g = False Else buf.Add buf.Count, ","
                    buf.Add buf.Count, Encode(i)
                Next
                buf.Add buf.Count, "]"
            Case vbObject
                If TypeName(obj) = "Dictionary" Then
                    g = True
                    buf.Add buf.Count, "{"
                    For Each i In obj
                        If g Then g = False Else buf.Add buf.Count, ","
                        buf.Add buf.Count, """" & i & """" & ":" & Encode(obj(i))
                    Next
                    buf.Add buf.Count, "}"
                Else
                    Err.Raise 8732, , "None dictionary object"
                End If
            Case Else
                buf.Add buf.Count, """" & CStr(obj) & """"
        End Select
        Encode = Join(buf.Items, "")
    End Function

    'Return the VBScript representation of ``str(``
    'Performs the following translations in decoding
    '+---------------+-------------------+
    '| JSON          | VBScript          |
    '+===============+===================+
    '| object        | Dictionary        |
    '+---------------+-------------------+
    '| array         | Array             |
    '+---------------+-------------------+
    '| string        | String            |
    '+---------------+-------------------+
    '| number        | Double            |
    '+---------------+-------------------+
    '| true          | True              |
    '+---------------+-------------------+
    '| false         | False             |
    '+---------------+-------------------+
    '| null          | Null              |
    '+---------------+-------------------+

    Public Function Decode(ByRef Str)
        Dim idx
        idx = SkipWhitespace(Str, 1)
        If Mid(Str, idx, 1) = "{" Then
            Set Decode = ScanOnce(Str, 1)
        Else
            Decode = ScanOnce(Str, 1)
        End If
    End Function

    Private Function ScanOnce(ByRef Str, ByRef idx)
        Dim c, ms
        idx = SkipWhitespace(Str, idx)
        c = Mid(Str, idx, 1)
        If c = "{" Then
            idx = idx + 1
            Set ScanOnce = ParseObject(Str, idx)
            Exit Function
        ElseIf c = "[" Then
            idx = idx + 1
            ScanOnce = ParseArray(Str, idx)
            Exit Function
        ElseIf c = """" Then
            idx = idx + 1
            ScanOnce = ParseString(Str, idx)
            Exit Function
        ElseIf c = "n" And StrComp("null", Mid(Str, idx, 4)) = 0 Then
            idx = idx + 4
            ScanOnce = Null
            Exit Function
        ElseIf c = "t" And StrComp("true", Mid(Str, idx, 4)) = 0 Then
            idx = idx + 4
            ScanOnce = True
            Exit Function
        ElseIf c = "f" And StrComp("false", Mid(Str, idx, 5)) = 0 Then
            idx = idx + 5
            ScanOnce = False
            Exit Function
        End If
        Set ms = NumberRegex.Execute(Mid(Str, idx))
        If ms.Count = 1 Then
            idx = idx + ms(0).Length
            ScanOnce = CDbl(ms(0))
            Exit Function
        End If
        Err.Raise 8732, , "No JSON object could be ScanOnced"
    End Function

    Private Function ParseObject(ByRef Str, ByRef idx)
        Dim c, Key, Value
        Set ParseObject = CreateObject("Scripting.Dictionary")
        idx = SkipWhitespace(Str, idx)
        c = Mid(Str, idx, 1)
        If c = "}" Then
            Exit Function
        ElseIf c <> """" Then
            Err.Raise 8732, , "Expecting property name"
        End If
        idx = idx + 1
        Do
            Key = ParseString(Str, idx)
            idx = SkipWhitespace(Str, idx)
            If Mid(Str, idx, 1) <> ":" Then
                Err.Raise 8732, , "Expecting : delimiter"
            End If
            idx = SkipWhitespace(Str, idx + 1)
            If Mid(Str, idx, 1) = "{" Then
                Set Value = ScanOnce(Str, idx)
            Else
                Value = ScanOnce(Str, idx)
            End If
            ParseObject.Add Key, Value
            idx = SkipWhitespace(Str, idx)
            c = Mid(Str, idx, 1)
            If c = "}" Then
                Exit Do
            ElseIf c <> "," Then
                Err.Raise 8732, , "Expecting , delimiter"
            End If
            idx = SkipWhitespace(Str, idx + 1)
            c = Mid(Str, idx, 1)
            If c <> """" Then
                Err.Raise 8732, , "Expecting property name"
            End If
            idx = idx + 1
        Loop
        idx = idx + 1
    End Function

    Private Function ParseArray(ByRef Str, ByRef idx)
        Dim c, values, Value
        Set values = CreateObject("Scripting.Dictionary")
        idx = SkipWhitespace(Str, idx)
        c = Mid(Str, idx, 1)
        If c = "]" Then
            ParseArray = values.Items
            Exit Function
        End If
        Do
            idx = SkipWhitespace(Str, idx)
            If Mid(Str, idx, 1) = "{" Then
                Set Value = ScanOnce(Str, idx)
            Else
                Value = ScanOnce(Str, idx)
            End If
            values.Add values.Count, Value
            idx = SkipWhitespace(Str, idx)
            c = Mid(Str, idx, 1)
            If c = "]" Then
                Exit Do
            ElseIf c <> "," Then
                Err.Raise 8732, , "Expecting , delimiter"
            End If
            idx = idx + 1
        Loop
        idx = idx + 1
        ParseArray = values.Items
    End Function

    Private Function ParseString(ByRef Str, ByRef idx)
        Dim chunks, content, terminator, ms, esc, char
        Set chunks = CreateObject("Scripting.Dictionary")
        Do
            Set ms = StringChunk.Execute(Mid(Str, idx))
            If ms.Count = 0 Then
                Err.Raise 8732, , "Unterminated string starting"
            End If
            content = ms(0).Submatches(0)
            terminator = ms(0).Submatches(1)
            If Len(content) > 0 Then
                chunks.Add chunks.Count, content
            End If
            idx = idx + ms(0).Length
            If terminator = """" Then
                Exit Do
            ElseIf terminator <> "\" Then
                Err.Raise 8732, , "Invalid control character"
            End If
            esc = Mid(Str, idx, 1)
            If esc <> "u" Then
                Select Case esc
                    Case """" char = """"
                    Case "\" char = "\"
                    Case "/" char = "/"
                    Case "b" char = b
                    Case "f" char = f
                    Case "n" char = n
                    Case "r" char = r
                    Case "t" char = t
                    Case Else Err.Raise 8732, , "Invalid escape"
                End Select
                idx = idx + 1
            Else
                char = ChrW("&H" & Mid(Str, idx + 1, 4))
                idx = idx + 5
            End If
            chunks.Add chunks.Count, char
        Loop
        ParseString = Join(chunks.Items, "")
    End Function

    Private Function SkipWhitespace(ByRef Str, ByVal idx)
        Do While idx <= Len(Str) And _
                        InStr(Whitespace, Mid(Str, idx, 1)) > 0
            idx = idx + 1
        Loop
        SkipWhitespace = idx
    End Function

End Class
%>