This extension provides a little more flexibility in importing an SVG
image into the HTML DOM. Since the two DOMs differ slightly, it takes
enough effort access the individual artifacts within an SVG to be a
show-stopper for your project. This extension will recursively load an
SVG file into the HTML DOM, while also providing you the ability to
easily add highlighting, to add pop-up labels, and to dynamically
intervene while the SVG is being loaded. The built-in functionalities
for highlighting and popups are made possible by adding/decorating the
SVG XML with additional attributes. For highlighting, this means adding
an attribute of your choice for which you can pass a selector that will
allow the plugin to be able to find those specific artifacts/shapes
within your XML. For popup messages, the attributes will also contain
the text to appear in the popup.
In the following image, we use three observers and three types of
highlighting and three types of labels for the counties, stores, and the
lake.
Example HTML:
<html>
<head>
<script src="jquery-1.6.min.js" type="text/javascript"></script>
<script src="isvg.js" type="text/javascript"></script>
<script src="index.js" type="text/javascript"></script>
</head>
<body>
<div id="SvgContainer" style="width: 500px; height: 500px; border: 1px solid black; "></div>
</body>
</html>
$("#SvgContainer").isvg({
Data: "Regions_20110929-1719_WithStores.svg",
DataIsUrl: true
});
Highlighting
In order to do highlighting, a selector must be specified for what is to
be hightlighted. In my case, I added the "label-type" attribute to the
paths within my SVG that represent the counties, stores, and
geographical features (the lake) in the image. Both these attributes and
their values are arbitrary.. I just made sure that my arguments agreed
with them.
Example of an SVG path for a county after it was modified for highlighting:
<path
d="M 252.4,208.6 V 209.4 L 252.5,213.9 H 255.1 L 256.3,213.8 258.9,213.7 H 263.1 264.9 L 267.8,213.6 V 215.1 L 267.9,217.5"
id="Collier"
style="fill:#ffffff;stroke:#000000;stroke-width:0.25"
label-type="county" />
To activate it for highlighting, call the following:
$("#SvgContainer").isvg(
"AddHighlighting",
{
Selector: "*[label-type='county']"
}
);
This applies the highlighting to every tag with an attribute
"label-type" with a value of "county" using the default fill-color
(black, at the present time).
Labelling
You may desire to have pop-up labels on certain SVG artifacts. To do
this, a selector must be specified to indicate what gets labelled. In my
example, I added the "label-name" attribute to the paths within my SVG
that represent the counties, stores, and geographical features in the
image. As before, the attribute name was arbitrary as long as the
arguments referenced them correctly.
Example of an SVG path for a county after it was modified for labels:
<path
d="M 252.4,208.6 V 209.4 L 252.5,213.9 H 255.1 L 256.3,213.8 258.9,213.7 H 263.1 264.9 L 267.8,213.6"
id="Collier"
style="fill:#ffffff;stroke:#000000;stroke-width:0.25"
label-name="Collier County"
label-type="county" />
To activate it for labelling, call the following:
$("#SvgContainer").isvg(
"AddLabels",
{
Selector: "*[label-type='county']",
TypeIdent: "county",
LabelAttribute: "label-name",
LabelClass: "entity-label county-label"
}
);
This applies the labelling to every tag with an attribute "label-type" with a value of "county".
Other arguments:
TypeIdent:
A string that uniquely identifies the label type. This is arbitrary.
LabelAttribute:
The name of the attribute that contains value for the label.
LabelClass:
The class(es) to add to the label that is created.
For your convenience, this is an example of nice-looking CSS that can be used for the labels:
.entity-label {
position: absolute;
display: none;
border: solid 1px #CDCDCD;
-moz-border-radius: 3px;
background: #292929;
color: white;
font-family: sans-serif, Verdana;
font-size: smaller;
padding: 3px;
}
You may call AddLabels() many times with the same TypeIdent if you'd
like many different selectors to have the same style/label. The
"LabelClass" parameter will be ignored if the value for "TypeIdent" has
already been used.
Observers
You may declare one or more functions as observers that will be called
throughout the loading process when certain nodes are encountered. You
are given the node after it's been loaded into the DOM with all of its
children.
Method 1
You may build an object-heirarchy that describes the tag-path (simple form of an xpath) at which the observer should trigger:
function PathObserver(node, observerTree)
{
// Change the stroke-width of all matching paths.
node.style.strokeWidth = .25;
}
var observerTree = { }
observerTree.svg = { }
observerTree.svg.g = { }
observerTree.svg.g.g = { }
observerTree.svg.g.g.path = { }
// Call the observer for all matching paths.
observerTree.svg.g.g.path["_"] = PathObserver;
// Or, call the observer for any artifact with an ID of "Collier" that's
// positioned at that level. In this case, it will be a path that has the ID
// of "Collier".
observerTree.svg.g.g["#Collier"] = PathObserver;
$("#SvgContainer").isvg({
Data: "CityRegions_20110929-1719_WithStores.svg",
DataIsUrl: true,
ObserverTree: observerTree
});
Method 2
This is an inefficient version of method 1. You may build a flat list of tag-paths and function calls:
function PathObserver(node, observerEntry)
{
// Change the stroke-width of all matching paths.
node.style.strokeWidth = .25;
}
// Trigger for every path at the given level in the SVG.
var observers = [{ On: "svg g g path", Handler: PathObserver }]
// Or, trigger for the specific artifact, at any level of the SVG. This
// differs from the heirarchical version in that this will match on an ID
// found anywhere in the tree rather than just at a particular level.
var observers = [{ On: "#Collier", Handler: PathObserver }]
$("#SvgContainer").isvg({
Data: "CityRegions_20110929-1719_WithStores.svg",
DataIsUrl: true,
Observers: observers
});
Both types of observer arguments ("ObserverTree" and "Observers") can be used simultaneously.
SVG Size and Positioning
Sizing/position SVGs within the browser window is unpleasant, to say the
least. This is an SVG problem outside the scope of this project. To fix
whatever issues you may be having, the quick solution is just to use a
vector-graphics studio like the free (and awesome) Inkscape
(http://inkscape.org/) to translate all of your SVG objects/artifacts so
that they are positioned according to an origin of (0, 0). Once you
open your SVG, go to "Object" -> "Transform...", and make sure
"Horizontal" and "Vertical" are "0", "Relative move" is unchecked, and
"Apply to each object separately" is checked under the "Move" tab.
Once you've done that, prepare to save and close Inkscape. WARNING: hen
you save your files, all unofficial tags (like the "label-..." tags we
added above) will be removed. If you had this information in their and
wish to retain it, save the file as an "Inkscape SVG" file. It will save
a bunch of extra stuff in the file, but it will leave the stuff you
added alone, and will still load into the browser fine.
Close Inkscape and open the SVG file in a text-editor. Either clip the
"width" and "height" atributes from the root SVG tag, or set them to
"100%". Now, the only other thing that should remain regarding sizing
should be "viewbox" with values "0 0 <width> <height>":
<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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.0"
viewBox="0 0 222.0875 257.07548"
id="svg101"
style="overflow:visible"
inkscape:version="0.48.1 r9760"
width="100%"
height="100%"
sodipodi:docname="CityRegions_20110929-1719_WithStores.svg">
For a dissertation on how to properly calculate sizes and positioning,
go to this comprehensively written evaluation:
http://tavmjong.free.fr/INKSCAPE/MANUAL/html/Web-SVG-Positioning.html . I
had no desire to deal with it.
Layered Artifacts
There is a small gotcha in HTML event-handling that can sabotage you
when you have a second layer of artifacts overlapping another, where
you'd like both to be highlighted at the same time. Such a case would be
with the individual locations on a store map, where you'd not only like
to highlight the store marker, but also the county underneath it. To
fix this, you may also add a third "label-parent" attribute to the
second-layer artifact (the store, in our example). This contains the ID
of the first-level artifact (the county, in our example):
<path
transform="translate(14.60889,43.312489)"
style="fill:#d40000;overflow:visible"
id="path3031-3"
d="M 70.070921,95.510679 67.820434,94.968812 66.098221,96.515516 65.918129,94.207729 63.914932,93.047765 66.054116,92.163342"
label-name="Naples Store"
label-type="store"
label-parent="Collier" />
It also has to be passed as an argument to the respective highlight function:
$("#SvgContainer").isvg(
"AddHighlighting",
{
Selector: "*[label-type='store']",
FillColor: "#A50000",
ParentRefAttribute: "label-parent"
}
);