I'm implementing a .NET wrapper around https://linkify.js.org/ java-script library. For this task I'm using Microsoft's ClearScript.
The only problem I have is when implementing the attributes property that is passed in the options object parameter for the linkifyHtml function.
The function expects an argument like this in java-script code:
attributes: {
title: "External Link",
etc: "etc..."
}
So, the idea I had is to define this Attributes
property which returns an anonymous type:
Public Class LinkifyFormatOptions
Public Property Attributes As Func(Of LinkifyLinkType, String, Object) =
Function(linkType As LinkifyLinkType, href As String) As Object
Select Case linkType
Case LinkifyLinkType.Url
Return New With {.test_attribute = "This is a test attribute"}
Case Else
Return Nothing
End Select
End Function
End Class
... which later I accommodate its return value for building the options object:
Friend Function GetLinkifyOptions() As Object
Dim options As New With {
.attributes = Function(href As String, linkType As String) As Object
Select Case linkType
Case "url"
Return Me.Attributes(LinkifyLinkType.Url, href)
Case "email"
Return Me.Attributes(LinkifyLinkType.Email, href)
Case "hashtag"
Return Me.Attributes(LinkifyLinkType.Hashtag, href)
Case "mention"
Return Me.Attributes(LinkifyLinkType.Mention, href)
Case "ticket"
Return Me.Attributes(LinkifyLinkType.Ticket, href)
Case Else
Return Nothing
End Select
End Function,
.className = etc...,
.formatHref = etc...,
.etc = etc...
}
End Function
... to finally call the java-script's linkifyHtml
function here:
Imports Microsoft.ClearScript
Imports Microsoft.ClearScript.V8
Public Shared Function Linkify(str As String,
[interface] As LinkifyInterface,
Optional plugins As LinkifyPlugins = LinkifyPlugins.None,
Optional formatOptions As LinkifyFormatOptions = Nothing) As String
' Argument validations and etc...
Using engine As New V8ScriptEngine("linkify_engine", V8ScriptEngineFlags.DisableGlobalMembers)
Dim options As Object = formatOptions.GetLinkifyOptions()
' linkify.js scripts loading and etc...
Dim linkifyFunction As ScriptObject = DirectCast(engine.Evaluate("linkifyHtml"), ScriptObject)
Return CStr(linkifyFunction.Invoke(asConstructor:=False, str, options))
End Using
End Function
The problem is that the ClearScript engine is exposing the members of the Object
type, like "ToString" method, "GetHashCode", etc.
This is the output I get after calling the java-script's linkifyHtml
function:
<p>Domain: <a href="http://example.domain.com" $test_attribute="This is a test attribute" ToString="[object Object]" Equals="[object Object]" GetHashCode="[object Object]" GetType="[object Object]" Finalize="[object Object]" MemberwiseClone="[object Object]" test_attribute="This is a test attribute" toJSON="function toJSON() { [native code] }">example.domain.com</a></p>
I partially solved that issue by enabling the V8ScriptEngine.SuppressInstanceMethodEnumeration Property like this:
Using engine As New V8ScriptEngine("linkify_engine", V8ScriptEngineFlags.DisableGlobalMembers)
engine.SuppressInstanceMethodEnumeration = True
' etc...
End Using
However, a reflected toJSON
function and other garbage is still present in the output and I don't know how to get rid of it:
<p>Domain: <a href="http://example.domain.com" $test_attribute="This is a test attribute" test_attribute="This is a test attribute" toJSON="function toJSON() { [native code] }">example.domain.com</a></p>
The desired output, of course, will be this:
<p>Domain: <a href="http://example.domain.com" test_attribute="This is a test attribute">example.domain.com</a></p>
Then, I would like to know how to suppress that reflected toJSON
member, together with the members that starts with a "$" symbol, like $test_attribute
.
I think that maybe I would need to replace and adapt the anonymous type usage for a qualified type that inherits or implements a specific interface from the Microsoft.ClearScript
namespaces and/or maybe also use specific attribute classes, all that to let the V8 engine do things in the right way... but I don't know how to.
I tried to define a custom type that I used to redefine (shadowing or overriding) ToString
, GetHashCode
, Equals
and the other members, and then I tried to apply various attribute classes in those redefined members to try to "hide" them from the ClearScript engine, without success. Anyway, the toJSON
function I don't know where it comes from.
I can reproduce your issue with this simplified code:
Dim attributes = New With {.test_attribute = "This is a test attribute"}
Dim options = New With {.attributes = attributes}
Console.WriteLine(engine.Script.linkifyHtml("example.domain.com", options))
Comments: The $test_attribute
property seems to be a part of the anonymous type, and ClearScript is probably doing the right thing in exposing it. On the other hand, the presence of toJSON
as an enumerable property is likely to be a bug in ClearScript.
Recommendation: Instead of using an anonymous type, use a script object. Here's a function that creates and populates a script object:
Private Function CreateObject(engine As V8ScriptEngine, ParamArray props() As (name As String, value As Object))
Dim result = engine.Evaluate("({})")
For Each prop In props
result(prop.name) = prop.value
Next
CreateObject = result
End Function
You can use that function to get the desired behavior:
Dim attributes = CreateObject(engine, ("test_attribute", "This is a test attribute"))
Dim options = CreateObject(engine, ("attributes", attributes))
Console.WriteLine(engine.Script.linkifyHtml("example.domain.com", options))