|
| 1 | +#AutoIt3Wrapper_UseX64=y |
| 2 | +; Html_Gui.au3 |
| 3 | +#include <GUIConstantsEx.au3> |
| 4 | +#include <WindowsConstants.au3> |
| 5 | +#include <Array.au3> |
| 6 | + |
| 7 | +; Register exit function to ensure clean WebView2 shutdown |
| 8 | +OnAutoItExitRegister("_ExitApp") |
| 9 | + |
| 10 | +; Global objects |
| 11 | +Global $oWeb, $oJS |
| 12 | +Global $oMyError = ObjEvent("AutoIt.Error", "_ErrFunc") ; COM Error Handler |
| 13 | +Global $g_DebugInfo = True |
| 14 | +Global $g_sProfilePath = @ScriptDir & "\UserDataFolder" |
| 15 | +Global $hGUI |
| 16 | + |
| 17 | +Main() |
| 18 | + |
| 19 | +Func Main() |
| 20 | + ; Create GUI with resizing support |
| 21 | + $hGUI = GUICreate("WebView2AutoIt JSON Viewer", 500, 650, -1, -1, BitOR($WS_OVERLAPPEDWINDOW, $WS_CLIPCHILDREN)) |
| 22 | + GUISetBkColor(0x2B2B2B, $hGUI) |
| 23 | + |
| 24 | + ; GUI Controls for JSON Tree interaction |
| 25 | + Local $idExpand = GUICtrlCreateLabel("Expand All", 10, 10, 90, 30) |
| 26 | + GUICtrlSetFont(-1, 12, Default, $GUI_FONTUNDER, "Segoe UI") |
| 27 | + GUICtrlSetResizing(-1, $GUI_DOCKALL) |
| 28 | + GUICtrlSetColor(-1, 0x00FF00) ; Green |
| 29 | + |
| 30 | + Local $idCollapse = GUICtrlCreateLabel("Collapse All", 110, 10, 90, 30) |
| 31 | + GUICtrlSetFont(-1, 12, Default, $GUI_FONTUNDER, "Segoe UI") |
| 32 | + GUICtrlSetResizing(-1, $GUI_DOCKALL) |
| 33 | + GUICtrlSetColor(-1, 0xFF4D4D) ; Red |
| 34 | + |
| 35 | + Local $idFind = GUICtrlCreateLabel("Search", 210, 10, 60, 30) |
| 36 | + GUICtrlSetFont(-1, 12, Default, $GUI_FONTUNDER, "Segoe UI") |
| 37 | + GUICtrlSetResizing(-1, $GUI_DOCKALL) |
| 38 | + GUICtrlSetColor(-1, 0xFFD700) ; Gold |
| 39 | + |
| 40 | + Local $idLoadFile = GUICtrlCreateLabel("Load JSON", 280, 10, 90, 30) |
| 41 | + GUICtrlSetFont(-1, 12, Default, $GUI_FONTUNDER, "Segoe UI") |
| 42 | + GUICtrlSetResizing(-1, $GUI_DOCKALL) |
| 43 | + GUICtrlSetColor(-1, 0x00CCFF) ; Light Blue |
| 44 | + |
| 45 | + ; Initialize WebView2 Manager and register events |
| 46 | + $oWeb = ObjCreate("NetWebView2.Manager") |
| 47 | + ObjEvent($oWeb, "WebEvents_", "IWebViewEvents") |
| 48 | + |
| 49 | + ; Important: Pass $hGUI in parentheses to maintain Pointer type for COM |
| 50 | + $oWeb.Initialize(($hGUI), $g_sProfilePath, 0, 50, 500, 600) |
| 51 | + |
| 52 | + ; Initialize JavaScript Bridge |
| 53 | + $oJS = $oWeb.GetBridge() |
| 54 | + ObjEvent($oJS, "JavaScript_", "IBridgeEvents") |
| 55 | + |
| 56 | + ; Wait for WebView2 to be ready |
| 57 | + Do |
| 58 | + Sleep(50) |
| 59 | + Until $oWeb.IsReady |
| 60 | + |
| 61 | + ; WebView2 Configuration |
| 62 | + $oWeb.SetAutoResize(True) ; Using SetAutoResize(True) to skip WM_SIZE |
| 63 | + $oWeb.BackColor = "0x2B2B2B" |
| 64 | + $oWeb.AreDevToolsEnabled = True ; Allow F12 |
| 65 | + $oWeb.ZoomFactor = 1.2 |
| 66 | + |
| 67 | + ; Initial JSON display |
| 68 | + Local $sMyJson = '{"Game": "Witcher 3", "ID": 1, "Meta": {"Developer": "CD Projekt", "Year": 2015 }, "Tags": ["RPG", "Open World"]}' |
| 69 | + |
| 70 | + _Web_jsonTree($oWeb, $sMyJson) ; 🏆 https://github.com/summerstyle/jsonTreeViewer |
| 71 | + |
| 72 | + GUISetState(@SW_SHOW) |
| 73 | + |
| 74 | + Local $sLastSearch = "" |
| 75 | + |
| 76 | + ; Main Application Loop |
| 77 | + While 1 |
| 78 | + Switch GUIGetMsg() |
| 79 | + Case $GUI_EVENT_CLOSE |
| 80 | + Exit |
| 81 | + |
| 82 | + Case $idExpand |
| 83 | + ; Call JavaScript expand method on the global tree object |
| 84 | + $oWeb.ExecuteScript("if(window.tree) window.tree.expand();") |
| 85 | + |
| 86 | + Case $idCollapse |
| 87 | + ; Call JavaScript collapse method |
| 88 | + $oWeb.ExecuteScript("if(window.tree) window.tree.collapse();") |
| 89 | + |
| 90 | + Case $idFind |
| 91 | + Local $sInput = InputBox("JSON Search", "Enter key or value:", $sLastSearch, "", 200, 130, Default, Default, Default, $hGUI) |
| 92 | + If Not @error And StringLen(StringStripWS($sInput, 3)) > 0 Then |
| 93 | + $sLastSearch = StringStripWS($sInput, 3) |
| 94 | + _Web_jsonTreeFind($sLastSearch, False) ; New search |
| 95 | + EndIf |
| 96 | + |
| 97 | + Case $idLoadFile |
| 98 | + Local $sFilePath = FileOpenDialog("Select JSON File", @ScriptDir, "JSON Files (*.json;*.txt)", 1) |
| 99 | + If Not @error Then |
| 100 | + Local $sFileData = FileRead($sFilePath) |
| 101 | + If $sFileData <> "" Then |
| 102 | + _Web_jsonTree($oWeb, $sFileData) ; Re-render tree with new data |
| 103 | + __DW("+ Loaded JSON from: " & $sFilePath & @CRLF) |
| 104 | + EndIf |
| 105 | + EndIf |
| 106 | + |
| 107 | + EndSwitch |
| 108 | + WEnd |
| 109 | +EndFunc ;==>Main |
| 110 | + |
| 111 | +#Region ; === EVENT HANDLERS === |
| 112 | + |
| 113 | +; Handles native WebView2 events |
| 114 | +Func WebEvents_OnMessageReceived($sMsg) |
| 115 | + __DW("+++ [WebEvents]: " & (StringLen($sMsg) > 150 ? StringLeft($sMsg, 150) & "..." : $sMsg) & @CRLF, 0) |
| 116 | + Local $iSplitPos = StringInStr($sMsg, "|") |
| 117 | + Local $sCommand = $iSplitPos ? StringStripWS(StringLeft($sMsg, $iSplitPos - 1), 3) : $sMsg |
| 118 | + Local $sData = $iSplitPos ? StringTrimLeft($sMsg, $iSplitPos) : "" |
| 119 | + Local $aParts |
| 120 | + |
| 121 | + Switch $sCommand |
| 122 | + Case "WINDOW_RESIZED" |
| 123 | + $aParts = StringSplit($sData, "|") |
| 124 | + If $aParts[0] >= 2 Then |
| 125 | + Local $iW = Int($aParts[1]), $iH = Int($aParts[2]) |
| 126 | + ; Filter minor resize glitches |
| 127 | + If $iW > 50 And $iH > 50 Then __DW("WINDOW_RESIZED : " & $iW & "x" & $iH & @CRLF) |
| 128 | + EndIf |
| 129 | + EndSwitch |
| 130 | +EndFunc ;==>WebEvents_OnMessageReceived |
| 131 | + |
| 132 | +; Handles custom messages from JavaScript (window.chrome.webview.postMessage) |
| 133 | +Func JavaScript_OnMessageReceived($sMsg) |
| 134 | + __DW(">>> [JavaScript]: " & (StringLen($sMsg) > 150 ? StringLeft($sMsg, 150) & "..." : $sMsg) & @CRLF, 0) |
| 135 | + Local $sFirstChar = StringLeft($sMsg, 1) |
| 136 | + |
| 137 | + ; 1. Modern JSON Messaging |
| 138 | + If $sFirstChar = "{" Or $sFirstChar = "[" Then |
| 139 | + __DW("+> : Processing JSON message..." & @CRLF) |
| 140 | + Local $oJson = ObjCreate("NetJson.Parser") |
| 141 | + If Not IsObj($oJson) Then Return ConsoleWrite("!> Error: Failed to create NetJson object." & @CRLF) |
| 142 | + |
| 143 | + $oJson.Parse($sMsg) |
| 144 | + Local $sJobType = $oJson.GetTokenValue("type") |
| 145 | + |
| 146 | + Switch $sJobType |
| 147 | + Case "CONSOLE_LOG" |
| 148 | + If Not _Web_DebugJStoConsole($oWeb) Then Return |
| 149 | + Local $sLvl = $oJson.GetTokenValue("level") |
| 150 | + Local $sTxt = $oJson.GetTokenValue("message") |
| 151 | + __DW(StringFormat("[JS-%s] %s", $sLvl, $sTxt) & @CRLF, 0) |
| 152 | + |
| 153 | + Case "COM_TEST" |
| 154 | + __DW("- COM_TEST Confirmed: " & $oJson.GetTokenValue("status") & @CRLF) |
| 155 | + EndSwitch |
| 156 | + |
| 157 | + Else |
| 158 | + ; 2. Legacy / Native Pipe-Delimited Messaging |
| 159 | + __DW("+> : Processing Delimited message..." & @CRLF, 0) |
| 160 | + Local $sCommand, $sData, $iSplitPos |
| 161 | + $iSplitPos = StringInStr($sMsg, "|") - 1 |
| 162 | + |
| 163 | + If $iSplitPos < 0 Then |
| 164 | + $sCommand = StringStripWS($sMsg, 3) |
| 165 | + $sData = "" |
| 166 | + Else |
| 167 | + $sCommand = StringStripWS(StringLeft($sMsg, $iSplitPos), 3) |
| 168 | + $sData = StringTrimLeft($sMsg, $iSplitPos + 1) |
| 169 | + EndIf |
| 170 | + |
| 171 | + Switch $sCommand |
| 172 | + Case "JSON_CLICKED" |
| 173 | + Local $aClickData = StringSplit($sData, "=", 2) ; Split "Key = Value" |
| 174 | + If UBound($aClickData) >= 2 Then |
| 175 | + Local $sKey = StringStripWS($aClickData[0], 3) |
| 176 | + Local $sVal = StringStripWS($aClickData[1], 3) |
| 177 | + __DW("+++ Property: " & $sKey & " | Value: " & $sVal & @CRLF) |
| 178 | + EndIf |
| 179 | + |
| 180 | + Case "COM_TEST" |
| 181 | + __DW("- Status: Legacy COM_TEST: " & $sData & @CRLF) |
| 182 | + |
| 183 | + Case "ERROR" |
| 184 | + __DW("! Status: " & $sData & @CRLF) |
| 185 | + EndSwitch |
| 186 | + EndIf |
| 187 | +EndFunc ;==>JavaScript_OnMessageReceived |
| 188 | + |
| 189 | +Func WebEvents_OnContextMenuRequested($sLink, $iX, $iY, $sSelection) |
| 190 | + #forceref $sLink, $iX, $iY, $sSelection |
| 191 | +EndFunc ;==>WebEvents_OnContextMenuRequested |
| 192 | + |
| 193 | +#EndRegion ; === EVENT HANDLERS === |
| 194 | + |
| 195 | +#Region ; === UTILS === |
| 196 | + |
| 197 | +Func _ErrFunc($oError) ; Global COM Error Handler |
| 198 | + ConsoleWrite('@@ Line(' & $oError.scriptline & ') : COM Error Number: (0x' & Hex($oError.number, 8) & ') ' & $oError.windescription & @CRLF) |
| 199 | +EndFunc ;==>_ErrFunc |
| 200 | + |
| 201 | +; Debug Write utility |
| 202 | +Func __DW($sString, $iErrorNoLineNo = 1, $iLine = @ScriptLineNumber, $iError = @error, $iExtended = @extended) |
| 203 | + If Not $g_DebugInfo Then Return SetError($iError, $iExtended, 0) |
| 204 | + Local $iReturn |
| 205 | + If $iErrorNoLineNo = 1 Then |
| 206 | + If $iError Then |
| 207 | + $iReturn = ConsoleWrite("@@(" & $iLine & ") :: @error:" & $iError & ", @extended:" & $iExtended & ", " & $sString) |
| 208 | + Else |
| 209 | + $iReturn = ConsoleWrite("+>(" & $iLine & ") :: " & $sString) |
| 210 | + EndIf |
| 211 | + Else |
| 212 | + $iReturn = ConsoleWrite($sString) |
| 213 | + EndIf |
| 214 | + Return SetError($iError, $iExtended, $iReturn) |
| 215 | +EndFunc ;==>__DW |
| 216 | + |
| 217 | +Func _NetJson_New($sInitialJson = "{}") |
| 218 | + Local $oParser = ObjCreate("NetJson.Parser") |
| 219 | + If Not IsObj($oParser) Then Return SetError(1, 0, 0) |
| 220 | + If $sInitialJson <> "" Then $oParser.Parse($sInitialJson) |
| 221 | + Return $oParser |
| 222 | +EndFunc ;==>_NetJson_New |
| 223 | + |
| 224 | +; #FUNCTION# ==================================================================================================================== |
| 225 | +; Name...........: _Web_jsonTree |
| 226 | +; Description....: Renders JSON data using the jsonTree library by summerstyle. |
| 227 | +; Author.........: summerstyle (https://github.com/summerstyle/jsonTreeViewer) |
| 228 | +; Integration....: Adapted for AutoIt WebView2 |
| 229 | +; =============================================================================================================================== |
| 230 | +Func _Web_jsonTree(ByRef $oWeb, $sJson) |
| 231 | + ; 1. Prepare JSON (Minify to prevent script errors from line breaks) |
| 232 | + Local $oJsonObj = _NetJson_New($sJson) |
| 233 | + $sJson = $oJsonObj.GetMinifiedJson() |
| 234 | + |
| 235 | + ; 2. Load local library files |
| 236 | + Local $sJsLib = FileRead(@ScriptDir & "\.JS_Lib\jsonTree.js") |
| 237 | + Local $sCssLib = FileRead(@ScriptDir & "\.JS_Lib\jsonTreeDark.css") |
| 238 | + |
| 239 | + ; 3. Build HTML with embedded Logic |
| 240 | + Local $sHTML = "<html><head><meta charset=""utf-8""><style>" & _ |
| 241 | + $sCssLib & _ |
| 242 | + "</style></head><body>" & _ |
| 243 | + "<div id='tree-container' class='jsontree_tree'></div>" & _ |
| 244 | + " <div style='position:fixed; bottom:5px; right:10px; font-size:10px; color:#555; font-family:sans-serif;'>" & _ |
| 245 | + " Powered by <a href='https://github.com/summerstyle/jsonTreeViewer' style='color:#777; text-decoration:none;'>jsonTree</a>" & _ |
| 246 | + " </div>" & _ |
| 247 | + "<script>" & @CRLF & _ |
| 248 | + $sJsLib & @CRLF & _ |
| 249 | + ";" & @CRLF & _ ; Ensure library/code separation |
| 250 | + "try {" & @CRLF & _ |
| 251 | + " var data = " & $sJson & ";" & @CRLF & _ |
| 252 | + " var container = document.getElementById('tree-container');" & @CRLF & _ |
| 253 | + " if (typeof jsonTree !== 'undefined') {" & @CRLF & _ |
| 254 | + " window.tree = jsonTree.create(data, container);" & @CRLF & _ ; Assign to window for global access |
| 255 | + " window.tree.expand(1);" & @CRLF & _ |
| 256 | + " container.addEventListener('click', function(e) {" & @CRLF & _ |
| 257 | + " var node = e.target.closest('.jsontree_node');" & @CRLF & _ |
| 258 | + " if (node) {" & @CRLF & _ |
| 259 | + " var labelEl = node.querySelector('.jsontree_label');" & @CRLF & _ |
| 260 | + " var valueEl = node.querySelector('.jsontree_value');" & @CRLF & _ |
| 261 | + " if (labelEl && valueEl) {" & @CRLF & _ |
| 262 | + " var msg = 'JSON_CLICKED|' + labelEl.innerText + ' = ' + valueEl.innerText;" & @CRLF & _ |
| 263 | + " window.chrome.webview.postMessage(msg);" & @CRLF & _ |
| 264 | + " }" & @CRLF & _ |
| 265 | + " }" & @CRLF & _ |
| 266 | + " });" & @CRLF & _ |
| 267 | + " } else {" & @CRLF & _ |
| 268 | + " throw new Error('jsonTree library not loaded');" & @CRLF & _ |
| 269 | + " }" & @CRLF & _ |
| 270 | + "} catch(e) {" & @CRLF & _ |
| 271 | + " window.chrome.webview.postMessage('DEBUG:' + e.message);" & @CRLF & _ |
| 272 | + " document.body.innerHTML = '<b style=""color:red"">JS Error:</b> ' + e.message;" & @CRLF & _ |
| 273 | + "}" & @CRLF & _ |
| 274 | + "</script></body></html>" |
| 275 | + |
| 276 | + ; 4. Navigate to the generated HTML |
| 277 | + $oWeb.NavigateToString($sHTML) |
| 278 | + __DW("+ JSON Tree Rendered & Listeners Active." & @CRLF) |
| 279 | +EndFunc ;==>_Web_jsonTree |
| 280 | + |
| 281 | +; #FUNCTION# ==================================================================================================================== |
| 282 | +; Name...........: _Web_jsonTreeFind |
| 283 | +; Description....: Searches for a string in labels and values and highlights matching nodes. |
| 284 | +; Parameters.....: $sSearch - The string to find |
| 285 | +; =============================================================================================================================== |
| 286 | +Func _Web_jsonTreeFind($sSearch, $bNext = False) |
| 287 | + Local $sJS = _ |
| 288 | + "var term = '" & $sSearch & "'.toLowerCase();" & _ |
| 289 | + "if (!window.searchIndices || window.lastTerm !== term) {" & _ |
| 290 | + " window.searchIndices = [];" & _ |
| 291 | + " window.currentSearchIndex = -1;" & _ |
| 292 | + " window.lastTerm = term;" & _ |
| 293 | + "}" & _ |
| 294 | + "" & _ |
| 295 | + "/* 1. If it's a new search, find all targets */" & _ |
| 296 | + "if (!" & StringLower($bNext) & " || window.searchIndices.length === 0) {" & _ |
| 297 | + " document.querySelectorAll('.jsontree_node_marked').forEach(el => el.classList.remove('jsontree_node_marked', 'jsontree_node_active'));" & _ |
| 298 | + " var targets = document.querySelectorAll('.jsontree_label, .jsontree_value');" & _ |
| 299 | + " window.searchIndices = [];" & _ |
| 300 | + " targets.forEach(function(el) {" & _ |
| 301 | + " var text = el.innerText.toLowerCase();" & _ |
| 302 | + " var isBracket = (text === '{' || text === '}' || text === '[' || text === ']' || text === '{ }' || text === '[ ]');" & _ |
| 303 | + " if (!isBracket && (el.classList.contains('jsontree_label') || el.children.length === 0) && text.includes(term)) {" & _ |
| 304 | + " el.classList.add('jsontree_node_marked');" & _ |
| 305 | + " window.searchIndices.push(el);" & _ |
| 306 | + " }" & _ |
| 307 | + " });" & _ |
| 308 | + "}" & _ |
| 309 | + "" & _ |
| 310 | + "/* 2. Move to next index */" & _ |
| 311 | + "if (window.searchIndices.length > 0) {" & _ |
| 312 | + " /* Remove active class from previous */" & _ |
| 313 | + " if (window.currentSearchIndex >= 0) window.searchIndices[window.currentSearchIndex].classList.remove('jsontree_node_active');" & _ |
| 314 | + " " & _ |
| 315 | + " window.currentSearchIndex++;" & _ |
| 316 | + " if (window.currentSearchIndex >= window.searchIndices.length) window.currentSearchIndex = 0;" & _ |
| 317 | + " " & _ |
| 318 | + " var activeEl = window.searchIndices[window.currentSearchIndex];" & _ |
| 319 | + " activeEl.classList.add('jsontree_node_active');" & _ |
| 320 | + " " & _ |
| 321 | + " /* Expand parents of active element */" & _ |
| 322 | + " var p = activeEl.closest('.jsontree_node');" & _ |
| 323 | + " while (p && p.id !== 'tree-container') {" & _ |
| 324 | + " if (p.classList.contains('jsontree_node_complex')) p.classList.add('jsontree_node_expanded');" & _ |
| 325 | + " p = p.parentElement;" & _ |
| 326 | + " }" & _ |
| 327 | + " activeEl.scrollIntoView({behavior: 'smooth', block: 'center'});" & _ |
| 328 | + "}" |
| 329 | + |
| 330 | + ; Replace the AutoIt variable $bNext with JS boolean |
| 331 | +;~ $sJS = StringReplace($sJS, "$bNext", ($bNext ? "true" : "false")) |
| 332 | + ConsoleWrite("$sJS=" & $sJS & @CRLF) |
| 333 | + $oWeb.ExecuteScript($sJS) |
| 334 | +EndFunc ;==>_Web_jsonTreeFind |
| 335 | + |
| 336 | +Func _ExitApp() |
| 337 | + If IsObj($oWeb) Then $oWeb.Cleanup() |
| 338 | + $oWeb = 0 |
| 339 | + $oJS = 0 |
| 340 | + Exit |
| 341 | +EndFunc ;==>_ExitApp |
| 342 | + |
| 343 | +#EndRegion ; === UTILS === |
0 commit comments