javascriptxsltsaxon-js

Calling Saxon-JS function or template from JavaScript


I use C3 to render graphs in an HTML page that uses Saxon-JS and a (compiled, SEF) XSL stylesheet. C3 generates an SVG image (a bar chart), and provides a possibility to respond to click events on parts of the chart (e.g., a bar), with an onclick function.

c3.generate(
  { bindto: ...
  , data:
    { type: 'bar'
    , columns: c3_columns
    , onclick: function(data, element) { ... }
    }
    ...
  })

I would like to process the click event in my XSL stylesheet. However:

This scenario is a bit complex, but I will include two simplified code samples that hopefully show what I am trying to do.

In the Javascript I have the onclick handler.

function chartClickHandler(data, element) {
  // Show the contents of the data.
  console.log(`Click on chart`);
  Object.entries(data).forEach(([k, v]) => console.log(`  ${k} : ${v}`));
  // Send a custom event.
  const event = new CustomEvent('chartclick', data);
  document.getElementsByTagName('body')[0].dispatchEvent(event);
  // Call a function in the XSL stylesheet.
  SaxonJS.XPath.evaluate(`chart:click('hello chartCLick')`, [],
    { namespaceContext: {'chart' : 'chart'}
    });
}

This is called from the HTML. For simplicity, I don't use C3, but:

<button onclick="chartClickHandler({x: 1, y: 2}, this)">test</button>

In the XSL stylesheet that is given to Saxon-JS I have:

  <xsl:template match="body" mode="ixsl:onchartclick">
    <xsl:if test="$show-debug-messages">
      <xsl:message>
        Chartclick event properties: <xsl:value-of select="ixsl:eval('Object.keys') => ixsl:apply([ixsl:event()])"/>
      </xsl:message>
    </xsl:if>
  </xsl:template>

  <xsl:function xmlns:chart="chart" name="chart:click">
    <xsl:param name="data"/>
    <xsl:message>
      Chart click data: <xsl:value-of select="serialize($data)"/>
    </xsl:message>
  </xsl:function>

As said before, all I get is

Click on chart debugger eval code:3:11
  x : 1 debugger eval code:4:52
  y : 2 debugger eval code:4:52
Uncaught 
Object { message: "Unknown function Q{chart}click()", stack: ...

This example still involves many parts, but the essence of my question is in the title: How to call a Saxon-JS function or template from JavaScript. Or how to intercept custom events.


Solution

  • As I said in a comment, I think to call a function in an SEF you should use SaxonJS.transform({ initialFunction : 'Q{namespace-uri}function-name', functionParams: [function arguments here], ..}, ..).

    Example is e.g. https://martin-honnen.github.io/xslt/2022/xsltFunctionCallTest2.html

    Content:

    <html lang="en">
      <head>
        <title>Saxon-JS 2 test</title>
        <script src="../../Saxon-JS-2.3/SaxonJS2.rt.js"></script>
        <script>
        var internalStylesheet;
        document.addEventListener('DOMContentLoaded', e => {
          var options = { 
            stylesheetLocation : 'xsltFunctionCallTest1.xsl.sef.json',
            sourceLocation: 'sample1.xml',
            destination: 'appendToBody'
          };
          SaxonJS.transform(options, 'async').then(result => { internalStylesheet = result.stylesheetInternal; console.log(internalStylesheet); console.log(result); });
        });
        </script>
        <script>
          function functionCallTest1() {
           SaxonJS.transform({ destination: 'raw', stylesheetLocation : 'xsltFunctionCallTest1.xsl.sef.json', initialFunction: 'Q{http://example.com/mf}f1', functionParams: [ { x: 1, y: 'foo' }] }, 'async').then(result => console.log(result.principalResult)); 
          }
        </script>
      </head>
      <body>
        <h1>Test</h1>
      </body>
    </html>
    

    where the first transform generates e.g.

    <section>
      <h2>Test</h2>
      <p>Run with {system-property('xsl:product-name')} {system-property('xsl:product-version')} {system-property('Q{http://saxon.sf.net/}platform')}</p>
      <input type="button" value="test"
        onclick="functionCallTest1();"/>
    </section>
    

    Caveat: in the XSLT, make sure you have visibility="public" on the XSLT xsl:function declared.