Skip to content

Instantly share code, notes, and snippets.

@dchaplinsky
Created October 14, 2013 09:40
Show Gist options
  • Select an option

  • Save dchaplinsky/6973291 to your computer and use it in GitHub Desktop.

Select an option

Save dchaplinsky/6973291 to your computer and use it in GitHub Desktop.
SVG/VML vector lib to draw simple maps with paths.
window.SVGMap = (function () {
var SM = function (){
return SM.init.apply(SM, arguments);
},
undefined,
doc = document,
win = window,
svgns = 'http://www.w3.org/2000/svg';
// Partially borrowed from RaphaelJS lib by D. Baranosvkiy.
SM.type = (window.SVGAngle || document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") ? "SVG" : "VML");
SM.getContainer = function (container) {
if (typeof container == "string")
container = doc.getElementById(container);
if (container.tagName) {
this.container = container;
if (this.options.width === undefined) {
this.options.width = container.style.pixelWidth || container.offsetWidth;
}
if (this.options.height === undefined) {
this.options.height = container.style.pixelHeight || container.offsetHeight;
}
} else {
throw "No container available";
}
};
var extend = function(destination, source) {
for (var property in source)
destination[property] = source[property];
return destination;
},
cloneArray = function(source){
return source.slice(0, source.length);
},
formatCoord = function(num) {
return SM.type == 'SVG' ?
num.toFixed(3)
: Math.round(num) - 1;
},
isFunction = function(obj) {
return typeof obj === "function";
},
whateverHandler = function(id, baseOptions, callback) {
if (isFunction(callback)) {
options = callback(id);
if (options === false)
return false;
return (options === true || options === undefined) ?
baseOptions
: extend(extend({}, baseOptions), options);
} else {
return baseOptions;
}
},
mouseoverHandler = function(event) {
var options = whateverHandler(this.region, SM.options.hover, SM.options.onHover);
if (options) {
setAttrs(this, options);
this.parentNode.appendChild(this);
}
},
drawHandler = function(event) {
var options = whateverHandler(this.region, SM.options.normal, SM.options.onShow);
options && setAttrs(this, options);
},
clickHandler = function(event) {
var options = whateverHandler(this.region, SM.options.hover, SM.options.onClick);
options && setAttrs(this, options);
};
SM.init = function(container, options) {
var self = this, i;
this.options = extend({
normal: {
'stroke': '#FF9D40',
'fill': '#FFB773',
'stroke-width': 1
},
hover: {
'stroke': '#FF7C00',
'fill': '#04859D',
'stroke-width': 1
},
zoom: -1
},
options || {});
this.getContainer(container);
this.canvas = createCanvas(this.options);
if (this.container.firstChild)
this.container.insertBefore(this.canvas, this.container.firstChild);
else
this.container.appendChild(this.canvas);
this.options.zoom = this.options.zoom != -1 ?
this.options.zoom
: 1;
return SM;
};
function createCanvas(options) {
var canvas;
if (SM.type == 'SVG') {
canvas = doc.createElementNS(svgns, "svg");
canvas.setAttribute("width", options.width);
canvas.setAttribute("height", options.height);
} else {
canvas = doc.createElement("div");
// canvas.coordsize = "21600 21600";
// canvas.coordorigin = "0 0";
canvas.style.width = options.width + "px";
canvas.style.height = options.height + "px";
canvas.style.position = "relative";
canvas.style.display = "inline-block";
canvas.style.left = 0;
canvas.style.top = 0;
canvas.style.overflow = "hidden";
canvas.style.clip = "rect(0 " + options.width + "px " + options.height + "px 0)";
}
return canvas;
}
function renderPath(path) {
var el;
if (SM.type == 'SVG') {
el = doc.createElementNS(svgns, "path");
el.setAttribute('d', path);
} else {
el = createNode("shape");
el.style.width = "1px"; //this.options.width + "px";
el.style.height = "1px"; //this.options.height + "px";
el.style.left = 0;
el.style.top = 0;
el.style.position = "absolute";
el.path = path;
el.coordsize = "21600 21600"; // this.canvas.coordsize;
el.coordorigin = "0 0"; // this.canvas.coordorigin;
var skew = createNode("skew");
skew.on = true;
skew.offset = "0 0";
skew.matrix = "1 0 0 1";
skew.origin = "0 0";
el.appendChild(skew);
el.skew = skew;
}
this.canvas.appendChild(el);
return el;
};
function createGroup() {
var el;
if (SM.type == 'SVG') {
el = doc.createElementNS(svgns, "g");
this.canvas.appendChild(el);
}
// No sense in groups for VML, no added value only hassle
return el;
};
if (SM.type == 'SVG') {
// SVG code
var setAttrs = function(el, options) {
options.stroke !== undefined && el.setAttribute('stroke', options.stroke);
options.fill !== undefined && el.setAttribute('fill', options.fill);
options["class"] !== undefined && el.setAttribute('class', options["class"]);
options["id"] !== undefined && el.setAttribute('id', options["id"]);
options['stroke-width'] !== undefined && el.setAttribute('stroke-width', options['stroke-width']);
};
function attachEvent(element, event, observer) {
var f = function (e) {
return observer.call(element, e);
};
element.addEventListener(event, f, false);
}
} else {
// VML code
doc.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)");
try {
if (!doc.namespaces.rvml) {
doc.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");
}
var createNode = function (tagName) {
return doc.createElement('<rvml:' + tagName + ' class="rvml">');
};
} catch (e) {
var createNode = function (tagName) {
return doc.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
};
}
var setAttrs = function(el, options) {
var created = false;
if (options.fill !== undefined) {
var fill = (el.getElementsByTagName("fill") && el.getElementsByTagName("fill")[0]);
if (!fill) {
fill = createNode("fill");
created = true;
}
if (options.fill) {
fill.on = true;
fill.src = "";
fill.type = "solid";
fill.color = options.fill;
} else {
fill.on = false;
}
if (created && options.fill) {
el.appendChild(fill);
}
}
if (options.stroke !== undefined) {
var stroke = (el.getElementsByTagName("stroke") && el.getElementsByTagName("stroke")[0]);
if (!stroke) {
stroke = createNode("stroke");
}
if (options.stroke != "none") {
stroke.on = true;
stroke.weight = 0.75;
stroke.miterlimit = 8;
stroke.color = options.stroke;
} else {
stroke.on = false;
stroke.weight = 0;
}
el.appendChild(stroke);
}
if (options["id"] !== undefined) {
el.id = options["id"];
}
if (options["class"] !== undefined) {
el.setAttribute("class", [el.getAttribute("class"), options["class"]].join(" "));
}
};
}
function Element(path) {
this.node = renderPath.call(SM, path);
this._x = 0;
this._y = 0;
this._scale = 1;
this.parent = null;
return this;
}
Element.prototype = {
setAttrs: function(options) {
setAttrs(this.node, options);
return this;
},
getBBox: function() {
var bb = this.node.getBoundingClientRect();
return {
"width": bb.width === undefined ? bb.right - bb.left : bb.width,
"height": bb.height === undefined ? bb.bottom - bb.top : bb.height,
"top": bb.top,
"bottom": bb.bottom,
"left": bb.left,
"right": bb.right
};
},
_updateTransform: function() {
if (SM.type == "SVG") {
this.node.setAttribute("transform",
["translate(", this._x, ",", this._y, ") scale(", this._scale, ")"].join(""));
} else {
if (this.parent !== undefined) {
this.node.skew.offset = (this.parent._x + this.parent._scale * this._x) + " " + (this.parent._y + this.parent._scale * this._y);
this.node.skew.matrix = (this.parent._scale * this._scale) + " 0 0 " + (this.parent._scale * this._scale);
} else {
this.node.skew.offset = this._x + " " + this._y;
this.node.skew.matrix = this._scale + " 0 0 " + this._scale;
}
}
},
translate: function(x, y) {
this._x = x;
this._y = y;
this._updateTransform();
return this;
},
scale: function(factor) {
this._scale = factor;
this._updateTransform();
return this;
}
};
function Set(elements) {
var i, len;
this.items = [];
this.length = 0;
this.node = createGroup.call(SM);
this._x = 0;
this._y = 0;
this._scale = 1;
if (elements !== undefined) {
for (i = 0, len = elements.length; i < len; i++) {
this.add(elements[i]);
}
}
return this;
}
Set.prototype = {
add: function(el) {
this.items.push(el);
if (this.node !== undefined) {
this.node.appendChild(el.node);
}
el.parent = this;
this.length = this.items.length;
},
_updateTransform: function() {
if (SM.type == "SVG") {
return Element.prototype._updateTransform.call(this);
} else {
var i, len;
for (i = 0, len = this.items.length; i < len; i++) {
this.items[i]._updateTransform();
}
}
},
translate: function(x, y) {
this._x = x;
this._y = y;
this._updateTransform();
return this;
},
scale: function(factor) {
this._scale = factor;
this._updateTransform();
return this;
},
getBBox: function() {
if (SM.type == "SVG") {
return Element.prototype.getBBox.call(this);
} else {
var x = [], y = [],
i, len, sub_bb, bb;
for (i = 0, len = this.items.length; i < len; i++) {
sub_bb = this.items[i].getBBox();
x.push(sub_bb.left, sub_bb.right);
y.push(sub_bb.top, sub_bb.bottom);
}
bb = {
"top": Math.min.apply(Math, y),
"bottom": Math.max.apply(Math, y),
"left": Math.min.apply(Math, x),
"right": Math.max.apply(Math, x)
};
bb.width = bb.right - bb.left;
bb.height = bb.bottom - bb.left;
return bb;
}
}
};
SM.addRegion = function(id, path, options) {
p = new Element(path).setAttrs(options);
// setAttrs(p.node, options);
// path = renderPath.call(this, path);
// setAttrs(path, options);
// $("textarea").val(path.parentNode.parentNode.innerHTML);
// alert(path.parentNode.innerHTML);
// attachEvent(path, 'mouseover', mouseoverHandler);
// attachEvent(path, 'mouseout', drawHandler);
// attachEvent(path, 'click', clickHandler);
return p;
};
SM.addCompound = function() {
return new Set();
};
return SM;
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment