Initial commit

This commit is contained in:
g0nzo83
2024-02-11 07:47:20 +01:00
commit 01190f5a9a
12 changed files with 3563 additions and 0 deletions

73
Lib/DefaultJSON.ahk Normal file
View File

@@ -0,0 +1,73 @@
default_JSON := "
(
{
"global_Settings": {
"Suspend_Hotkeys_Hotkey": "",
"Global_Hotkeys": 1,
"LastUsedProfile": "Default",
"Minimize_Delay": 100,
"ThumbnailStartLocation": {
"x": 20,
"y": 20,
"width": 250,
"height": 140
},
"ThumbnailBackgroundColor":"#57504E",
"ThumbnailSnap": true,
"ThumbnailSnap_Distance": 20,
"ThumbnailMinimumSize": {
"width": 50,
"height": 50
}
},
"_Profiles": {
"Default": {
"Client Settings": {
"MinimizeInactiveClients": false,
"AlwaysMaximize": false,
"TrackClientPossitions": false,
"Dont_Minimize_Clients": [
"Example Name1",
"Example Name2",
"Example Name3"
]
},
"Thumbnail Settings": {
"ShowThumbnailTextOverlay": true,
"ThumbnailTextColor":"#FAC57A",
"ThumbnailTextSize": 12,
"ThumbnailTextFont": "Gill Sans MT",
"ThumbnailTextMargins": {
"x": 15,
"y": 5
},
"ShowClientHighlightBorder": true,
"ClientHighligtColor": "#E36A0D",
"ClientHighligtBorderthickness": 4,
"HideThumbnailsOnLostFocus": false,
"ThumbnailOpacity": 80,
"ShowThumbnailsAlwaysOnTop": true,
"ShowAllColoredBorders":0
},
"Hotkeys":[
{"Example Name1":"1"},
{"Example Name2":"ctrl & 1"},
{"Example Name3":"Xbutton1 & 1"},
{"Example Name4":"^XButton1 & 1"}
],
"Thumbnail Positions": {},
"Client Possitions": {},
"Thumbnail Visibility":{},
"Hotkey Groups":{},
"Custom Colors":{
"cColorActive": "0",
"cColors": {
"CharNames": ["Example Char"],
"TextColor": ["FFFFFF"],
"Bordercolor":["FFFFFF"]
}
}
}
}
}
)"

330
Lib/LiveThumb.ahk Normal file
View File

@@ -0,0 +1,330 @@
; ----------------------------------------------------------------------------------------------------------------------
; Name .........: LiveThumb class library
; Description ..: Windows live thumbnails implementation (requires AERO to be enabled).
; AHK Version ..: AHK v2 2.0.3
; Author .......: cyruz - http://ciroprincipe.info
; v2 Convertion : Gonz0
; Forum .......: Ahk V1: https://www.autohotkey.com/boards/viewtopic.php?t=66585
; Thanks .......: maul-esel - https://github.com/maul-esel/AeroThumbnail
; License ......: WTFPL - http://www.wtfpl.net/txt/copying/
; Changelog ....: Feb. 21, 2019 - v0.1.0 - First version.
; ..............: Mar. 04, 2019 - v0.1.1 - Added active properties management and propertes getters.
; ..............: Jul. 28, 2019 - v0.1.2 - Added "Discard" method. Fixed LoadLibrary return type and a wrong behavior
; ..............: when returning false from the constructor (thanks Helgef).
; ..............: Sep. 21, 2019 - v0.1.3 - Fixed potential issue with HRESULT return code. Used internal memory
; ..............: management instead of LocalAlloc.
; ..............: Sep. 21, 2019 - v0.1.4 - Fixed Object.SetCapacity not zero-filling allocated memory.
; ..............: Nov. 15, 2022 - v0.1.5 - Fixed DWM_THUMB_PROPERTIES structure offsets (thanks swagfag).
; ..............: Jun. 26, 2023 - v0.1.5 - Conerverted to Autohotkey version 2
; Remarks ......: The class registers a thumbnail and waits for properties update. When all the desired properties have
; ..............: been updated, the "Update()" method should be called to submit the properties. Getting a property
; ..............: when an update has not been performed before, will result in the string "NOT UPDATED" to be returned.
; ..............: Due to some internal unknown reason, the instantiated object must be in the global namespace to work.
; Info .........: Implements the following functions and structures from Win32 API.
; ..............: "DwmRegisterThumbnail" Win32 function:
; ..............: https://docs.microsoft.com/en-us/windows/desktop/api/dwmapi/nf-dwmapi-dwmregisterthumbnail
; ..............: "SIZE" Win32 structure:
; ..............: https://docs.microsoft.com/en-us/previous-versions//dd145106(v=vs.85)
; ..............: "DwmQueryThumbnailSourceSize" Win32 function:
; ..............: https://docs.microsoft.com/en-us/windows/desktop/api/dwmapi/nf-dwmapi-dwmquerythumbnailsourcesize
; ..............: "DWM_THUMB_PROPERTIES" Win32 structure:
; ..............: https://docs.microsoft.com/en-us/windows/desktop/api/dwmapi/ns-dwmapi-_dwm_thumbnail_properties
; ..............: "DWM_TNP" Constants for the dwFlags field:
; ..............: https://docs.microsoft.com/en-us/windows/desktop/dwm/dwm-tnp-constants
; ..............: "DwmUpdateThumbnailProperties" Win32 function:
; ..............: https://docs.microsoft.com/en-us/windows/desktop/api/dwmapi/nf-dwmapi-dwmupdatethumbnailproperties
; ..............: "DwmUnregisterThumbnail" Win32 function:
; ..............: https://docs.microsoft.com/en-us/windows/desktop/api/dwmapi/nf-dwmapi-dwmunregisterthumbnail
; ----------------------------------------------------------------------------------------------------------------------
; * How to use:
; -------------
; Instantiate the object:
; thumb := LiveThumb(HWND Source Window, HWND Destination Window)
; Modify its properties:
; thumb.Source := [x, y, w, h] ;Size of the Source Window Thumbnail. Should be the same Size of the Source Window or it shows free space
; thumb.Destination := [x, y, w, h] ;Size of the Destination Window Thumbnail. Shrinks or streches the Thumbnail to the given size. To show the full area its should be the same size like the Destination Window
; thumb.Visible := True ;True or False Set the Thumbnail Visible
; hLT.SourceClientAreaOnly := True ;Optional: True or False Set Show Client Area Only.
; hLT.Opacity := 255 ;Optional: value between 0 and 255
; Update the properties:
; thumb.Update() ;Updates the given Propertys. This should be called everytime if some value is changed.
; Get a property:
; aProp := thumb.Visible ; Shows 1 or 0
; * Example:
; ----------
; #SingleInstance Force
; #Include <LiveThumb>
; Run("notepad", , , &PID := 0)
; WinWait("ahk_pid " PID)
; hwndNotepad := WinExist("ahk_pid " PID)
; WinGetPos( &X, &Y, &W, &H, "ahk_pid " PID)
; Mygui := Gui()
; hLT := LiveThumb(hwndNotepad, Mygui.Hwnd)
; hLT.Source := [0, 0, W, H]
; hLT.Destination := [0, 0, W, H]
; hLT.SourceClientAreaOnly := False
; hLT.Visible := True
; hLT.Opacity := 255
; hLT.Update()
; Mygui.Show("w" W "h" H)
; Name .........: LiveThumb - PUBLIC CLASS
; Description ..: Manages thumbnails creation and destruction.
Class LiveThumb
{
Static Dll_Module := 0,
OBJ_COUNTER := 0,
DTP_SIZE := 48,
DTP_DWFLAGS := { Destination: 0x0001,
Source: 0x0002,
Opacity: 0x0004,
Visible: 0x0008,
SourceClientAreaOnly: 0x0010 },
DTP_OFFSETS := { dwFlags: 0,
Destination: 4,
Source: 20,
Opacity: 36,
Visible: 37,
SourceClientAreaOnly: 41 }
; Name .........: __New - PRIVATE CONSTRUCTOR
; Description ..: Initialize the object, registering a new thumbnail and allocating the memory.
; Parameters ...: hSource = Handle to the window to be previewed.
; ..............: hDest = Handle to the window containing the live preview.
; Return .......: LiveThumb object on success - False on error.
__New( hSource, hDest )
{
; Load the library on first run.
If ( !LiveThumb.DLL_MODULE )
LiveThumb.DLL_MODULE := DllCall("LoadLibrary", "Str","dwmapi.dll", "Ptr")
LiveThumb.OBJ_COUNTER += 1
; Register a thumbnail to get an ID.
If ( DllCall( "dwmapi.dll\DwmRegisterThumbnail"
, "Ptr", hDest
, "Ptr", hSource
, "Ptr*", &phThumb := 0
, "UPtr" ) )
Return False
this.THUMB_ID := phThumb
this.THUMB_UPDATED := False
this.THUMB_PENDING_UPDATE := True
; We define 2 portions of memory used for properties update and active properties tracking.
this.THUMB_UPD_PROP_PTR := Buffer(LiveThumb.DTP_SIZE,0)
this.THUMB_ACT_PROP_PTR := Buffer(LiveThumb.DTP_SIZE,0)
; Object.SetCapacity doesn't zero-fill the allocated memory so we call the Discard method.
this.Discard( )
Return this
}
; Name .........: __Delete - PRIVATE DESTRUCTOR
; Description ..: Unregister the thumbnail and deallocate memory.
__Delete( )
{
; Unregister thumbnail ID.
If ( this.THUMB_ID )
DllCall( "dwmapi.dll\DwmUnregisterThumbnail"
, "Ptr", this.THUMB_ID )
; If it's last instantiated object, free the library.
If ( LiveThumb.DLL_MODULE && !(LiveThumb.OBJ_COUNTER -= 1) )
{
DllCall("FreeLibrary", "Ptr",LiveThumb.DLL_MODULE)
LiveThumb.DLL_MODULE := 0
}
}
__Get(aName, Params) {
If ( LiveThumb.HasOwnProp(aName))
return LiveThumb.%aName%
}
; Name .........: QuerySourceSize - PUBLIC METHOD
; Description ..: Return the size (width/height) of the source thumbnail.
; Return .......: Array with width and height values - False on error.
QuerySourceSize( )
{
SIZE := Buffer(8,0)
If ( DllCall( "dwmapi.dll\DwmQueryThumbnailSourceSize"
, "Ptr", this.THUMB_ID
, "Ptr", SIZE
, "UPtr" ) ) {
SIZE := Buffer(0)
Return False
}
SourceSize := [NumGet(SIZE, 0, "Int"), NumGet(SIZE, 4, "Int")]
SIZE := Buffer(0)
Return SourceSize
}
; Name .........: Update - PUBLIC METHOD
; Description ..: Update thumbnail properties and zero fill the dwFlags memory to be ready for next update.
; Return .......: True on success - False on error.
Update( )
{
; If no update is pending, return false.
; If ( !this.THUMB_PENDING_UPDATE )
; Return False
; Update properties.
If ( DllCall( "dwmapi.dll\DwmUpdateThumbnailProperties"
, "Ptr", this.THUMB_ID
, "Ptr", this.THUMB_UPD_PROP_PTR
, "UPtr" ) )
Return False
; Flag as updated and copy memory so that we can use this portion to track active properties with getters.
this.THUMB_UPDATED := True
DllCall( "NtDll.dll\RtlCopyMemory"
, "Ptr", this.THUMB_ACT_PROP_PTR
, "Ptr", this.THUMB_UPD_PROP_PTR
, "UInt", LiveThumb.DTP_SIZE )
; Use the "Discard" method to reset dwFlags and return.
this.Discard( )
Return True
}
; Name .........: Discard - PUBLIC METHOD
; Description ..: Discard set properties, resetting dwFlags memory.
Discard( )
{
; Zero-fill dwFlags memory in the properties update memory portion.
NumPut("UInt", 0, this.THUMB_UPD_PROP_PTR, LiveThumb.DTP_OFFSETS.dwFlags)
this.THUMB_PENDING_UPDATE := False
}
; Name .........: Destination - PUBLIC PROPERTY
; Description ..: The area in the destination window where the thumbnail will be rendered.
; Value ........: Array with 4 client related coordinates [ left, top, right, bottom ].
; Remarks ......: "Destination" property is of "RECT" type (16 bytes structure) and starts from offset 4.
Destination {
Get {
If (!this.THUMB_UPDATED)
Return "NOT UPDATED"
arrRet := []
Loop 4
arrRet.Push(NumGet(this.THUMB_ACT_PROP_PTR, LiveThumb.DTP_OFFSETS.Destination * A_Index, "Int"))
Return arrRet
}
Set {
This.SetDwFlags("Destination")
Loop 4
NumPut("Int", value[A_Index], this.THUMB_UPD_PROP_PTR, LiveThumb.DTP_OFFSETS.Destination * A_Index)
}
}
; Name .........: Source - PUBLIC PROPERTY
; Description ..: The region of the source window to use as the thumbnail. Default is the entire window.
; Value ........: Array with 4 client related coordinates [ left, top, right, bottom ].
; Remarks ......: "Source" property is of "RECT" type (16 bytes structure) and starts from offset 20.
Source {
Get {
If (!this.THUMB_UPDATED)
Return "NOT UPDATED"
arrRet := []
Loop 4
arrRet.Push(NumGet(this.THUMB_ACT_PROP_PTR, LiveThumb.DTP_OFFSETS.Source + 4*(A_Index-1), "Int"))
Return arrRet
}
Set {
This.SetDwFlags("Source")
Loop 4
NumPut("Int", value[A_Index], this.THUMB_UPD_PROP_PTR, LiveThumb.DTP_OFFSETS.Source + 4*(A_Index-1))
}
}
; Name .........: Opacity - PUBLIC PROPERTY
; Description ..: The opacity with which to render the thumbnail. 0: transparent - 255: opaque. Default is 255.
; Value ........: Integer value from 0 to 255.
; Remarks ......: "Opacity" property is of "BYTE" type (1 byte + 3 padding) and starts from offset 36.
Opacity {
Get {
If (!this.THUMB_UPDATED)
Return "NOT UPDATED"
Return NumGet(this.THUMB_ACT_PROP_PTR, LiveThumb.DTP_OFFSETS.Opacity, "UChar")
}
Set {
This.SetDwFlags("Opacity")
NumPut("UChar", value, this.THUMB_UPD_PROP_PTR, LiveThumb.DTP_OFFSETS.Opacity)
}
}
; Name .........: Visible - PUBLIC PROPERTY
; Description ..: True to make the thumbnail visible, otherwise False. Default is False.
; Value ........: Boolean True/False or integer 1/0 value.
; Remarks ......: "Visible" property is of "BOOL" type (4 bytes) and starts from offset 40.
Visible {
Get {
If (!this.THUMB_UPDATED)
Return "NOT UPDATED"
Return NumGet(this.THUMB_ACT_PROP_PTR, LiveThumb.DTP_OFFSETS.Visible, "Int")
}
Set {
This.SetDwFlags("Visible")
NumPut("Int", value, this.THUMB_UPD_PROP_PTR, LiveThumb.DTP_OFFSETS.Visible)
}
}
; Name .........: SourceClientAreaOnly - PUBLIC PROPERTY
; Description ..: True to use only the thumbnail source's client area, otherwise False. Default is False.
; Value ........: Boolean True/False or integer 1/0 value.
; Remarks ......: "SourceClientAreaOnly" property is of "BOOL" type (4 bytes) and starts from offset 44.
SourceClientAreaOnly {
Get {
If (!this.THUMB_UPDATED)
Return "NOT UPDATED"
Return NumGet(this.THUMB_ACT_PROP_PTR, LiveThumb.DTP_OFFSETS.SourceClientAreaOnly, "Int")
}
Set {
This.SetDwFlags("SourceClientAreaOnly")
NumPut("Uint", value, this.THUMB_UPD_PROP_PTR, LiveThumb.DTP_OFFSETS.SourceClientAreaOnly)
}
}
;Writes the DWFlags from the given Property into Memory.
SetDwFlags(aName)
{
If (LiveThumb.DTP_DWFLAGS.HasOwnProp(aName)) {
NumPut( "UInt", NumGet( this.THUMB_UPD_PROP_PTR,
LiveThumb.DTP_OFFSETS.dwFlags, "UInt" ) | LiveThumb.DTP_DWFLAGS.%aName%,
this.THUMB_UPD_PROP_PTR,
LiveThumb.DTP_OFFSETS.dwFlags)
this.THUMB_PENDING_UPDATE := true
}
}
}

158
Lib/json.ahk Normal file
View File

@@ -0,0 +1,158 @@
/************************************************************************
* @Mdodified by G0nzo
* @description: JSON格式字符串序列化和反序列化, [HotKeyIt/Yaml](https://github.com/HotKeyIt/Yaml)
* true/false/null类型的支持,
* @author thqby, HotKeyIt
* @date 2023/05/12
* @version 1.0.5
***********************************************************************/
class JSON {
static null := ComValue(1, 0), true := ComValue(0xB, 1), false := ComValue(0xB, 0)
/**
* Converts a AutoHotkey Object Notation JSON string into an object.
* @param text A valid JSON string.
* @param keepbooltype convert true/false/null to JSON.true / JSON.false / JSON.null where it's true, otherwise 1 / 0 / ''
* @param as_map object literals are converted to map, otherwise to object
*/
static Load(text, keepbooltype := false, as_map := true) {
keepbooltype ? (_true := JSON.true, _false := JSON.false, _null := JSON.null) : (_true := true, _false := false, _null := "")
as_map ? (map_set := (maptype := Map).Prototype.Set) : (map_set := (obj, key, val) => obj.%key% := val, maptype := Object)
NQ := "", LF := "", LP := 0, P := "", R := ""
D := [C := (A := InStr(text := LTrim(text, " `t`r`n"), "[") = 1) ? [] : maptype()], text := LTrim(SubStr(text, 2), " `t`r`n"), L := 1, N := 0, V := K := "", J := C, !(Q := InStr(text, '"') != 1) ? text := LTrim(text, '"') : ""
Loop Parse text, '"' {
Q := NQ ? 1 : !Q
NQ := Q && (SubStr(A_LoopField, -3) = "\\\" || (SubStr(A_LoopField, -1) = "\" && SubStr(A_LoopField, -2) != "\\"))
if !Q {
if (t := Trim(A_LoopField, " `t`r`n")) = "," || (t = ":" && V := 1)
continue
else if t && (InStr("{[]},:", SubStr(t, 1, 1)) || RegExMatch(t, "^-?\d*(\.\d*)?\s*[,\]\}]")) {
Loop Parse t {
if N && N--
continue
if InStr("`n`r `t", A_LoopField)
continue
else if InStr("{[", A_LoopField) {
if !A && !V
throw Error("Malformed JSON - missing key.", 0, t)
C := A_LoopField = "[" ? [] : maptype(), A ? D[L].Push(C) : map_set(D[L], K, C), D.Has(++L) ? D[L] := C : D.Push(C), V := "", A := Type(C) = "Array"
continue
} else if InStr("]}", A_LoopField) {
if !A && V
throw Error("Malformed JSON - missing value.", 0, t)
else if L = 0
throw Error("Malformed JSON - to many closing brackets.", 0, t)
else C := --L = 0 ? "" : D[L], A := Type(C) = "Array"
} else if !(InStr(" `t`r,", A_LoopField) || (A_LoopField = ":" && V := 1)) {
if RegExMatch(SubStr(t, A_Index), "m)^(null|false|true|-?\d+\.?\d*)\s*[,}\]\r\n]", &R) && (N := R.Len(0) - 2, R := R.1, 1) {
if A
C.Push(R = "null" ? _null : R = "true" ? _true : R = "false" ? _false : IsNumber(R) ? R + 0 : R)
else if V
map_set(C, K, R = "null" ? _null : R = "true" ? _true : R = "false" ? _false : IsNumber(R) ? R + 0 : R), K := V := ""
else throw Error("Malformed JSON - missing key.", 0, t)
} else {
; Added support for comments without '"'
if A_LoopField == '/' {
nt := SubStr(t, A_Index + 1, 1), N := 0
if nt == '/' {
if nt := InStr(t, '`n', , A_Index + 2)
N := nt - A_Index - 1
} else if nt == '*' {
if nt := InStr(t, '*/', , A_Index + 2)
N := nt + 1 - A_Index
} else nt := 0
if N
continue
}
throw Error("Malformed JSON - unrecognized character-", 0, A_LoopField " in " t)
}
}
}
} else if InStr(t, ':') > 1
throw Error("Malformed JSON - unrecognized character-", 0, SubStr(t, 1, 1) " in " t)
} else if NQ && (P .= A_LoopField '"', 1)
continue
else if A
LF := P A_LoopField, C.Push(InStr(LF, "\") ? UC(LF) : LF), P := ""
else if V
LF := P A_LoopField, map_set(C, K, InStr(LF, "\") ? UC(LF) : LF), K := V := P := ""
else
LF := P A_LoopField, K := InStr(LF, "\") ? UC(LF) : LF, P := ""
}
return J
UC(S, e := 1) {
static m := Map(Ord('"'), '"', Ord("a"), "`a", Ord("b"), "`b", Ord("t"), "`t", Ord("n"), "`n", Ord("v"), "`v", Ord("f"), "`f", Ord("r"), "`r")
local v := ""
Loop Parse S, "\"
if !((e := !e) && A_LoopField = "" ? v .= "\" : !e ? (v .= A_LoopField, 1) : 0)
v .= (t := InStr("ux", SubStr(A_LoopField, 1, 1)) ? SubStr(A_LoopField, 1, RegExMatch(A_LoopField, "i)^[ux]?([\dA-F]{4})?([\dA-F]{2})?\K") - 1) : "") && RegexMatch(t, "i)^[ux][\da-f]+$") ? Chr(Abs("0x" SubStr(t, 2))) SubStr(A_LoopField, RegExMatch(A_LoopField, "i)^[ux]?([\dA-F]{4})?([\dA-F]{2})?\K")) : m.has(Ord(A_LoopField)) ? m[Ord(A_LoopField)] SubStr(A_LoopField, 2) : "\" A_LoopField, e := A_LoopField = "" ? e : !e
return v
}
}
/**
* Converts a AutoHotkey Array/Map/Object to a Object Notation JSON string.
* @param obj A AutoHotkey value, usually an object or array or map, to be converted.
* @param expandlevel The level of JSON string need to expand, by default expand all.
* @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
*/
static Dump(obj, expandlevel := unset, space := " ") {
expandlevel := IsSet(expandlevel) ? Abs(expandlevel) : 10000000
return Trim(CO(obj, expandlevel))
CO(O, J := 0, R := 0, Q := 0) {
static M1 := "{", M2 := "}", S1 := "[", S2 := "]", N := "`n", C := ",", S := "- ", E := "", K := ":"
if (OT := Type(O)) = "Array" {
D := !R ? S1 : ""
for key, value in O {
F := (VT := Type(value)) = "Array" ? "S" : InStr("Map,Object", VT) ? "M" : E
Z := VT = "Array" && value.Length = 0 ? "[]" : ((VT = "Map" && value.count = 0) || (VT = "Object" && ObjOwnPropCount(value) = 0)) ? "{}" : ""
D .= (J > R ? "`n" CL(R + 2) : "") (F ? (%F%1 (Z ? "" : CO(value, J, R + 1, F)) %F%2) : ES(value)) (OT = "Array" && O.Length = A_Index ? E : C)
}
} else {
D := !R ? M1 : ""
for key, value in (OT := Type(O)) = "Map" ? (Y := 1, O) : (Y := 0, O.OwnProps()) {
F := (VT := Type(value)) = "Array" ? "S" : InStr("Map,Object", VT) ? "M" : E
Z := VT = "Array" && value.Length = 0 ? "[]" : ((VT = "Map" && value.count = 0) || (VT = "Object" && ObjOwnPropCount(value) = 0)) ? "{}" : ""
D .= (J > R ? "`n" CL(R + 2) : "") (Q = "S" && A_Index = 1 ? M1 : E) ES(key) K (F ? (%F%1 (Z ? "" : CO(value, J, R + 1, F)) %F%2) : ES(value)) (Q = "S" && A_Index = (Y ? O.count : ObjOwnPropCount(O)) ? M2 : E) (J != 0 || R ? (A_Index = (Y ? O.count : ObjOwnPropCount(O)) ? E : C) : E)
if J = 0 && !R
D .= (A_Index < (Y ? O.count : ObjOwnPropCount(O)) ? C : E)
}
}
if J > R
D .= "`n" CL(R + 1)
if R = 0
D := RegExReplace(D, "^\R+") (OT = "Array" ? S2 : M2)
return D
}
ES(S) {
switch Type(S) {
case "Float":
if (v := '', d := InStr(S, 'e'))
v := SubStr(S, d), S := SubStr(S, 1, d - 1)
if ((StrLen(S) > 17) && (d := RegExMatch(S, "(99999+|00000+)\d{0,3}$")))
S := Round(S, Max(1, d - InStr(S, ".") - 1))
return S v
case "Integer":
return S
case "String":
S := StrReplace(S, "\", "\\")
S := StrReplace(S, "`t", "\t")
S := StrReplace(S, "`r", "\r")
S := StrReplace(S, "`n", "\n")
S := StrReplace(S, "`b", "\b")
S := StrReplace(S, "`f", "\f")
S := StrReplace(S, "`v", "\v")
S := StrReplace(S, '"', '\"')
return '"' S '"'
default:
return S == JSON.true ? "true" : S == JSON.false ? "false" : "null"
}
}
CL(i) {
Loop (s := "", space ? i - 1 : 0)
s .= space
return s
}
}
}