Interactive SVG using <object>?

I’m trying to embed an interactive, SVG-based, data visualization. I’m generating SVG from a separate system, (but still in the same second-level domain as my Discourse installation.) I’ve tried bringing the SVG in using an OBJECT tag…

<object type="image/svg+xml" data="full_URL_to_foo.svg">
(appears if object load fails)
</object>

If I simply bring that SVG (it’s dynamically generated each time) in with an IMG tag, the visual appears, but (as I expected) there’s no interactivity…

<img src="full_URL_to_foo.svg">

Thoughts or pointers where I can buy some clues re SVGs and Discourse? :slight_smile:

1 Like

The easier way to get this working is using an iframe. Add the SVG domain to the allowed_iframes site setting and add it to a post using HTML.

5 Likes

hmmmm, so that’s the easy way.

Is there a hard way I can try?

…because I want to have links in the SVG that change the browser location. Links in the SVG, in an IFRAME, change what’s shown in the IFRAME . . .

I suspect that embedding j/s in the topic, just before the IFRAME, and calling a j/s function in the page onclick in the SVG… that’s not going to work either.

Am I making sense? Should I be adding some thin j/s code ala Mitigate XSS Attacks with Content Security Policy and then trying to call that from within the SVG in the IFRAME? …or is “in the IFRAME” an airtight fortress?

Maybe I should be asking how I can directly inline the SVG into the page? Then use theme-component injected j/s to interact with my other server to dynamically update the SVG?

Cluebat please :slight_smile:

2 Likes

Putting the svg in the post marked by a sorrounding special div and using a theme-component to make it into an object tag using the decorateCooked callback may work. Check Developer’s guide to Discourse Themes

3 Likes

…in case anyone is following along, decorateCooked() is deprecated. I’m fiddling with decorateCookedElement() instead.

1 Like

I’m up against CORS/Access-Control-Allow-Origin. I’m not familiar with how that works…

I have two sites—the Hosted Discourse site (forum.moversmindset.com) and an Apache-based Wordpress site (moversmindset.com). Note that there’s no obvious content on the Wordpress site—it’s all generating RSS feeds, serving media, etc. If you go to the domain, it’ll just send you to the forum.

I have a directory, that serves SVG-type replies to GET request. For example (not the real url) https://moversmindset.com/foo/bar.php

In my Discourse theme I’m experimenting with script code. (eventually to be a proper plugin) It calls api.decorateCooked() on particular DIVs that have some data-custom added to them. So inside the function called by decorateCooked() I’m doing what amounts to

$.get(‘https://moversmindset.com/foo/bar.php’ … bla bla blah

So I want to retrieve the SVG and then append it to the DOM. But my browser error console says:

Question:

Does that mean I have to configure CORS on the Discourse installation or over on the Apache/Wordpress?

I do have https://moversmindset.com configured as allowed in CORS on Discourse.

2 Likes

I think it’s on this side. Can you take a look at Access-Control-Allow-Origin - HTTP | MDN if you haven’t already and see if that gives you any clues?

4 Likes

teeny tiny steps, but YES!

I had to add an Access-Control-Allow-Origin header on the apache/WP server. That made the j/s (sourced from the Discourse platform) happy. Thanks.

3 Likes

I’ve continued to slowly figure this out. This requires a lot of setup to ask my question:

SVG

I have a web server that generates SVG. For this question, it generates a very simple test SVG…

<svg xmlns="http://www.w3.org/2000/svg" stroke-linejoin="round" viewBox="0 0 100 100">
<path d="M50,4L4,50L50,96L96,50Z" stroke="#40638C" stroke-width="3"></path>
<path d="M50,5L5,50L50,95L95,50Z" stroke="#333" fill="#40638C" stroke-width="3"></path>
<path d="M37,42c-1,0,11-20,13-20c1,0,15,20,13,20h-9c0,8,9,22,12,25l-4,4l-8,-7v13h-10v-35z" stroke="#40495E" fill="#40495E"></path>
<path d="M35,40c-1,0,11-20,13-20c1,0,15,20,13,20h-9c0,8,9,22,12,25l-4,4l-8,-7v13h-10v-35z" stroke="#333" fill="#555"></path>
</svg>

It’s just a fancy-looking “merge” sign. Note that it has four PATH elements.

Inlining

To get the SVG into a post, I’m using some javascript added via my theme.

The end-goal would be to make a proper plugin. But I’m just trying to build a proof-of-concept. So it’s simply pasted into the </head> section of my theme customization:

<script type="text/discourse-plugin" version="0.8">
var UMB = {
    svgload: function(base, target) {
        var url = base + $(target).text();
        $(target).html('');
        $.ajax({
            method: "GET",
            url: url,
            async: false,
            dataType: "text",
            success: function(data) { $(target).append(data); }
        });
        alert('loaded!');
        $(target).children('path').each(function(){alert('here is a path element');});
    },
}
$.fn.umbdv = function() {
    this.each(
        function() {
            UMB.svgload('__URL_REDACTED__', this);
        }
    );
    return this;
};
api.decorateCooked(
  $elem => $elem.children('.cooked div[data-custom="umbdv"]').umbdv(),
  { id: 'umbdv' }
);
</script>

Where…

var UMB = { is simply a global variable that avoids me having giant anonymous functions all over the place.

$.fn.umdv = is [what I think is called] a “plugin” extending JQuery.

api.decorateCooked( let’s me manipulate the post before it’s sent to the browser.

Incantation

In a topic, I then write…

<div data-custom="umbdv">/vtest</div>

UMB.svgload('__URL_REDACTED__', this) is called for that DIV.

UMB.svgload() correctly groks that /vtest string, composes a url and makes the AJAX request. It successfully does the append(data) and *boop* my SVG is inline…

My alert() inside UMB.svgload() then fires, just as expected. (It’s obviously a debug hack, right? :)

The question (finally)

I have a STAFF ONLY topic in my Discourse hosted forum where you can see this in action. (I’m talking to Discourse staff/support who can barge into my installation as Admin users.)

https://forum.moversmindset.com/t/svg-experimentation-in-progress/1109

Why doesn’t…

$(target).children('path').each(function(){alert('here is a path element');});

…select any of the PATH elements?

It does nothing—no errors. Nothing.

Next

Where I’ll be going if I get this trivial alert() proof-of-concept working…

I’m aware that the chunk of the DOM I’m “working with” is not yet connected to the actual document DOM (at the point where UMB.svgload() is called.) That’s why I expect $(target)… to be what I need.

Ultimately, I’m inlining vastly more complex SVG and I’ll need to use more complex JQuery selectors. I want to find many elements inside $(target) and attach event handlers (onclick for example) that will be calling other UMB.… global functions.

1 Like

Iframes can change top level navigation if you want to

http://w3c-test.org/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation_by_user_activation-manual.html

3 Likes

Thanks for the tip!

But definitely preferring the SVG inline now.

aaaaaanyway. Weeks later. This doesn’t work.

Doesn’t seem possible to manipulate the SVG DOM elements once it’s inlined to the main document. So I cannot figure out how to add event triggers/actions (which one could easily do as onclick et al if the SVG is loaded in an iframe)
((not using an iFrame was my whole goal.))

¯\_(ツ)_/¯

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.