How to make an svg widget?

19 views
Skip to first unread message

uuh

unread,
Jun 25, 2009, 5:17:10 AM6/25/09
to weblocks
Hi,

I am trying to render an SVG file as a widget. This seems to work ok
(SVG portability magic aside) as long
as I am rendering a static svg file that lives below the /pub/
hierarchy and is include via an <object> tag in
the HTML (say, inside a composite widget).

What I want to do is call back to the weblocks session from the SVG:
onclick events should tell the application
which node was clicked. I understand that a static destination url for
such clicks is not compatible with the
weblocks view of things. I want to avoid inlining the SVG (which would
probably allow me to use dynamic
url as render-link generates them) for portability problems (I have to
cater to MSIE).

What is a suggested solution?
Does anyone have a magic svg widget?

Utz

Leslie P. Polzer

unread,
Jun 25, 2009, 6:33:04 AM6/25/09
to webl...@googlegroups.com

uuh wrote:

> I understand that a static destination url for
> such clicks is not compatible with the
> weblocks view of things. I want to avoid inlining the SVG (which would
> probably allow me to use dynamic url as render-link generates them)
> for portability problems (I have to cater to MSIE).
>
> What is a suggested solution?

You need to serve your SVG file dynamically.

Convenient support for this isn't in Weblocks yet,
but you can use a dispatcher.

Example:

(defun my-svg-handler ()
(setf (hunchentoot:content-type) "image/svg")
"<svg/>")

(push (hunchentoot:create-prefix-dispatcher
"/pub/svg/my-svg"
#'my-svg-handler)
hunchentoot:*dispatch-table*)

You can of course render your widget in MY-SVG-HANDLER
as well, and dynamically generate your onclick JS.

Leslie

uuh

unread,
Jun 25, 2009, 6:49:38 AM6/25/09
to weblocks
Hi Leslie,
On Jun 25, 12:33 pm, "Leslie P. Polzer" <s...@viridian-project.de>
wrote:
> uuh wrote:
> You need to serve your SVG file dynamically.

Ok, thanks for the code snipped too.

Where in this scheme do I get access to a session handle? Lets say, to
the widget instance
whose render-body method inserts the reference to the svg file?

Ascii art:
fancy-widget (composite)
buttons
image area (an SVG object)
status text

Imagine the SVG wants to store the coordinates of the onclick event in
fancy-widget, so that
that gets redrawn and status text updated to say 'you clicked at
(x,y)'

Maybe I'm blocked here and it just magically works if? Should I encode
a token into the SVG's url
to have it available in the my-svg-handler you suggested?

Utz

nunb

unread,
Jun 25, 2009, 7:46:53 AM6/25/09
to weblocks

Are you writing simple :onclick "foobar();" snippets or a full
function? If the latter, this is what I do, for example, with google
maps, and anywhere I want to call Weblocks actions from within
Javascript. It's based on make-action* by Leslie.

Snippet at http://paste.lisp.org/display/82484

Basically, it populates a hash in Javascript, and uses the hash-name
as a parameter to make-action-string-with-args.

The example m-send-location-chooser-js is a JS function called when
the user clicks on a google maps geocoded result link, and it passes
those values (lat, long etc) into CLOS objects.

I am not so familiar with svg files, are you not able to embed them
inside (with-html (:object ..)) etc. ?

nunb

unread,
Jun 25, 2009, 7:57:13 AM6/25/09
to weblocks
And if I understand you correctly, you want to put an svg file in /
pub/ *and* have it parameterized for weblocks actions?
(then, eg, a proxy like nginx could replace the /pub/foo.svg access
with something else for MSIE).

In that case, perhaps you could parameterize the svg file itself?

I use parameterized JS with html-template in preference to format's
~As

(send-script
(with-string-stream
(html-template::fill-and-print-template
"YAHOO.util.Event.on('<!-- tmpl_var map-button -->', 'click', function
() { dialog<!-- tmpl_var dialog-id -->.show();
//insert_api_key(); //map_initialize(); //map_first_init();
if (YAHOO.env.ua.opera && document.documentElement) {
document.documentElement.style += ''; // Opera needs to force a
repaint
} });"
(list :dialog-id dialog-id :map-button map-button )
:stream *stream*)
))

But this could easily be:

(html-template::fill-and-print-template
"some-svg-file.svg"
(list :svg-param-1 xml-is-cool :svg-param-2 (make-action-string-with-
args (f_% "do-stuff")) )
:stream *weblocks-output-stream*))

where some-svg-file.svg:

<xml <supercool xml schema dtd>
<!-- tmpl_var svg-param-1 -->
<define javascript onclick="<!-- svg-param-2 --> >
<circle> <square>
>

Of course, it'd bork an xml parser :-)

uuh

unread,
Jun 25, 2009, 8:13:24 AM6/25/09
to weblocks
Hi,

thanks for the hint in the last message; I'll see what I can roll from
that.

Basically, templating is out of the question, as I's have to
substitute
a few hundered different links/actions in that case. I had hoped to be
able to
generate a session-specific version of a destination-url like
http://my-host/noticeclick?targetid=4711
that I can call from an onclick="sendToServer(evt)" event, where
sendToServer() is a JS method which calls to weblocks, weblocks does
something with the
information, and based on the result we might need to update a few
attributes in the SVG too.

The right thing(tm) would maybe be to have the SVG delivered
dynamically, with a JS-variable
containing the session-specific url destination for the above being
templated in at that time. However,
I still don't see how to tweak that into Leslie's suggested
hunchentoot-prefix handler.

Utz

Leslie P. Polzer

unread,
Jun 25, 2009, 8:18:56 AM6/25/09
to webl...@googlegroups.com

uuh wrote:

> Where in this scheme do I get access to a session handle? Lets say, to
> the widget instance whose render-body method inserts the reference to
> the svg file?

Using -dev you could use FIND-WIDGET-BY-ID.


> Maybe I'm blocked here and it just magically works if?

No magic here, you're basically walking uncharted territory. :)


> Should I encode a token into the SVG's url to have it available
> in the my-svg-handler you suggested?

Yes, that's a good way to do it. I recommend it over the FIND-WIDGET
approach above.

nunb

unread,
Jun 25, 2009, 9:17:04 AM6/25/09
to weblocks
> a few hundered different links/actions in that case. I had hoped to
be able to
> generate a session-specific version of a destination-url like
>  http://my-host/noticeclick?targetid=4711

If there are so many actions, then mux them into a muxer-fn and use
flags for the actions, eg

onclick="mux(1);"
onclick="mux(2);"

etc.

Perhaps I don't understand your line of thinking, but I am at a loss
as to what /noticeclick?targetid=foo buys you.

As Lelie said, this can easily be done with a custom url handler (note
that calling it will spit out the updated weblocks widget tree in the
output stream).

> that I can call from an onclick="sendToServer(evt)" event, where
> sendToServer() is a JS method which calls to weblocks, weblocks does
> something with the

If you want a static svg, put in onclick="myspecialfn()" where you
need some interaction, then

(defmethod render-widget-body ((widget contains-svg) &rest args)

(send-script (format nil "function my-special-fn (argv) { /* code
that populates args_hash_js = {key1:foo key2:bar}; */ ; ~A ; } "
(make-action-string-with-args
(lambda (&key key1 key2)
"lambda can access everything in
the current session, incl. the widget whose render-widget-body this is
in.")
:args "args_hash_js" ))
;; any of these:
(with-html (:object (:xml ....)))
(slurp-bar-file "/pub/some.svg)
(template-file "/pub/some.svg" :var1 "foo") )

Depending on how complex you make my-special-fn you can embed all the
various action-options into it, similar to muxer-fn above.

> information, and based on the result we might need to update a few
> attributes in the SVG too.

afaik, this is impossible with a static svg, right?

this is easy to do if your svg is embedded inside a render-widget-body
(even allowing for large static chunks of it to be pulled in through
templates/format strings etc). html-template is probably more
efficient than format.

> The right thing(tm) would maybe be to have the SVG delivered
> dynamically, with a JS-variable
> containing the session-specific url destination for the above being
> templated in at that time.

Absolutely. Especially if you want the SVG to change based on the
input. The rough-cut above addresses exactly this scenario, if I'm not
mistaken.

I still don't get the need for "the session-specific url destination"
though :-P

> However,
> I still don't see how to tweak that into Leslie's suggested
> hunchentoot-prefix handler.

nope, still don't get it. perhaps you could restate the case for this?

nunb

unread,
Jun 25, 2009, 10:00:15 AM6/25/09
to weblocks

> As Lelie said, this can easily be done with a custom url handler (note
> that calling it will spit out the updated weblocks widget tree in the
> output stream).

sorry, this is incorrect.

Ian Eslick

unread,
Jun 25, 2009, 3:24:59 PM6/25/09
to webl...@googlegroups.com
This is off the cuff, and I'm likely to be missing subtle issues, but
I believe permanent actions are still supported via def-permanent-
action (or something similar). Since the handler executes in the
session context, you can store a reference to the active widget
(assuming only 1 such widget active per session) in the session table.
If you pass a set of parameters to the handler function (embedded in
your SVG file) that could then perform side effects on the widget
reachable from the current session table.

If you want the SVG itself to change in response to a click via a
server side response - you'll either need to run a javascript handler
that interacts with it or ship out a new file (or generate a new file)
in your response.

I'm not sure this addresses all the issues, but hopefully it's a
helpful scenario to think about.

Ian

uuh

unread,
Jun 25, 2009, 4:50:04 PM6/25/09
to weblocks
Hi Ian,


On Jun 25, 9:24 pm, Ian Eslick <esl...@media.mit.edu> wrote:
> I believe permanent actions are still supported via def-permanent-
> action (or something similar).  Since the handler executes in the  
> session context, you can store a reference to the active widget  
> (assuming only 1 such widget active per session) in the session table.

Great! That's exactly what I was looking for. You are correct in
assuming that there is only one such widget active per session.
 
> If you want the SVG itself to change in response to a click via a  
> server side response - you'll either need to run a javascript handler  
> that interacts with

That's what I will do -- I have a simulation of that (without the
server reply :)) readily prepared.

> it or ship out a new file (or generate a new file)  
> in your response.

Re-shipment is ugly and will lead to flickering/minor realignment
issues (at least on opera). I just want to avoid keeping state in two
places (and risk getting things out of sync) by only modifying the
visual part (SVG) after a server reply indicates that the session data
knows what the user did.

> I'm not sure this addresses all the issues, but hopefully it's a  
> helpful scenario to think about.

This is very close to a solution I believe. I will see what I come up
with tomorrow and post some solution snippet here if I have one.


Thanks,
Utz

uuh

unread,
Jun 25, 2009, 4:59:23 PM6/25/09
to weblocks
Hi,

I like the idea of Ian, so I will try that first.

However, I'll answer some questions you raised just for the record
here:


On Jun 25, 3:17 pm, nunb <nandan.bagc...@gmail.com> wrote:
> If you want a static svg, put in onclick="myspecialfn()" where you
> need some interaction, then
>
> (defmethod render-widget-body ((widget contains-svg) &rest args)
>
>   (send-script (format nil "function my-special-fn (argv) { /* code
> that populates args_hash_js = {key1:foo key2:bar}; */ ; ~A ; } "
>                        (make-action-string-with-args
>                                (lambda (&key key1 key2)
>                                      "lambda can access everything in
> the current session, incl. the widget whose render-widget-body this is
> in.")
>                       :args "args_hash_js" ))

I assume that send-script will populate the Javascript namespace of
the target xhtml tree; since
I do not want to inline the SVG (portability issues with IE; if you
want check out info at wiki.svn.org) the SVG javascript namespace is
separate, so the function will not be visible to the SVG.


> > information, and based on the result we might need to update a few
> > attributes in the SVG too.
>
> afaik, this is impossible with a static svg, right?

In a static SVG file you can have JS code that handles events and
modifies the DOM tree of the SVG,
with automatic redraw by the browser. The SVG file delivered to the
client is 'static' to the server,
but the SVG DOM tree is dynamic in the client. This is exactly where I
want to get some synchronization
to the server in.

> this is easy to do if your svg is embedded inside a render-widget-body
> (even allowing for large static chunks of it to be pulled in through
> templates/format strings etc). html-template is probably more
> efficient than format.

Yes, it would all be easy if the SVG is a subtree of the XHTML DOM
tree in the browser.


> I still don't get the need for "the session-specific url destination"
> though :-P

This was my error: apparently I automatically get a reference through
hunchentoot:*session* in any sensible handler. Just like allegroserve
used to provide :) Maybe this is not MVC-compliant but is what I was
looking for. I'll store the interesting widget's CLOS instance in a
session variable and that will suffice.


Utz

nunb

unread,
Jun 26, 2009, 4:15:30 AM6/26/09
to weblocks

>
> Yes, it would all be easy if the SVG is a subtree of the XHTML DOM
> tree in the browser.
>

Ah, I did not know that, thanks.

> > I still don't get the need for "the session-specific url destination"
> > though :-P
>
> This was my error: apparently I automatically get a reference through
> hunchentoot:*session* in any sensible handler. Just like allegroserve
> used to provide :) Maybe this is not MVC-compliant but is what I was
> looking for. I'll store the interesting widget's CLOS instance in a
> session variable and that will suffice.

Yup. It would also be a nice thing to have a place to store these tips
that are not quite manual-worthy.
Reply all
Reply to author
Forward
0 new messages