0

Years back I wrote some code to automatically include some javascript code inside html pages delivered by an http server. The approach is documented here in a blog entry and in this StackOverflow question. The solution served me well over the last years.

The approach in two sentences:

  • for every full html page delivered by the http server some placeholder is added to the bottom of the page by means of server side substitution
  • that placeholder again is replaced by the javascript code to be included by means of server side inclusion

Now I wanted to extend that solution and apply the same logic to pages based on SVG instead of HTML as document format. Note: not SVGs included in html pages, but pages in SVG format to be directly referenced by links.

The extension itself was not an issue, the javascript code is included inside the SVG markup. The issue is that the code is not executed. Unfortunately I have little knowledge of scripting in SVG which is why I am somewhat lost. Maybe someone can give me a hint what is wrong here :-)

The filter chain as included in various apache vhosts definitions:

# ----------
# internal requests to include the piwik tracking code at the bottom of every html page

FilterDeclare PIWIK_token
FilterProvider PIWIK_token SUBSTITUTE resp=Content-Type $text/html
SUBSTITUTE 's|^\s*</body>|<!--#include virtual="/piwik"--></body>|iq'
FilterProvider PIWIK_token SUBSTITUTE resp=Content-Type $image/svg+xml
SUBSTITUTE 's|^\s*</svg>|<!--#include virtual="/piwik"--></svg>|iq'

FilterDeclare PIWIK_code
FilterProvider PIWIK_code INCLUDES resp=Content-Type $text/html
FilterProvider PIWIK_code INCLUDES resp=Content-Type $image/svg+xml

FilterChain PIWIK_token PIWIK_code

# map virtual request to the file system
Alias /piwik /srv/www/internal/piwik.php

# all that is left for the virtual host is to do two things:
#        SetEnv PIWIK_ID 15
#        Include /etc/apache2/vhosts.d/_internal.inc
# note: the '15' above is an example piwik site id

The javascript code to be included is a slightly modified version of the piwik tracking snippet:

<?php
define('piwikBase','https://some.domain.xxx/stats/');
define('piwikSite',apache_getenv('PIWIK_ID'));
if(is_numeric(piwikSite)){
?>

<script type="text/javascript">
  var _paq = _paq || [];
  _paq.push(["trackPageView"]);
  _paq.push(["enableLinkTracking"]);

  (function() {
    var u="<?= piwikBase ?>";
    _paq.push(["setTrackerUrl", u+"piwik.php"]);
    _paq.push(["setSiteId", "<?= piwikSite ?>"]);
    var d=document, g=d.createElement("script"), s=d.getElementsByTagName("script")[0]; g.type="text/javascript";
    g.defer=true; g.async=true; g.src=u+"piwik.js"; s.parentNode.insertBefore(g,s);
  })();
</script>

<?php } else { ?>
<!-- invalid piwik site id: <?php echo piwikSite;?> -->
<?php } ?>

As said: the extension works, the javascript code is successfully included into the SVG documents. However it is apparently not activated and I failed to see the cause for that...

The final SVG as delivered looks like this:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg
    xmlns:dc="http://purl.org/dc/elements/1.1/"
    xmlns:cc="http://creativecommons.org/ns#"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:svg="http://www.w3.org/2000/svg"
    xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    version="1.2"
    width="100%"
    height="100%"
    viewBox="0 0 640 400"
    docname="somedocument.svg">

[... content of the SVG ...]

<script type="text/javascript">
  var _paq = _paq || [];
  _paq.push(["trackPageView"]);
  _paq.push(["enableLinkTracking"]);

  (function() {
    var u="https://some.domain.xxx/stats/";
    _paq.push(["setTrackerUrl", u+"piwik.php"]);
    _paq.push(["setSiteId", "15"]);
    var d=document, g=d.createElement("script"), s=d.getElementsByTagName("script")[0]; g.type="text/javascript";
    g.defer=true; g.async=true; g.src=u+"piwik.js"; s.parentNode.insertBefore(g,s);
  })();
</script>

</svg>
Community
  • 1
  • 1
arkascha
  • 41,620
  • 7
  • 58
  • 90
  • Problem seems to be that dynamically created and appended `script` element in SVG does not load (? well … what does your network console say?) and/or is not executed. It sounds strange, because it seems [1] that doing the same thing in HTML embedded SVG *from outside* works well. [1] http://stackoverflow.com/a/16626699/540955 – myf Jun 17 '15 at 18:02
  • @myf Cannot follow here... From the browsers point of view the script is _not_ created dynamically. It is part of the document that is delivered. So obviously it also is not loaded in an asynchronous manner which might appear in the networking console, since it is already included on the server side. – arkascha Jun 17 '15 at 18:04
  • 1
    Ah, let me clear that: that `script` with those `_paq.push`es creates new `script` element (see, `document.createElement('script')` ?) and `append`s it into your SVG document, before itself, being _static originator_ (or how to call that): you can inspect the DOM and you'll see two scripts in your SVG. That's what I meant by _dynamically created_. So you should see `https://some.domain.xxx/stats/piwik.js` or whatever was its `xlink:href` (NOT `src`) in your network console. – myf Jun 17 '15 at 18:26
  • This last comment put me onto what's wrong, but it's been in the question all along, I just didn't spot it. Sorry for asking for all those irrelevant things. – Robert Longson Jun 17 '15 at 20:43

2 Answers2

1

Just guess, but I'd try to change the code so dynamic script injection is replaced with inline script tag with xlink:href (I've tested doing it dynamically as your code does with seemingly "more correct" createAttibuteNS('xlink','href','...') but result wasn't executed either)

Resulting code would look like this (haven't tested in real environment, so wath for typos):

<?php
define('piwikBase','https://some.domain.xxx/stats/');
define('piwikSite',apache_getenv('PIWIK_ID'));
if(is_numeric(piwikSite)){
?>
<script type="text/javascript"><![CDATA[
  var _paq = _paq || [];
  _paq.push(["trackPageView"]);
  _paq.push(["enableLinkTracking"]);
  _paq.push(["setTrackerUrl", "<?= piwikBase ?>piwik.php"]);
  _paq.push(["setSiteId", "<?= piwikSite ?>"]);    
]]></script>
<script type="text/javascript"
 xmlns:xlink="http://www.w3.org/1999/xlink"
 xlink:href="<?= piwikBase ?>piwik.js"
 defer="defer" async="async"></script>

<?php } else { ?>
<!-- invalid piwik site id: <?php echo piwikSite;?> -->
<?php } ?>

I'm not sure about those defer and async stuff, whether it has any imact in SVG. I've tested this with simple datauri sample and it seemed to work well.

Notes:

  • xmlns:xlink="http://www.w3.org/1999/xlink" on the script or some of it's parent is necessary, but most probably your SVG has it already
  • CDATA aren't necessary in this case, but if you plan to use < or & there in future … you know what.
myf
  • 9,874
  • 2
  • 37
  • 49
  • Hm, don't like that fact that this means to use different javascript snippets. But maybe it is required.... I will give it a try and report back. – arkascha Jun 17 '15 at 17:26
  • This works, thanks! I modified the apache filter chain to point to two separate script versions for html and svg documents. The html version stayed unchanged, the svg version follow your suggestion above. – arkascha Jun 18 '15 at 07:14
0

You can't create an SVG script element with

g=d.createElement("script")

you need to write

g=d.createElementNS("http://www.w3.org/2000/svg", "script")

Robert Longson
  • 118,664
  • 26
  • 252
  • 242