Project

Profile

Help

Feature #5142

open

New <ixsl:add-listener> instruction

Added by Martynas Jusevicius over 2 years ago. Updated over 1 year ago.

Status:
New
Priority:
Normal
Assignee:
-
Category:
IXSL extensions
Sprint/Milestone:
Start date:
2021-10-23
Due date:
% Done:

0%

Estimated time:
Applies to JS Branch:
2
Fix Committed on JS Branch:
Fixed in JS Release:
SEF Generated with:
Platforms:
Company:
-
Contact person:
-
Additional contact persons:
-

Description

Hi. I think there is a generic use case that shows up especially when integrating Saxon-JS with JS libraries. For example, with Google Maps one needs to use this to add an onlick listener for a marker:

  marker.addListener("click", () => {
    infowindow.open({
      anchor: marker,
      map,
      shouldFocus: false,
    });
  });

It requires a JS callback function, which is anonymous () in this case. I would like to use IXSL to handle the marker onclick event instead.

The native IXSL way would be:

<xsl:template match="a[@class = 'marker']" mode="ixsl:onclick">

but the problem is that the DOM generated by Google Maps, including marker IDs or class names, cannot be relied upon. So the match pattern approach does not really work.

I propose a second, generalized mechanism to handle events using a new extension instruction <ixsl:add-listener>. It could be used like this:

<ixsl:add-listener name="click" object="$marker">
    <xsl:call-template name="onMarkerClick"/>
</ixsl:add-listener>

<xsl:template name="onMarkerClick" mode="ixsl:onclick">
    <xsl:variable name="event" select="ixsl:event()"/> <!-- MapMouseEvent -->
    <xsl:variable name="lat-lng" select="ixsl:get(ixsl:event(), 'latLng')"/>
    ...
</xsl:template>

If the native match/mode/ approach can be used instead, one could replace the name="onMarkerClick" with mode="ixsl:onclick" and the same template body should still work.

The <ixsl:add-listener> call would translate to this pseudo-JS:

marker.addListener('click', (event) => { SaxonJS.callTemplate('onMarkerClick', event); });
Actions #1

Updated by Martynas Jusevicius over 2 years ago

I realized just now that Google Maps' google.maps.MVCObject.addListener() is not the same as EventTarget.addEventListener(), so maybe this approach is not generic after all...

Actions #2

Updated by Martynas Jusevicius over 2 years ago

So I got the following working, but I cannot see how it could be generalized since addListener() is GMaps-specific :/ If the addGoogleMapsListener could also be implemented in IXSL, it could at leaset be used to create a native GMaps API wrapper for Saxon-JS.

var addGoogleMapsListener = function(object, type, options, templateName, params)
{
    object.addListener(type, 
        function (event)
        {
            SaxonJS.transform({
                "stylesheetLocation": contextUri + "static/com/atomgraph/linkeddatahub/xsl/client.xsl.sef.json",
                "initialTemplate": templateName,
                "templateParams": Object.assign(params, { "event": event })
            });
        },
        options);
};
<xsl:variable name="params" select="map{ 'url': string(@rdf:about) }" as="map(xs:string, xs:string)"/>
<xsl:variable name="params-obj" select="ixsl:call(ixsl:window(), 'JSON.parse', [ $params => serialize(map { 'method': 'json' }) ])"/>
<xsl:sequence select="ixsl:call(ixsl:window(), 'addGoogleMapsListener', [ $marker, 'click', (), 'onInfoWindowLoad', $params-obj ])[current-date() lt xs:date('2000-01-01')]"/>
<xsl:template name="onInfoWindowLoad">
    <xsl:param name="event"/>
    <xsl:param name="url" as="xs:string"/>
    
    <xsl:sequence select="ixsl:call(ixsl:window(), 'alert', [ 'onInfoWindowLoad' ])"/>
</xsl:template>
Actions #3

Updated by Martynas Jusevicius over 2 years ago

onInfoWindowLoad in all the examples should have been onMarkerClick...

Actions #5

Updated by Norm Tovey-Walsh over 1 year ago

  • Sprint/Milestone set to SaxonJS 3.0
  • Fix Committed on JS Branch deleted (2)
Actions #6

Updated by Martynas Jusevicius over 1 year ago

I think now I have a better idea of what I wanted to describe here. Not an instruction, but an IXSL function ixsl:template-call that returns a JS function that calls an XSL template.

The body of ixsl:template-call would be equivalent to:

function(stylesheetLocation, initialTemplate, stylesheetParams, templateParams, event)
{
    // event is bound as ixsl:event() here

    SaxonJS.transform({
        "stylesheetLocation": stylesheetLocation,
        "initialTemplate": initialTemplate,
        "stylesheetParams": stylesheetParams,
        "templateParams": templateParams
    });
};

That would allow using templates for JS listener callbacks. Registration:

<ixsl:set-property name="map" select="$map" object="$template-params"/>
<xsl:variable name="map-marker-onclick" select="ixsl:template-call(static-base-uri(), 'onMapMarkerClick', $stylesheet-params, $template-params)"/>
<xsl:sequence select="ixsl:call($map, 'on', [ 'click', $map-marker-onclick ])[current-date() lt xs:date('2000-01-01')]"/>

Template body:

<xsl:template name="onMapMarkerClick">
    <xsl:param name="event" select="ixsl:event()"/>
    <xsl:param name="map"/>
    ...
</xsl:template>
Actions #7

Updated by Martynas Jusevicius over 1 year ago

This is how I use the JS function currently (called ixslTemplateListener):

<xsl:variable name="map-marker-onclick" select="ixsl:call(ixsl:get(ixsl:window(), 'ixslTemplateListener'), 'bind', [ (), static-base-uri(), 'onMapMarkerClick', $stylesheet-params, $template-params ])"/>
<xsl:sequence select="ixsl:call($map, 'on', [ 'click', $map-marker-onclick ])[current-date() lt xs:date('2000-01-01')]"/>

Please register to edit this issue

Also available in: Atom PDF Tracking page