Created
October 14, 2013 09:40
-
-
Save dchaplinsky/6973291 to your computer and use it in GitHub Desktop.
SVG/VML vector lib to draw simple maps with paths.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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