Page 1 of 2

How to get a JavaScript variable on a WebViewGadget from PB procedure?

Posted: Sun Jan 26, 2025 11:27 am
by Caronte3D
I know how to set the value but... how to read it directly from a PureBasic procedure?

This is the macro I use to set a text element by her ID:

Code: Select all

Macro SetTextElement(element, text)
        WebViewExecuteScript(0, element+~"=document.getElementById(\""+element+~"\"); "+element+~".textContent=\""+text+~"\";");
EndMacro
Thanks in advance! :D

Re: How to get a JavaScript variable on a WebViewGadget from PB procedure?

Posted: Sun Jan 26, 2025 12:08 pm
by infratec
What should I do with this Macro :?:

There is no code that I can execute, so I do nothing.

Re: How to get a JavaScript variable on a WebViewGadget from PB procedure?

Posted: Sun Jan 26, 2025 12:46 pm
by Caronte3D
Sorry infratec, the Macro was only an example of the oposite (set a text element by ID) in case someone need it.
What I need is a way to obtain the text of an element (i.e: a button) by her ID.

I modified the example on the help to change the button text form PB (without clicking on the JS button), but... how to get that text (or another one) from JS to PB if I only know the element ID?

Code: Select all

  Html$ =  ~"<button id=\"displayInfo\">Display Info</button>\n"+
           ~"<script>\n"+
           ~"  const displayInfoElement=document.getElementById(\"displayInfo\");\n"+
           ~"  document.addEventListener(\"DOMContentLoaded\", () => {\n"+
           ~"    displayInfoElement.addEventListener(\"click\", () => {\n"+
           ~"      window.displayInfo(1000, 2000).then(result => {\n"+
           ~"      });\n"+
           ~"    });\n"+
           ~"  });\n"+
           ~"</script>";
  
  Macro SetTextElement(element, text)
          WebViewExecuteScript(0, element+~"=document.getElementById(\""+element+~"\"); "+element+~".textContent=\""+text+~"\";");
  EndMacro  
  

  Procedure mytimer()
    Static ii
    ii+1
    ;  WebViewExecuteScript(0, ~"incre=document.getElementById(\"increment2\"); incre.textContent=\"Hello from Callback "+Str(ii)+~"!\";");
    SetTextElement("displayInfo","MyText "+Str(ii))
  EndProcedure
  
  Procedure DisplayInfoCallback(JsonParameters$)
    ; Execute a script in the webview
    WebViewExecuteScript(0, ~"const element=document.getElementById(\"displayInfo\"); element.textContent=\"Hello from Callback !\";");
  EndProcedure

  OpenWindow(0, 100, 100, 400, 400, "Hello", #PB_Window_SystemMenu)

  WebViewGadget(0, 0, 0, 400, 400)
  SetGadgetItemText(0, #PB_WebView_HtmlCode, Html$)
    
  BindWebViewCallback(0, "displayInfo", @DisplayInfoCallback())
  
  AddWindowTimer(0, 1, 1000)
  
  BindEvent(#PB_Event_Timer, @mytimer())


  Repeat 
    Event = WaitWindowEvent()
  Until Event = #PB_Event_CloseWindow


Re: How to get a JavaScript variable on a WebViewGadget from PB procedure?

Posted: Sun Jan 26, 2025 1:57 pm
by infratec
Save it as WebViewExecuteScriptWithStringResult.pbi

Code: Select all

CompilerIf #PB_Compiler_IsMainFile
  EnableExplicit
CompilerEndIf

DataSection
  IID_IUnknown:
  Data.l $00000000
  Data.w $0000, $0000
  Data.b $C0, $00, $00, $00, $00, $00, $00, $46
EndDataSection

Structure IUnknownBase_Structure
  *pVtbl
  *pQueryInterface
  *pAddRef
  *pRelease
  *pInvoke
  
  iRefCount.i
  hEvent.i
EndStructure

Structure ICoreWebView2ExecuteScriptCompletedHandler_Structure Extends IUnknownBase_Structure
  Result$
EndStructure

Procedure.l CoreWebView2CompletedHandler_QueryInterface(*this.IUnknownBase_Structure, *riid.IID, *ppvObject.Integer)
  If *ppvObject And *riid
    If CompareMemory(*riid, ?IID_IUnknown, SizeOf(IID))
      *this\iRefCount + 1
      *ppvObject\i = *this
    Else
      *ppvObject\i = 0
      ProcedureReturn #E_NOINTERFACE
    EndIf
  Else
    ProcedureReturn #E_POINTER
  EndIf
  ProcedureReturn #S_OK
EndProcedure

Procedure.l CoreWebView2CompletedHandler_AddRef(*this.IUnknownBase_Structure)
  ;Debug "CoreWebView2CompletedHandler_AddRef"
  *this\iRefCount + 1
  ProcedureReturn *this\iRefCount
EndProcedure

Procedure.l CoreWebView2CompletedHandler_Release(*this.IUnknownBase_Structure)
  ;Debug "CoreWebView2CompletedHandler_Release"
  *this\iRefCount - 1
  ProcedureReturn *this\iRefCount
EndProcedure

Procedure.l CoreWebView2ExecuteScriptCompletedHandler_Invoke(*this.ICoreWebView2ExecuteScriptCompletedHandler_Structure, errorCode.l, *returnObjectAsJson)
  Protected sResult.s, iCount
  
  ;Debug "CoreWebView2ExecuteScriptCompletedHandler_Invoke"
  
  If errorCode = #S_OK
    If *returnObjectAsJson
      *this\Result$ = PeekS(*returnObjectAsJson)
    EndIf
  EndIf
  
  If *this\hEvent
    SetEvent_(*this\hEvent)
  EndIf
  
  ProcedureReturn #S_OK
  
EndProcedure


Procedure.s WebViewExecuteScriptWithStringResult(WebGadget, JavaScript$)
  
  Protected Result$
  Protected Controller.ICoreWebView2Controller, Core.ICoreWebView2
  Protected *ExecuteScriptCompletedHandler.ICoreWebView2ExecuteScriptCompletedHandler_Structure
  Protected hEvent
  
  
  If GadgetType(WebGadget) = #PB_GadgetType_WebView
    
    Controller = GetGadgetAttribute(WebGadget, #PB_WebView_ICoreController)
    If Controller
      
      If Controller\get_CoreWebView2(@Core) = #S_OK And Core <> #Null
        
        hEvent = CreateEvent_(0, 0, 0, 0)
        If hEvent
          
          *ExecuteScriptCompletedHandler = AllocateMemory(SizeOf(ICoreWebView2ExecuteScriptCompletedHandler_Structure))
          If *ExecuteScriptCompletedHandler
            *ExecuteScriptCompletedHandler\pVtbl = *ExecuteScriptCompletedHandler + OffsetOf(IUnknownBase_Structure\pQueryInterface)
            *ExecuteScriptCompletedHandler\pQueryInterface = @CoreWebView2CompletedHandler_QueryInterface()
            *ExecuteScriptCompletedHandler\pAddRef = @CoreWebView2CompletedHandler_AddRef()
            *ExecuteScriptCompletedHandler\pRelease = @CoreWebView2CompletedHandler_Release()
            *ExecuteScriptCompletedHandler\pInvoke = @CoreWebView2ExecuteScriptCompletedHandler_Invoke()
            
            *ExecuteScriptCompletedHandler\hEvent = hEvent
            
            If Core\ExecuteScript(JavaScript$, *ExecuteScriptCompletedHandler) = #S_OK
              
              While WaitForSingleObject_(hEvent, 0) <> #WAIT_OBJECT_0
                WindowEvent()
              Wend
              
              Result$ = *ExecuteScriptCompletedHandler\Result$
            EndIf
            
            FreeMemory(*ExecuteScriptCompletedHandler)
          EndIf
          
          CloseHandle_(hEvent)
        EndIf
        
        Core\Release()
      EndIf
    EndIf
    
  EndIf
  
  ProcedureReturn Result$
  
EndProcedure



CompilerIf #PB_Compiler_IsMainFile
  
  Define event, Html$
  
  
  Html$ =  "<div id='pb'>string</div>"
  
  
  OpenWindow(0, 0, 0, 1000, 600, "", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  
  
  WebViewGadget(0, 0, 0, 400, 400)
  SetGadgetItemText(0, #PB_WebView_HtmlCode, Html$)
  
  AddWindowTimer(0, 0, 3000)
  
  Repeat 
    Event = WaitWindowEvent()
    If Event = #PB_Event_Timer
      RemoveWindowTimer(0, 0)
      Debug WebViewExecuteScriptWithStringResult(0, "document.getElementById('pb').textContent;")
    EndIf
    
  Until Event = #PB_Event_CloseWindow
  
CompilerEndIf

Code: Select all

IncludeFile "WebViewExecuteScriptWithStringResult.pbi"


Html$ =  ~"<button id=\"displayInfo\">Display Info</button>\n"+
           ~"<script>\n"+
           ~"  const displayInfoElement=document.getElementById(\"displayInfo\");\n"+
           ~"  document.addEventListener(\"DOMContentLoaded\", () => {\n"+
           ~"    displayInfoElement.addEventListener(\"click\", () => {\n"+
           ~"      window.displayInfo(1000, 2000).then(result => {\n"+
           ~"      });\n"+
           ~"    });\n"+
           ~"  });\n"+
           ~"</script>";
  
  Macro SetTextElement(element, text)
          WebViewExecuteScript(0, element+~"=document.getElementById(\""+element+~"\"); "+element+~".textContent=\""+text+~"\";");
  EndMacro  
  

  Procedure mytimer()
    Static ii
    ii+1
    ;  WebViewExecuteScript(0, ~"incre=document.getElementById(\"increment2\"); incre.textContent=\"Hello from Callback "+Str(ii)+~"!\";");
    SetTextElement("displayInfo","MyText "+Str(ii))
    
  EndProcedure
  
  Procedure DisplayInfoCallback(JsonParameters$)
    ; Execute a script in the webview
    WebViewExecuteScript(0, ~"const element=document.getElementById(\"displayInfo\"); element.textContent=\"Hello from Callback !\";");
  EndProcedure

  OpenWindow(0, 100, 100, 400, 400, "Hello", #PB_Window_SystemMenu)

  WebViewGadget(0, 0, 0, 400, 400)
  SetGadgetItemText(0, #PB_WebView_HtmlCode, Html$)
    
  BindWebViewCallback(0, "displayInfo", @DisplayInfoCallback())
  
  AddWindowTimer(0, 1, 1000)
  
  BindEvent(#PB_Event_Timer, @mytimer())

  
  AddWindowTimer(0, 0, 500)
  Repeat
    Event = WaitWindowEvent()
    If Event = #PB_Event_Timer
      Debug WebViewExecuteScriptWithStringResult(0, "document.getElementById('displayInfo').textContent")
    EndIf
  Until Event = #PB_Event_CloseWindow

Re: How to get a JavaScript variable on a WebViewGadget from PB procedure?

Posted: Sun Jan 26, 2025 2:10 pm
by Caronte3D
Thank you very much, works nice! What would we do without you, gurus? :D
Anyway I think PB should have a simplest solution for that simple thing, like let WebViewExecuteScript to return a value or something so... :wink:

Re: How to get a JavaScript variable on a WebViewGadget from PB procedure?

Posted: Sun Jan 26, 2025 2:42 pm
by infratec
I added a feature request.

Re: How to get a JavaScript variable on a WebViewGadget from PB procedure?

Posted: Sun Jan 26, 2025 3:17 pm
by mk-soft
Over callback ...

Update
- With ButtonGadget

Code: Select all

; TOP

Html$ =  ~"<input type=\"text\" id=\"userInput\" name=\"userInput\">\n"+
         ~"<button id=\"buttonGetData\">Get Data</button>\n"+
         ~"<script>\n"+
         ~"  const buttonGetDataElement=document.getElementById(\"buttonGetData\");\n"+
         ~"  document.addEventListener(\"DOMContentLoaded\", () => {\n"+
         ~"    buttonGetData.addEventListener(\"click\", () => {\n"+
         ~"      var id = \"userInput\";"+
         ~"      var userInput = document.getElementById(id).value;"+
         ~"      window.getData(id, userInput);"+
         ~"    });\n"+
         ~"  });\n"+
         ~"</script>";

Procedure GetDataJS(JsonParameters$)
  ParseJSON(0, JsonParameters$)
  Debug "ID: " + GetJSONString(GetJSONElement(JSONValue(0), 0))
  Debug "Value: " + GetJSONString(GetJSONElement(JSONValue(0), 1))
  
  ;Debug JsonParameters$
  ProcedureReturn 0
EndProcedure


OpenWindow(0, 100, 100, 400, 250, "Hello", #PB_Window_SystemMenu)

WebViewGadget(0, 0, 0, 400, 200, #PB_WebView_Debug)
SetGadgetItemText(0, #PB_WebView_HtmlCode, Html$)
ButtonGadget(1, 10, 210, 120, 25, "Get Data")
BindWebViewCallback(0, "getData", @GetDataJS())

Repeat 
  Select WaitWindowEvent()
    Case #PB_Event_Gadget 
      If EventGadget() = 1
        id.s = ~"\"userInput\""
        script.s = ~"window.getData(" + id + ", document.getElementById(" + id +").value)"
        WebViewExecuteScript(0, script)
      EndIf
      
    Case #PB_Event_CloseWindow
      Break
      
  EndSelect
ForEver

Re: How to get a JavaScript variable on a WebViewGadget from PB procedure?

Posted: Sun Jan 26, 2025 3:20 pm
by infratec
But for this you need a JS function.

My code returns a value by an id of any element.
Look at the example with <div>

Re: How to get a JavaScript variable on a WebViewGadget from PB procedure?

Posted: Sun Jan 26, 2025 5:05 pm
by mk-soft
Your code only for Windows ...

Update
- With ButtonGadget and ID

See Update

Re: How to get a JavaScript variable on a WebViewGadget from PB procedure?

Posted: Sun Jan 26, 2025 5:19 pm
by Piero
Too much time passed since the last time I boasted I'm a mk-soft Fan

Re: How to get a JavaScript variable on a WebViewGadget from PB procedure?

Posted: Sun Jan 26, 2025 5:33 pm
by mk-soft
@infratec

What I still notice about your code is that when you call your function, the PB events are all deleted until the result is there.
It is better to respond to the data with a callback so that the program is still operable.

For Linux, I also solved these with a callback.
See: Linux WebGadget Extensions

P.S.
Is that first that I try out the new WebViewGadget functions.
Need to do more with JavaScript. There is certainly more ...

Re: How to get a JavaScript variable on a WebViewGadget from PB procedure?

Posted: Sun Jan 26, 2025 5:52 pm
by Caronte3D
mk-soft wrote: Sun Jan 26, 2025 5:33 pm Is that first that I try out the new WebViewGadget functions.
Need to do more with JavaScript. There is certainly more ...
Me too! Indeed I think this can solve the "obsolete look" of the PB programs UI 8)

Thanks again to both of you :wink:

Re: How to get a JavaScript variable on a WebViewGadget from PB procedure?

Posted: Sun Jan 26, 2025 6:05 pm
by infratec
A bit extended to show when and how you get the result:

Code: Select all

EnableExplicit


Procedure GetDataCallback(JsonParameters$)
  
  PostEvent(#PB_Event_DataReceived, EventWindow(), EventGadget(), 0, UTF8(JsonParameters$))
  
  ProcedureReturn #Null
  
EndProcedure



Define Html$, *Data

Html$ = "<div id='userInput'>test</div>"

OpenWindow(0, 100, 100, 400, 250, "Hello", #PB_Window_SystemMenu)

WebViewGadget(0, 0, 0, 400, 200, #PB_WebView_Debug)
SetGadgetItemText(0, #PB_WebView_HtmlCode, Html$)
ButtonGadget(1, 10, 210, 120, 25, "Get Data")
BindWebViewCallback(0, "getData", @GetDataCallback())

Repeat 
  Select WaitWindowEvent()
    Case #PB_Event_DataReceived
      If EventGadget() = 0
        *Data = EventData()
        If *Data
          Debug PeekS(*Data, -1, #PB_UTF8)
          FreeMemory(*Data)
        EndIf
      EndIf
      
    Case #PB_Event_Gadget
      If EventGadget() = 1
        WebViewExecuteScript(0, "window.getData(document.getElementById('userInput').textContent)")
      EndIf
      
    Case #PB_Event_CloseWindow
      Break
      
  EndSelect
ForEver

Re: How to get a JavaScript variable on a WebViewGadget from PB procedure?

Posted: Sun Jan 26, 2025 6:22 pm
by mk-soft
Nice :wink:

Missing ...

Code: Select all

Enumeration CustomEvent #PB_Event_FirstCustomValue
  #PB_Event_DataReceived
EndEnumeration

Re: How to get a JavaScript variable on a WebViewGadget from PB procedure?

Posted: Sun Jan 26, 2025 6:26 pm
by Caronte3D
Image