Playing around with XUL and SVG
I plan to continue the tutorial, sorry, just too busy.
Today, let’s play around with XUL, SVG, and JS.
Let’s make a std window with some SVG for fun.
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<window title="SVG Demo"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:html="http://www.w3.org/1999/xhtml"
id="svg_demo_window"
style="width: 100%; background-color: #CCCCCC;"
pack="stretch"
onload="loadUp();"
>
<script type="application/x-javascript" src="svg_demo.js" />
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://web.resource.org/cc/"
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:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg_demo">
<rect
style="fill:#3099c8;fill-opacity:0.44444442"
id="svg_rect"
width="532.09326"
height="271.95877"
rx="28.903818"
ry="26.823261" />
</svg>
</window>
And GIMP for Windows is crashing on me right now, so no screenshots. But you can see it here, at least what we have so far.
Where did I get that rect code, me who knows not SVG? Inkscape! Save the file and you can open it in a text editor and pull out the
Let’s add some text, and let’s do it dynamically. Y’know. Cuz that’s leet hacking skills. =)
Here’s the JS in full, then I’ll break it down to what my understanding of it is.
<pre>
// Define the SVG namespace
var svgns = "http://www.w3.org/2000/svg";
function loadUp() {
// Get the SVG element we're drawing to
var svg = document.getElementById("svg_demo");
// Let's fill in the text variable and create the text SVG node
var text = document.createTextNode("Hello SVG World!");
var text_element = document.createElementNS(svgns, "text");
// Let's set some basic attributes.
text_element.setAttribute("fill", "#FFFFFF");
text_element.setAttribute("style","font-size:16px;");
text_element.appendChild(text);
// Let's place the text in the center top...
// Let's find out the width of the text first.
text_element.setAttribute("x", "0");
text_element.setAttribute("y", "0");
// Let's add the element to the svg box
svg.appendChild(text_element);
// Now that it's added, we can center it.
var text_width = text_element.getComputedTextLength();
// Let's get the width of the svg rectangle it's going to appear in(Not technically in)
var rect = document.getElementById("svg_rect");
var width = rect.width.animVal.value;
var center = width/2; // Find the center of it
var halfwidth = text_width/2; // Half width of the text_element.
var start = center - halfwidth; // Where we should place the text element
// Get the start position of the svg element we're drawing to.
var x = svg.getAttribute("x");
var y = svg.getAttribute("y");
text_element.setAttribute("x", x + start);
text_element.setAttribute("y", y + 15);
}
</pre>
You’ll get this result from the above code combos.
Neat, eh?
Make sense? Maybe a little.
Let’s break it down part by part.
<pre>
// Define the SVG namespace
var svgns = "http://www.w3.org/2000/svg";
function loadUp() {
// Get the SVG element we're drawing to
var svg = document.getElementById("svg_demo");
// Let's fill in the text variable and create the text SVG node
var text = document.createTextNode("Hello SVG World!");
var text_element = document.createElementNS(svgns, "text");
</pre>
First we create a var to hold the svg namespace address so we don’t have to type it each time. Next, when loadUp is called anyways, we assign the svg element in our xul file to the svg variable. Next we create a text node that says “Hello SVG World!”, which we’ll assign to text_element later. Because of XML magic, etc.. it has to be a text node. After that, instead of the normal xul document.createElement we createElementNS passing the svgns as the first parameter. Using this we can create any svg element.
<pre>
// Let's set some basic attributes.
text_element.setAttribute("fill", "#FFFFFF");
text_element.setAttribute("style","font-size:16px;");
text_element.appendChild(text);
</pre>
Here we’re just setting some basic attributes to the text element. Fill is just the color of the text, style is the CSS attributes. Then we assign the textnode we created earlier to the text element.
Here’s where it gets fun(meh, a little).
<pre>
// Let's place the text in the center top...
// Let's find out the width of the text first.
text_element.setAttribute("x", "0");
text_element.setAttribute("y", "0");
// Let's add the element to the svg box
svg.appendChild(text_element);
// Now that it's added, we can center it.
var text_width = text_element.getComputedTextLength();
</pre>
We set x and y attributes of the text element to 0 for sanity’s sake. I think this isn’t necessary at all though. Hmm.. Then we append it to our svg element that we’re working with, then when compute the width of the text. Simple enough, eh?
<pre>
// Let's get the width of the svg rectangle it's going to appear in(Not technically in)
var rect = document.getElementById("svg_rect");
var width = rect.width.animVal.value;
var center = width/2; // Find the center of it
var halfwidth = text_width/2; // Half width of the text_element.
var start = center - halfwidth; // Where we should place the text element
</pre>
Next we’ll get the svg_rectangle and figure out it’s width using the cryptic rect.width.animVal.value. Huh? Well, it’s support, somehow, for animated SVG and figuring out the width or something. I have no idea. Next we do some basic math to place it appropriately.
<pre>
// Get the start position of the svg element we're drawing to.
var x = svg.getAttribute("x");
var y = svg.getAttribute("y");
text_element.setAttribute("x", x + start);
text_element.setAttribute("y", y + 15);
</pre>
We get the x,y coordinates of our SVG element, in case it’s placed somewhere else on the page. We could probably grab it from the rectangle would be even better and more accurate, but for now it just works, ok? Then we set the x coordinates to center the text, and the y to move it down slightly so it looks like a title or a header.
Alright, well that’s all nice and good. Let’s create the rectangle instead of specificying it manually.
<pre>
function createRectangle(svg) {
var rect = document.createElementNS(svgns, "rect");
rect.setAttribute("id", "svg_rect");
rect.setAttribute("width", "500");
rect.setAttribute("height", "300");
rect.setAttribute("rx", "30");
rect.setAttribute("ry", "30");
rect.setAttribute("style", "fill:#3099c8;fill-opacity:0.44444442");
svg.appendChild(rect);
return(rect);
}
</pre>
And let’s change the loadUp function to start like this:
<pre>
function loadUp() {
// Get the SVG element we're drawing to
var svg = document.getElementById("svg_demo");
var rect = createRectangle(svg);
</pre>
Go ahead and delete the rect element from the XUL file. You’ll end up with this result. I rounded some of the rectangle variables as I’m sure you’ve noticed.
In fact, let’s add a createText function and a change the loadUp function. You’ll end up with this:
<pre>
// Define the SVG namespace
var svgns = "http://www.w3.org/2000/svg";
function loadUp() {
// Get the SVG element we're drawing to
var svg = document.getElementById("svg_demo");
var rect = createRectangle(svg);
var text = createText(svg, "Hello New Functional World!");
centerTextToRect(text, rect);
}
function createRectangle(svg) {
var rect = document.createElementNS(svgns, "rect");
rect.setAttribute("id", "svg_rect");
rect.setAttribute("width", "500");
rect.setAttribute("height", "300");
rect.setAttribute("rx", "30");
rect.setAttribute("ry", "30");
rect.setAttribute("style", "fill:#3099c8;fill-opacity:0.44444442");
svg.appendChild(rect);
return(rect);
}
function createText(svg, display) {
var text = document.createTextNode(display);
var text_element = document.createElementNS(svgns, "text");
text_element.setAttribute("fill", "#FFFFFF");
text_element.setAttribute("style","font-size:16px;");
text_element.appendChild(text);
svg.appendChild(text_element);
return(text_element);
}
function centerTextToRect(text, rect) {
var text_width = text.getComputedTextLength();
var width = rect.width.animVal.value;
var center = width/2; // Find the center of it
var halfwidth = text_width/2; // Half width of the text_element.
var start = center - halfwidth; // Where we should place the text element
// Get the start position of the svg element we're drawing to.
var x = rect.getAttribute("x");
var y = rect.getAttribute("y");
text.setAttribute("x", x + start);
text.setAttribute("y", y + 15);
}
</pre>
Much much better. In fact, you can actually repeat the last 3 lines of loadUp to create another rectangle and more text.
<pre>
function loadUp() {
// Get the SVG element we're drawing to
var svg = document.getElementById("svg_demo");
var rect = createRectangle(svg);
var text = createText(svg, "Hello New Functional World!");
centerTextToRect(text, rect);
var rect = createRectangle(svg);
var text = createText(svg, "Hello New Functional World!");
centerTextToRect(text, rect);
}
</pre>
Wait, where’s the new rectangle and the new text? Why is it darker? That’s because they are overlaid on top, and with opacity, you see part of the rectangle below. Hmm, we should really move the second rectangle down.
<pre>
// Define the SVG namespace
var svgns = "http://www.w3.org/2000/svg";
function loadUp() {
// Get the SVG element we're drawing to
var svg = document.getElementById("svg_demo");
var rect = createRectangle(svg);
var text = createText(svg, "Hello New Functional World!");
centerTextToRect(text, rect);
var height = rect.height.animVal.value;
var rect = createRectangle(svg);
rect.setAttribute("y", height + 10);
var text = createText(svg, "Hello New Functional World!");
centerTextToRect(text, rect);
}
function createRectangle(svg) {
var rect = document.createElementNS(svgns, "rect");
rect.setAttribute("width", "500");
rect.setAttribute("height", "300");
rect.setAttribute("rx", "30");
rect.setAttribute("ry", "30");
rect.setAttribute("x", "0");
rect.setAttribute("y", "0");
rect.setAttribute("style", "fill:#3099c8;fill-opacity:0.44444442");
svg.appendChild(rect);
return(rect);
}
function createText(svg, display) {
var text = document.createTextNode(display);
var text_element = document.createElementNS(svgns, "text");
text_element.setAttribute("fill", "#FFFFFF");
text_element.setAttribute("style","font-size:16px;");
text_element.appendChild(text);
svg.appendChild(text_element);
return(text_element);
}
function centerTextToRect(text, rect) {
var text_width = text.getComputedTextLength();
var width = rect.width.animVal.value;
var center = width/2; // Find the center of it
var halfwidth = text_width/2; // Half width of the text_element.
var start = center - halfwidth; // Where we should place the text element
// Get the start position of the svg element we're drawing to.
var x = parseInt(rect.getAttribute("x"));
var y = parseInt(rect.getAttribute("y"));
text.setAttribute("x", x + start);
text.setAttribute("y", y + 15);
}
</pre>
That’s our new code. Pretty nice, eh?
You can see it for yourself here.
That should get you started on the SVG in XUL path. I hope this helped. I couldn’t find too much, just a very cool example of a XUL/SVG Outlook style calendar.
Oh, the isNull function was taken blatantly from Remedial Javascript.
Comments
Comment from Joseph
Time: April 30, 2006, 1:43 pm
test

Write a comment