|
Option Explicit |
|
|
|
Const DataVbaIdName = "data-vbaid" |
|
|
|
Private WithEvents TargetBrowser As WebBrowser |
|
|
|
Private NextElementId |
|
Private EventCollectionDict As Dictionary |
|
|
|
Public Function Init(Browser) |
|
Set TargetBrowser = Browser |
|
NextElementId = CLng(1) |
|
Set EventCollectionDict = New Dictionary |
|
Set Init = Me |
|
End Function |
|
|
|
Public Function AddEventHandler(DomElement, EventType, CallbackFunc, Optional CallObject, Optional UseCapture As Boolean = False, Optional StopPropagation As Boolean = False, Optional PreventDefault As Boolean = False) |
|
With TargetBrowser.Document |
|
' イベント対象となる要素に一意の番号(VbaId)を付与 |
|
Dim VbaId: VbaId = DomElement.getAttribute(DataVbaIdName) |
|
If IsNull(VbaId) Then |
|
VbaId = NextElementId |
|
NextElementId = NextElementId + 1 |
|
Call DomElement.setAttribute(DataVbaIdName, VbaId) |
|
End If |
|
VbaId = CStr(VbaId) |
|
|
|
' 指定されたイベント情報をVbaIdをkeyとして対応するCollectionに追加 |
|
Dim EventInfo As Dictionary: Set EventInfo = New Dictionary |
|
With EventInfo |
|
Call .Add("DomElement", DomElement) |
|
Call .Add("EventType", EventType) |
|
Call .Add("CallbackFunc", CallbackFunc) |
|
Call .Add("CallObject", IIf(IsMissing(CallObject), Nothing, CallObject)) |
|
Call .Add("UseCapture", UseCapture) |
|
Call .Add("StopPropagation", StopPropagation) |
|
Call .Add("PreventDefault", PreventDefault) |
|
End With |
|
If Not EventCollectionDict.Exists(VbaId) Then Call EventCollectionDict.Add(VbaId, New Collection) |
|
Call EventCollectionDict(VbaId).Add(EventInfo) |
|
|
|
' ブラウザ上(JavaScript)にて、イベント対象となる要素にイベントハンドラを登録 |
|
' [TODO] イベントハンドラ登録時に document.querySelector() にて要素を特定する関係上、DomElement は予め document 下の DOM ツリー上になければならない |
|
' |
|
' ※ EventType で指定されたイベントが発生すると、 |
|
' - event オブジェクトを自要素([data-vbaid="<VbaId>"])の lastEvent として保存 |
|
' - document.title を自要素の VbaId に書き換え |
|
' が行われ、これにより VBAのTargetBrowser_TitleChangeプロシージャが呼び出される |
|
' |
|
Dim ElmScript: Set ElmScript = .createElement("script") |
|
Dim ScriptText |
|
|
|
If .documentMode < 9 Then |
|
' [TODO] コールバックに渡される event において、IE8 以下だと event.type がセットされない模様→独自に .eventType を設定 |
|
ScriptText = Join(Array( _ |
|
"(function(elm){", _ |
|
"elm.attachEvent('on" & EventType & "', function(event){", _ |
|
" event.eventType = '" & EventType & "'; elm.lastEvent = event;", _ |
|
" document.title = " & VbaId & ";", _ |
|
" return " & IIf(PreventDefault, "false", "true") & ";", _ |
|
"});", _ |
|
"})(document.querySelector('[" & DataVbaIdName & "=""" & VbaId & """]'));" _ |
|
), vbLf) |
|
Else |
|
ScriptText = Join(Array( _ |
|
"document.querySelector('[" & DataVbaIdName & "=""" & VbaId & """]').addEventListener('" & EventType & "', function(event){", _ |
|
" " & IIf(StopPropagation, "event.stopPropagation();", ""), _ |
|
" " & IIf(PreventDefault, "event.preventDefault();", ""), _ |
|
" this.lastEvent = event;", _ |
|
" document.title = " & VbaId & ";", _ |
|
"}, " & IIf(UseCapture, "true", "false") & ");" _ |
|
), vbLf) |
|
End If |
|
ElmScript.Text = ScriptText |
|
Call .body.appendChild(ElmScript) |
|
End With |
|
Set AddEventHandler = Me |
|
End Function |
|
|
|
' JavaScript での document.title の上書きに伴い呼び出される |
|
Private Sub TargetBrowser_TitleChange(ByVal Text As String) |
|
Dim VbaId: VbaId = Text |
|
If Not EventCollectionDict.Exists(VbaId) Then Exit Sub |
|
|
|
Dim TargetElement |
|
|
|
' On Error Resume Next |
|
' Set TargetElement = TargetBrowser.Document.querySelector("[" & DataVbaIdName & "=""" & VbaId & """]") |
|
' ' [TODO] documentMode が 8 以下だと、WebBrowser コントロールの Document.querySelector が動作しない模様 |
|
' If Err.Number <> 0 Then Exit Sub |
|
|
|
If Not EventCollectionDict.Exists(VbaId) Then Exit Sub |
|
|
|
Dim EventInfo |
|
For Each EventInfo In EventCollectionDict(VbaId) |
|
Set TargetElement = EventInfo("DomElement") |
|
Call Callback(TargetElement, EventInfo) |
|
Next |
|
End Sub |
|
|
|
Private Sub Callback(DomElement, EventInfo) |
|
Dim DomEvent: Set DomEvent = DomElement.lastEvent |
|
Dim EventType: EventType = EventInfo("EventType") |
|
|
|
If TargetBrowser.Document.documentMode < 9 Then |
|
' [TODO] DomEvent.Type は IE8 以下だとセットされない模様→ JavaScript 側にて独自に DomEvent.eventType を設定するようにしてある |
|
If DomEvent.EventType <> EventType Then Exit Sub |
|
Else |
|
If DomEvent.Type <> EventType Then Exit Sub |
|
End If |
|
|
|
Dim CallbackFunc: CallbackFunc = EventInfo("CallbackFunc") |
|
Dim CallObject: Set CallObject = EventInfo("CallObject") |
|
|
|
If CallObject Is Nothing Then |
|
Call Application.Run(CallbackFunc, EventType, DomElement, DomEvent) |
|
Else |
|
Call CallByName(CallObject, CallbackFunc, VbMethod, EventType, DomElement, DomEvent) |
|
End If |
|
End Sub |