http://git-wip-us.apache.org/repos/asf/stratos/blob/53c98dd9/extensions/das/modules/artifacts/metering-dashboard/capps/stratos-metering-service/GadgetMemberCount_1.0.0/Member_Count/js/vega.js ---------------------------------------------------------------------- diff --git a/extensions/das/modules/artifacts/metering-dashboard/capps/stratos-metering-service/GadgetMemberCount_1.0.0/Member_Count/js/vega.js b/extensions/das/modules/artifacts/metering-dashboard/capps/stratos-metering-service/GadgetMemberCount_1.0.0/Member_Count/js/vega.js new file mode 100644 index 0000000..dd3ae3b --- /dev/null +++ b/extensions/das/modules/artifacts/metering-dashboard/capps/stratos-metering-service/GadgetMemberCount_1.0.0/Member_Count/js/vega.js @@ -0,0 +1,7986 @@ +// Define module using Universal Module Definition pattern +// https://github.com/umdjs/umd/blob/master/amdWeb.js + +(function (factory) { + if (typeof define === 'function' && define.amd) { + // Support AMD. Register as an anonymous module. + // NOTE: List all dependencies in AMD style + define(['d3.min', 'topojson'], factory); + } else { + // No AMD. Set module as a global variable + // NOTE: Pass dependencies to factory function + // (assume that both d3 and topojson are also global.) + var tj = (typeof topojson === 'undefined') ? null : topojson; + vg = factory(d3, tj); + } +}( +//NOTE: The dependencies are passed to this function + function (d3, topojson) { +//--------------------------------------------------- +// BEGIN code for this module +//--------------------------------------------------- + + var vg = { + version: "1.4.3", // semantic versioning + d3: d3, // stash d3 for use in property functions + topojson: topojson // stash topojson similarly + }; +// type checking functions + var toString = Object.prototype.toString; + + vg.isObject = function (obj) { + return obj === Object(obj); + }; + + vg.isFunction = function (obj) { + return toString.call(obj) == '[object Function]'; + }; + + vg.isString = function (obj) { + return toString.call(obj) == '[object String]'; + }; + + vg.isArray = Array.isArray || function (obj) { + return toString.call(obj) == '[object Array]'; + }; + + vg.isNumber = function (obj) { + return toString.call(obj) == '[object Number]'; + }; + + vg.isBoolean = function (obj) { + return toString.call(obj) == '[object Boolean]'; + }; + + vg.isTree = function (obj) { + return obj && obj.__vgtree__; + }; + + vg.tree = function (obj, children) { + var d = [obj]; + d.__vgtree__ = true; + d.children = children || "children"; + return d; + }; + + vg.number = function (s) { + return +s; + }; + + vg.boolean = function (s) { + return !!s; + }; + +// utility functions + + vg.identity = function (x) { + return x; + }; + + vg.true = function () { + return true; + }; + + vg.extend = function (obj) { + for (var x, name, i = 1, len = arguments.length; i < len; ++i) { + x = arguments[i]; + for (name in x) { + obj[name] = x[name]; + } + } + return obj; + }; + + vg.duplicate = function (obj) { + return JSON.parse(JSON.stringify(obj)); + }; + + vg.field = function (f) { + return f.split("\\.") + .map(function (d) { + return d.split("."); + }) + .reduce(function (a, b) { + if (a.length) { + a[a.length - 1] += "." + b.shift(); + } + a.push.apply(a, b); + return a; + }, []); + }; + + vg.accessor = function (f) { + var s; + return (vg.isFunction(f) || f == null) + ? f : vg.isString(f) && (s = vg.field(f)).length > 1 + ? function (x) { + return s.reduce(function (x, f) { + return x[f]; + }, x); + } + : function (x) { + return x[f]; + }; + }; + + vg.mutator = function (f) { + var s; + return vg.isString(f) && (s = vg.field(f)).length > 1 + ? function (x, v) { + for (var i = 0; i < s.length - 1; ++i) x = x[s[i]]; + x[s[i]] = v; + } + : function (x, v) { + x[f] = v; + }; + }; + + vg.comparator = function (sort) { + var sign = []; + if (sort === undefined) sort = []; + sort = vg.array(sort).map(function (f) { + var s = 1; + if (f[0] === "-") { + s = -1; + f = f.slice(1); + } + else if (f[0] === "+") { + s = +1; + f = f.slice(1); + } + sign.push(s); + return vg.accessor(f); + }); + return function (a, b) { + var i, n, f, x, y; + for (i = 0, n = sort.length; i < n; ++i) { + f = sort[i]; + x = f(a); + y = f(b); + if (x < y) return -1 * sign[i]; + if (x > y) return sign[i]; + } + return 0; + }; + }; + + vg.cmp = function (a, b) { + return a < b ? -1 : a > b ? 1 : 0; + }; + + vg.numcmp = function (a, b) { + return a - b; + }; + + vg.array = function (x) { + return x != null ? (vg.isArray(x) ? x : [x]) : []; + }; + + vg.values = function (x) { + return (vg.isObject(x) && !vg.isArray(x) && x.values) ? x.values : x; + }; + + vg.str = function (x) { + return vg.isArray(x) ? "[" + x.map(vg.str) + "]" + : vg.isObject(x) ? JSON.stringify(x) + : vg.isString(x) ? ("'" + vg_escape_str(x) + "'") : x; + }; + + var escape_str_re = /(^|[^\\])'/g; + + function vg_escape_str(x) { + return x.replace(escape_str_re, "$1\\'"); + } + + vg.keys = function (x) { + var keys = []; + for (var key in x) keys.push(key); + return keys; + }; + + vg.unique = function (data, f, results) { + if (!vg.isArray(data) || data.length == 0) return []; + f = f || vg.identity; + results = results || []; + for (var v, i = 0, n = data.length; i < n; ++i) { + v = f(data[i]); + if (results.indexOf(v) < 0) results.push(v); + } + return results; + }; + + vg.minIndex = function (data, f) { + if (!vg.isArray(data) || data.length == 0) return -1; + f = f || vg.identity; + var idx = 0, min = f(data[0]), v = min; + for (var i = 1, n = data.length; i < n; ++i) { + v = f(data[i]); + if (v < min) { + min = v; + idx = i; + } + } + return idx; + }; + + vg.maxIndex = function (data, f) { + if (!vg.isArray(data) || data.length == 0) return -1; + f = f || vg.identity; + var idx = 0, max = f(data[0]), v = max; + for (var i = 1, n = data.length; i < n; ++i) { + v = f(data[i]); + if (v > max) { + max = v; + idx = i; + } + } + return idx; + }; + + vg.truncate = function (s, length, pos, word, ellipsis) { + var len = s.length; + if (len <= length) return s; + ellipsis = ellipsis || "..."; + var l = Math.max(0, length - ellipsis.length); + + switch (pos) { + case "left": + return ellipsis + (word ? vg_truncateOnWord(s, l, 1) : s.slice(len - l)); + case "middle": + case "center": + var l1 = Math.ceil(l / 2), l2 = Math.floor(l / 2); + return (word ? vg_truncateOnWord(s, l1) : s.slice(0, l1)) + ellipsis + + (word ? vg_truncateOnWord(s, l2, 1) : s.slice(len - l2)); + default: + return (word ? vg_truncateOnWord(s, l) : s.slice(0, l)) + ellipsis; + } + } + + function vg_truncateOnWord(s, len, rev) { + var cnt = 0, tok = s.split(vg_truncate_word_re); + if (rev) { + s = (tok = tok.reverse()) + .filter(function (w) { + cnt += w.length; + return cnt <= len; + }) + .reverse(); + } else { + s = tok.filter(function (w) { + cnt += w.length; + return cnt <= len; + }); + } + return s.length ? s.join("").trim() : tok[0].slice(0, len); + } + + var vg_truncate_word_re = /([\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u2028\u2029\u3000\uFEFF])/; + +// Logging + + function vg_write(msg) { + vg.config.isNode + ? process.stderr.write(msg + "\n") + : console.log(msg); + } + + vg.log = function (msg) { + vg_write("[Vega Log] " + msg); + }; + + vg.error = function (msg) { + msg = "[Vega Err] " + msg; + vg_write(msg); + if (typeof alert !== "undefined") alert(msg); + }; + vg.config = {}; + +// are we running in node.js? +// via timetler.com/2012/10/13/environment-detection-in-javascript/ + vg.config.isNode = typeof exports !== 'undefined' && this.exports !== exports; + +// Allows domain restriction when using data loading via XHR. +// To enable, set it to a list of allowed domains +// e.g., ['wikipedia.org', 'eff.org'] + vg.config.domainWhiteList = false; + +// If true, disable potentially unsafe transforms (filter, formula) +// involving possible JavaScript injection attacks. + vg.config.safeMode = false; + +// base url for loading external data files +// used only for server-side operation + vg.config.baseURL = ""; + +// version and namepsaces for exported svg + vg.config.svgNamespace = + 'version="1.1" xmlns="http://www.w3.org/2000/svg" ' + + 'xmlns:xlink="http://www.w3.org/1999/xlink"'; + +// inset padding for automatic padding calculation + vg.config.autopadInset = 5; + +// extensible scale lookup table +// all d3.scale.* instances also supported + vg.config.scale = { + time: d3.time.scale, + utc: d3.time.scale.utc + }; + +// default rendering settings + vg.config.render = { + lineWidth: 1, + lineCap: "butt", + font: "sans-serif", + fontSize: 11 + }; + +// default axis properties + vg.config.axis = { + orient: "bottom", + ticks: 10, + padding: 3, + axisColor: "#000", + gridColor: "#d8d8d8", + tickColor: "#000", + tickLabelColor: "#000", + axisWidth: 1, + tickWidth: 1, + tickSize: 6, + tickLabelFontSize: 11, + tickLabelFont: "sans-serif", + titleColor: "#000", + titleFont: "sans-serif", + titleFontSize: 11, + titleFontWeight: "bold", + titleOffset: 35 + }; + +// default legend properties + vg.config.legend = { + orient: "right", + offset: 10, + padding: 3, + gradientStrokeColor: "#888", + gradientStrokeWidth: 1, + gradientHeight: 16, + gradientWidth: 100, + labelColor: "#000", + labelFontSize: 10, + labelFont: "sans-serif", + labelAlign: "left", + labelBaseline: "middle", + labelOffset: 8, + symbolShape: "circle", + symbolSize: 50, + symbolColor: "#888", + symbolStrokeWidth: 1, + titleColor: "#000", + titleFont: "sans-serif", + titleFontSize: 11, + titleFontWeight: "bold" + }; + +// default color values + vg.config.color = { + rgb: [128, 128, 128], + lab: [50, 0, 0], + hcl: [0, 0, 50], + hsl: [0, 0, 0.5] + }; + +// default scale ranges + vg.config.range = { + category10: [ + "#1f77b4", + "#ff7f0e", + "#2ca02c", + "#d62728", + "#9467bd", + "#8c564b", + "#e377c2", + "#7f7f7f", + "#bcbd22", + "#17becf" + ], + category20: [ + "#1f77b4", + "#aec7e8", + "#ff7f0e", + "#ffbb78", + "#2ca02c", + "#98df8a", + "#d62728", + "#ff9896", + "#9467bd", + "#c5b0d5", + "#8c564b", + "#c49c94", + "#e377c2", + "#f7b6d2", + "#7f7f7f", + "#c7c7c7", + "#bcbd22", + "#dbdb8d", + "#17becf", + "#9edae5" + ], + shapes: [ + "circle", + "cross", + "diamond", + "square", + "triangle-down", + "triangle-up" + ] + }; + vg.Bounds = (function () { + var bounds = function (b) { + this.clear(); + if (b) this.union(b); + }; + + var prototype = bounds.prototype; + + prototype.clear = function () { + this.x1 = +Number.MAX_VALUE; + this.y1 = +Number.MAX_VALUE; + this.x2 = -Number.MAX_VALUE; + this.y2 = -Number.MAX_VALUE; + return this; + }; + + prototype.set = function (x1, y1, x2, y2) { + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + return this; + }; + + prototype.add = function (x, y) { + if (x < this.x1) this.x1 = x; + if (y < this.y1) this.y1 = y; + if (x > this.x2) this.x2 = x; + if (y > this.y2) this.y2 = y; + return this; + }; + + prototype.expand = function (d) { + this.x1 -= d; + this.y1 -= d; + this.x2 += d; + this.y2 += d; + return this; + }; + + prototype.round = function () { + this.x1 = Math.floor(this.x1); + this.y1 = Math.floor(this.y1); + this.x2 = Math.ceil(this.x2); + this.y2 = Math.ceil(this.y2); + return this; + }; + + prototype.translate = function (dx, dy) { + this.x1 += dx; + this.x2 += dx; + this.y1 += dy; + this.y2 += dy; + return this; + }; + + prototype.rotate = function (angle, x, y) { + var cos = Math.cos(angle), + sin = Math.sin(angle), + cx = x - x * cos + y * sin, + cy = y - x * sin - y * cos, + x1 = this.x1, x2 = this.x2, + y1 = this.y1, y2 = this.y2; + + return this.clear() + .add(cos * x1 - sin * y1 + cx, sin * x1 + cos * y1 + cy) + .add(cos * x1 - sin * y2 + cx, sin * x1 + cos * y2 + cy) + .add(cos * x2 - sin * y1 + cx, sin * x2 + cos * y1 + cy) + .add(cos * x2 - sin * y2 + cx, sin * x2 + cos * y2 + cy); + } + + prototype.union = function (b) { + if (b.x1 < this.x1) this.x1 = b.x1; + if (b.y1 < this.y1) this.y1 = b.y1; + if (b.x2 > this.x2) this.x2 = b.x2; + if (b.y2 > this.y2) this.y2 = b.y2; + return this; + }; + + prototype.encloses = function (b) { + return b && ( + this.x1 <= b.x1 && + this.x2 >= b.x2 && + this.y1 <= b.y1 && + this.y2 >= b.y2 + ); + }; + + prototype.intersects = function (b) { + return b && !( + this.x2 < b.x1 || + this.x1 > b.x2 || + this.y2 < b.y1 || + this.y1 > b.y2 + ); + }; + + prototype.contains = function (x, y) { + return !( + x < this.x1 || + x > this.x2 || + y < this.y1 || + y > this.y2 + ); + }; + + prototype.width = function () { + return this.x2 - this.x1; + }; + + prototype.height = function () { + return this.y2 - this.y1; + }; + + return bounds; + })(); + vg.Gradient = (function () { + + function gradient(type) { + this.id = "grad_" + (vg_gradient_id++); + this.type = type || "linear"; + this.stops = []; + this.x1 = 0; + this.x2 = 1; + this.y1 = 0; + this.y2 = 0; + }; + + var prototype = gradient.prototype; + + prototype.stop = function (offset, color) { + this.stops.push({ + offset: offset, + color: color + }); + return this; + }; + + return gradient; + })(); + + var vg_gradient_id = 0; + vg.canvas = {}; + vg.canvas.path = (function () { + + // Path parsing and rendering code taken from fabric.js -- Thanks! + var cmdLength = {m: 2, l: 2, h: 1, v: 1, c: 6, s: 4, q: 4, t: 2, a: 7}, + re = [/([MLHVCSQTAZmlhvcsqtaz])/g, /###/, /(\d)-/g, /\s|,|###/]; + + function parse(path) { + var result = [], + currentPath, + chunks, + parsed; + + // First, break path into command sequence + path = path.slice().replace(re[0], '###$1').split(re[1]).slice(1); + + // Next, parse each command in turn + for (var i = 0, j, chunksParsed, len = path.length; i < len; i++) { + currentPath = path[i]; + chunks = currentPath.slice(1).trim().replace(re[2], '$1###-').split(re[3]); + chunksParsed = [currentPath.charAt(0)]; + + for (var j = 0, jlen = chunks.length; j < jlen; j++) { + parsed = parseFloat(chunks[j]); + if (!isNaN(parsed)) { + chunksParsed.push(parsed); + } + } + + var command = chunksParsed[0].toLowerCase(), + commandLength = cmdLength[command]; + + if (chunksParsed.length - 1 > commandLength) { + for (var k = 1, klen = chunksParsed.length; k < klen; k += commandLength) { + result.push([chunksParsed[0]].concat(chunksParsed.slice(k, k + commandLength))); + } + } + else { + result.push(chunksParsed); + } + } + + return result; + } + + function drawArc(g, x, y, coords, bounds, l, t) { + var rx = coords[0]; + var ry = coords[1]; + var rot = coords[2]; + var large = coords[3]; + var sweep = coords[4]; + var ex = coords[5]; + var ey = coords[6]; + var segs = arcToSegments(ex, ey, rx, ry, large, sweep, rot, x, y); + for (var i = 0; i < segs.length; i++) { + var bez = segmentToBezier.apply(null, segs[i]); + g.bezierCurveTo.apply(g, bez); + bounds.add(bez[0] - l, bez[1] - t); + bounds.add(bez[2] - l, bez[3] - t); + bounds.add(bez[4] - l, bez[5] - t); + } + } + + function boundArc(x, y, coords, bounds) { + var rx = coords[0]; + var ry = coords[1]; + var rot = coords[2]; + var large = coords[3]; + var sweep = coords[4]; + var ex = coords[5]; + var ey = coords[6]; + var segs = arcToSegments(ex, ey, rx, ry, large, sweep, rot, x, y); + for (var i = 0; i < segs.length; i++) { + var bez = segmentToBezier.apply(null, segs[i]); + bounds.add(bez[0], bez[1]); + bounds.add(bez[2], bez[3]); + bounds.add(bez[4], bez[5]); + } + } + + var arcToSegmentsCache = {}, + segmentToBezierCache = {}, + join = Array.prototype.join, + argsStr; + + // Copied from Inkscape svgtopdf, thanks! + function arcToSegments(x, y, rx, ry, large, sweep, rotateX, ox, oy) { + argsStr = join.call(arguments); + if (arcToSegmentsCache[argsStr]) { + return arcToSegmentsCache[argsStr]; + } + + var th = rotateX * (Math.PI / 180); + var sin_th = Math.sin(th); + var cos_th = Math.cos(th); + rx = Math.abs(rx); + ry = Math.abs(ry); + var px = cos_th * (ox - x) * 0.5 + sin_th * (oy - y) * 0.5; + var py = cos_th * (oy - y) * 0.5 - sin_th * (ox - x) * 0.5; + var pl = (px * px) / (rx * rx) + (py * py) / (ry * ry); + if (pl > 1) { + pl = Math.sqrt(pl); + rx *= pl; + ry *= pl; + } + + var a00 = cos_th / rx; + var a01 = sin_th / rx; + var a10 = (-sin_th) / ry; + var a11 = (cos_th) / ry; + var x0 = a00 * ox + a01 * oy; + var y0 = a10 * ox + a11 * oy; + var x1 = a00 * x + a01 * y; + var y1 = a10 * x + a11 * y; + + var d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0); + var sfactor_sq = 1 / d - 0.25; + if (sfactor_sq < 0) sfactor_sq = 0; + var sfactor = Math.sqrt(sfactor_sq); + if (sweep == large) sfactor = -sfactor; + var xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0); + var yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0); + + var th0 = Math.atan2(y0 - yc, x0 - xc); + var th1 = Math.atan2(y1 - yc, x1 - xc); + + var th_arc = th1 - th0; + if (th_arc < 0 && sweep == 1) { + th_arc += 2 * Math.PI; + } else if (th_arc > 0 && sweep == 0) { + th_arc -= 2 * Math.PI; + } + + var segments = Math.ceil(Math.abs(th_arc / (Math.PI * 0.5 + 0.001))); + var result = []; + for (var i = 0; i < segments; i++) { + var th2 = th0 + i * th_arc / segments; + var th3 = th0 + (i + 1) * th_arc / segments; + result[i] = [xc, yc, th2, th3, rx, ry, sin_th, cos_th]; + } + + return (arcToSegmentsCache[argsStr] = result); + } + + function segmentToBezier(cx, cy, th0, th1, rx, ry, sin_th, cos_th) { + argsStr = join.call(arguments); + if (segmentToBezierCache[argsStr]) { + return segmentToBezierCache[argsStr]; + } + + var a00 = cos_th * rx; + var a01 = -sin_th * ry; + var a10 = sin_th * rx; + var a11 = cos_th * ry; + + var cos_th0 = Math.cos(th0); + var sin_th0 = Math.sin(th0); + var cos_th1 = Math.cos(th1); + var sin_th1 = Math.sin(th1); + + var th_half = 0.5 * (th1 - th0); + var sin_th_h2 = Math.sin(th_half * 0.5); + var t = (8 / 3) * sin_th_h2 * sin_th_h2 / Math.sin(th_half); + var x1 = cx + cos_th0 - t * sin_th0; + var y1 = cy + sin_th0 + t * cos_th0; + var x3 = cx + cos_th1; + var y3 = cy + sin_th1; + var x2 = x3 + t * sin_th1; + var y2 = y3 - t * cos_th1; + + return (segmentToBezierCache[argsStr] = [ + a00 * x1 + a01 * y1, a10 * x1 + a11 * y1, + a00 * x2 + a01 * y2, a10 * x2 + a11 * y2, + a00 * x3 + a01 * y3, a10 * x3 + a11 * y3 + ]); + } + + function render(g, path, l, t) { + var current, // current instruction + previous = null, + x = 0, // current x + y = 0, // current y + controlX = 0, // current control point x + controlY = 0, // current control point y + tempX, + tempY, + tempControlX, + tempControlY, + bounds = new vg.Bounds(); + if (l == undefined) l = 0; + if (t == undefined) t = 0; + + g.beginPath(); + + for (var i = 0, len = path.length; i < len; ++i) { + current = path[i]; + + switch (current[0]) { // first letter + + case 'l': // lineto, relative + x += current[1]; + y += current[2]; + g.lineTo(x + l, y + t); + bounds.add(x, y); + break; + + case 'L': // lineto, absolute + x = current[1]; + y = current[2]; + g.lineTo(x + l, y + t); + bounds.add(x, y); + break; + + case 'h': // horizontal lineto, relative + x += current[1]; + g.lineTo(x + l, y + t); + bounds.add(x, y); + break; + + case 'H': // horizontal lineto, absolute + x = current[1]; + g.lineTo(x + l, y + t); + bounds.add(x, y); + break; + + case 'v': // vertical lineto, relative + y += current[1]; + g.lineTo(x + l, y + t); + bounds.add(x, y); + break; + + case 'V': // verical lineto, absolute + y = current[1]; + g.lineTo(x + l, y + t); + bounds.add(x, y); + break; + + case 'm': // moveTo, relative + x += current[1]; + y += current[2]; + g.moveTo(x + l, y + t); + bounds.add(x, y); + break; + + case 'M': // moveTo, absolute + x = current[1]; + y = current[2]; + g.moveTo(x + l, y + t); + bounds.add(x, y); + break; + + case 'c': // bezierCurveTo, relative + tempX = x + current[5]; + tempY = y + current[6]; + controlX = x + current[3]; + controlY = y + current[4]; + g.bezierCurveTo( + x + current[1] + l, // x1 + y + current[2] + t, // y1 + controlX + l, // x2 + controlY + t, // y2 + tempX + l, + tempY + t + ); + bounds.add(x + current[1], y + current[2]); + bounds.add(controlX, controlY); + bounds.add(tempX, tempY); + x = tempX; + y = tempY; + break; + + case 'C': // bezierCurveTo, absolute + x = current[5]; + y = current[6]; + controlX = current[3]; + controlY = current[4]; + g.bezierCurveTo( + current[1] + l, + current[2] + t, + controlX + l, + controlY + t, + x + l, + y + t + ); + bounds.add(current[1], current[2]); + bounds.add(controlX, controlY); + bounds.add(x, y); + break; + + case 's': // shorthand cubic bezierCurveTo, relative + // transform to absolute x,y + tempX = x + current[3]; + tempY = y + current[4]; + // calculate reflection of previous control points + controlX = 2 * x - controlX; + controlY = 2 * y - controlY; + g.bezierCurveTo( + controlX + l, + controlY + t, + x + current[1] + l, + y + current[2] + t, + tempX + l, + tempY + t + ); + bounds.add(controlX, controlY); + bounds.add(x + current[1], y + current[2]); + bounds.add(tempX, tempY); + + // set control point to 2nd one of this command + // "... the first control point is assumed to be the reflection of the second control point on the previous command relative to the current point." + controlX = x + current[1]; + controlY = y + current[2]; + + x = tempX; + y = tempY; + break; + + case 'S': // shorthand cubic bezierCurveTo, absolute + tempX = current[3]; + tempY = current[4]; + // calculate reflection of previous control points + controlX = 2 * x - controlX; + controlY = 2 * y - controlY; + g.bezierCurveTo( + controlX + l, + controlY + t, + current[1] + l, + current[2] + t, + tempX + l, + tempY + t + ); + x = tempX; + y = tempY; + bounds.add(current[1], current[2]); + bounds.add(controlX, controlY); + bounds.add(tempX, tempY); + // set control point to 2nd one of this command + // "... the first control point is assumed to be the reflection of the second control point on the previous command relative to the current point." + controlX = current[1]; + controlY = current[2]; + + break; + + case 'q': // quadraticCurveTo, relative + // transform to absolute x,y + tempX = x + current[3]; + tempY = y + current[4]; + + controlX = x + current[1]; + controlY = y + current[2]; + + g.quadraticCurveTo( + controlX + l, + controlY + t, + tempX + l, + tempY + t + ); + x = tempX; + y = tempY; + bounds.add(controlX, controlY); + bounds.add(tempX, tempY); + break; + + case 'Q': // quadraticCurveTo, absolute + tempX = current[3]; + tempY = current[4]; + + g.quadraticCurveTo( + current[1] + l, + current[2] + t, + tempX + l, + tempY + t + ); + x = tempX; + y = tempY; + controlX = current[1]; + controlY = current[2]; + bounds.add(controlX, controlY); + bounds.add(tempX, tempY); + break; + + case 't': // shorthand quadraticCurveTo, relative + + // transform to absolute x,y + tempX = x + current[1]; + tempY = y + current[2]; + + if (previous[0].match(/[QqTt]/) === null) { + // If there is no previous command or if the previous command was not a Q, q, T or t, + // assume the control point is coincident with the current point + controlX = x; + controlY = y; + } + else if (previous[0] === 't') { + // calculate reflection of previous control points for t + controlX = 2 * x - tempControlX; + controlY = 2 * y - tempControlY; + } + else if (previous[0] === 'q') { + // calculate reflection of previous control points for q + controlX = 2 * x - controlX; + controlY = 2 * y - controlY; + } + + tempControlX = controlX; + tempControlY = controlY; + + g.quadraticCurveTo( + controlX + l, + controlY + t, + tempX + l, + tempY + t + ); + x = tempX; + y = tempY; + controlX = x + current[1]; + controlY = y + current[2]; + bounds.add(controlX, controlY); + bounds.add(tempX, tempY); + break; + + case 'T': + tempX = current[1]; + tempY = current[2]; + + // calculate reflection of previous control points + controlX = 2 * x - controlX; + controlY = 2 * y - controlY; + g.quadraticCurveTo( + controlX + l, + controlY + t, + tempX + l, + tempY + t + ); + x = tempX; + y = tempY; + bounds.add(controlX, controlY); + bounds.add(tempX, tempY); + break; + + case 'a': + drawArc(g, x + l, y + t, [ + current[1], + current[2], + current[3], + current[4], + current[5], + current[6] + x + l, + current[7] + y + t + ], bounds, l, t); + x += current[6]; + y += current[7]; + break; + + case 'A': + drawArc(g, x + l, y + t, [ + current[1], + current[2], + current[3], + current[4], + current[5], + current[6] + l, + current[7] + t + ], bounds, l, t); + x = current[6]; + y = current[7]; + break; + + case 'z': + case 'Z': + g.closePath(); + break; + } + previous = current; + } + return bounds.translate(l, t); + } + + function bounds(path, bounds) { + var current, // current instruction + previous = null, + x = 0, // current x + y = 0, // current y + controlX = 0, // current control point x + controlY = 0, // current control point y + tempX, + tempY, + tempControlX, + tempControlY; + + for (var i = 0, len = path.length; i < len; ++i) { + current = path[i]; + + switch (current[0]) { // first letter + + case 'l': // lineto, relative + x += current[1]; + y += current[2]; + bounds.add(x, y); + break; + + case 'L': // lineto, absolute + x = current[1]; + y = current[2]; + bounds.add(x, y); + break; + + case 'h': // horizontal lineto, relative + x += current[1]; + bounds.add(x, y); + break; + + case 'H': // horizontal lineto, absolute + x = current[1]; + bounds.add(x, y); + break; + + case 'v': // vertical lineto, relative + y += current[1]; + bounds.add(x, y); + break; + + case 'V': // verical lineto, absolute + y = current[1]; + bounds.add(x, y); + break; + + case 'm': // moveTo, relative + x += current[1]; + y += current[2]; + bounds.add(x, y); + break; + + case 'M': // moveTo, absolute + x = current[1]; + y = current[2]; + bounds.add(x, y); + break; + + case 'c': // bezierCurveTo, relative + tempX = x + current[5]; + tempY = y + current[6]; + controlX = x + current[3]; + controlY = y + current[4]; + bounds.add(x + current[1], y + current[2]); + bounds.add(controlX, controlY); + bounds.add(tempX, tempY); + x = tempX; + y = tempY; + break; + + case 'C': // bezierCurveTo, absolute + x = current[5]; + y = current[6]; + controlX = current[3]; + controlY = current[4]; + bounds.add(current[1], current[2]); + bounds.add(controlX, controlY); + bounds.add(x, y); + break; + + case 's': // shorthand cubic bezierCurveTo, relative + // transform to absolute x,y + tempX = x + current[3]; + tempY = y + current[4]; + // calculate reflection of previous control points + controlX = 2 * x - controlX; + controlY = 2 * y - controlY; + bounds.add(controlX, controlY); + bounds.add(x + current[1], y + current[2]); + bounds.add(tempX, tempY); + + // set control point to 2nd one of this command + // "... the first control point is assumed to be the reflection of the second control point on the previous command relative to the current point." + controlX = x + current[1]; + controlY = y + current[2]; + + x = tempX; + y = tempY; + break; + + case 'S': // shorthand cubic bezierCurveTo, absolute + tempX = current[3]; + tempY = current[4]; + // calculate reflection of previous control points + controlX = 2 * x - controlX; + controlY = 2 * y - controlY; + x = tempX; + y = tempY; + bounds.add(current[1], current[2]); + bounds.add(controlX, controlY); + bounds.add(tempX, tempY); + // set control point to 2nd one of this command + // "... the first control point is assumed to be the reflection of the second control point on the previous command relative to the current point." + controlX = current[1]; + controlY = current[2]; + + break; + + case 'q': // quadraticCurveTo, relative + // transform to absolute x,y + tempX = x + current[3]; + tempY = y + current[4]; + + controlX = x + current[1]; + controlY = y + current[2]; + + x = tempX; + y = tempY; + bounds.add(controlX, controlY); + bounds.add(tempX, tempY); + break; + + case 'Q': // quadraticCurveTo, absolute + tempX = current[3]; + tempY = current[4]; + + x = tempX; + y = tempY; + controlX = current[1]; + controlY = current[2]; + bounds.add(controlX, controlY); + bounds.add(tempX, tempY); + break; + + case 't': // shorthand quadraticCurveTo, relative + + // transform to absolute x,y + tempX = x + current[1]; + tempY = y + current[2]; + + if (previous[0].match(/[QqTt]/) === null) { + // If there is no previous command or if the previous command was not a Q, q, T or t, + // assume the control point is coincident with the current point + controlX = x; + controlY = y; + } + else if (previous[0] === 't') { + // calculate reflection of previous control points for t + controlX = 2 * x - tempControlX; + controlY = 2 * y - tempControlY; + } + else if (previous[0] === 'q') { + // calculate reflection of previous control points for q + controlX = 2 * x - controlX; + controlY = 2 * y - controlY; + } + + tempControlX = controlX; + tempControlY = controlY; + + x = tempX; + y = tempY; + controlX = x + current[1]; + controlY = y + current[2]; + bounds.add(controlX, controlY); + bounds.add(tempX, tempY); + break; + + case 'T': + tempX = current[1]; + tempY = current[2]; + + // calculate reflection of previous control points + controlX = 2 * x - controlX; + controlY = 2 * y - controlY; + + x = tempX; + y = tempY; + bounds.add(controlX, controlY); + bounds.add(tempX, tempY); + break; + + case 'a': + boundArc(x, y, [ + current[1], + current[2], + current[3], + current[4], + current[5], + current[6] + x, + current[7] + y + ], bounds); + x += current[6]; + y += current[7]; + break; + + case 'A': + boundArc(x, y, [ + current[1], + current[2], + current[3], + current[4], + current[5], + current[6], + current[7] + ], bounds); + x = current[6]; + y = current[7]; + break; + + case 'z': + case 'Z': + break; + } + previous = current; + } + return bounds; + } + + function area(items) { + var o = items[0]; + var area; + + if (o.orient === "horizontal") { + area = d3.svg.area() + .y(function (d) { + return d.y; + }) + .x0(function (d) { + return d.x; + }) + .x1(function (d) { + return d.x + d.width; + }); + } else { + area = d3.svg.area() + .x(function (d) { + return d.x; + }) + .y1(function (d) { + return d.y; + }) + .y0(function (d) { + return d.y + d.height; + }); + } + + if (o.interpolate) area.interpolate(o.interpolate); + if (o.tension != null) area.tension(o.tension); + return area(items); + } + + function line(items) { + var o = items[0]; + var line = d3.svg.line() + .x(function (d) { + return d.x; + }) + .y(function (d) { + return d.y; + }); + if (o.interpolate) line.interpolate(o.interpolate); + if (o.tension != null) line.tension(o.tension); + return line(items); + } + + return { + parse: parse, + render: render, + bounds: bounds, + area: area, + line: line + }; + + })(); + vg.canvas.marks = (function () { + + var parsePath = vg.canvas.path.parse, + renderPath = vg.canvas.path.render, + halfpi = Math.PI / 2, + sqrt3 = Math.sqrt(3), + tan30 = Math.tan(30 * Math.PI / 180), + tmpBounds = new vg.Bounds(); + + // path generators + + function arcPath(g, o) { + var x = o.x || 0, + y = o.y || 0, + ir = o.innerRadius || 0, + or = o.outerRadius || 0, + sa = (o.startAngle || 0) - Math.PI / 2, + ea = (o.endAngle || 0) - Math.PI / 2; + g.beginPath(); + if (ir === 0) g.moveTo(x, y); + else g.arc(x, y, ir, sa, ea, 0); + g.arc(x, y, or, ea, sa, 1); + g.closePath(); + } + + function areaPath(g, items) { + var o = items[0], + m = o.mark, + p = m.pathCache || (m.pathCache = parsePath(vg.canvas.path.area(items))); + renderPath(g, p); + } + + function linePath(g, items) { + var o = items[0], + m = o.mark, + p = m.pathCache || (m.pathCache = parsePath(vg.canvas.path.line(items))); + renderPath(g, p); + } + + function pathPath(g, o) { + if (o.path == null) return; + var p = o.pathCache || (o.pathCache = parsePath(o.path)); + return renderPath(g, p, o.x, o.y); + } + + function symbolPath(g, o) { + g.beginPath(); + var size = o.size != null ? o.size : 100, + x = o.x, y = o.y, r, t, rx, ry; + + if (o.shape == null || o.shape === "circle") { + r = Math.sqrt(size / Math.PI); + g.arc(x, y, r, 0, 2 * Math.PI, 0); + g.closePath(); + return; + } + + switch (o.shape) { + case "cross": + r = Math.sqrt(size / 5) / 2; + t = 3 * r; + g.moveTo(x - t, y - r); + g.lineTo(x - r, y - r); + g.lineTo(x - r, y - t); + g.lineTo(x + r, y - t); + g.lineTo(x + r, y - r); + g.lineTo(x + t, y - r); + g.lineTo(x + t, y + r); + g.lineTo(x + r, y + r); + g.lineTo(x + r, y + t); + g.lineTo(x - r, y + t); + g.lineTo(x - r, y + r); + g.lineTo(x - t, y + r); + break; + + case "diamond": + ry = Math.sqrt(size / (2 * tan30)); + rx = ry * tan30; + g.moveTo(x, y - ry); + g.lineTo(x + rx, y); + g.lineTo(x, y + ry); + g.lineTo(x - rx, y); + break; + + case "square": + t = Math.sqrt(size); + r = t / 2; + g.rect(x - r, y - r, t, t); + break; + + case "triangle-down": + rx = Math.sqrt(size / sqrt3); + ry = rx * sqrt3 / 2; + g.moveTo(x, y + ry); + g.lineTo(x + rx, y - ry); + g.lineTo(x - rx, y - ry); + break; + + case "triangle-up": + rx = Math.sqrt(size / sqrt3); + ry = rx * sqrt3 / 2; + g.moveTo(x, y - ry); + g.lineTo(x + rx, y + ry); + g.lineTo(x - rx, y + ry); + } + g.closePath(); + } + + function lineStroke(g, items) { + var o = items[0], + lw = o.strokeWidth, + lc = o.strokeCap; + g.lineWidth = lw != null ? lw : vg.config.render.lineWidth; + g.lineCap = lc != null ? lc : vg.config.render.lineCap; + linePath(g, items); + } + + function ruleStroke(g, o) { + var x1 = o.x || 0, + y1 = o.y || 0, + x2 = o.x2 != null ? o.x2 : x1, + y2 = o.y2 != null ? o.y2 : y1, + lw = o.strokeWidth, + lc = o.strokeCap; + + g.lineWidth = lw != null ? lw : vg.config.render.lineWidth; + g.lineCap = lc != null ? lc : vg.config.render.lineCap; + g.beginPath(); + g.moveTo(x1, y1); + g.lineTo(x2, y2); + } + + // drawing functions + + function drawPathOne(path, g, o, items) { + var fill = o.fill, stroke = o.stroke, opac, lc, lw; + + path(g, items); + + opac = o.opacity == null ? 1 : o.opacity; + if (opac == 0 || !fill && !stroke) return; + + if (fill) { + g.globalAlpha = opac * (o.fillOpacity == null ? 1 : o.fillOpacity); + g.fillStyle = color(g, o, fill); + g.fill(); + } + + if (stroke) { + lw = (lw = o.strokeWidth) != null ? lw : vg.config.render.lineWidth; + if (lw > 0) { + g.globalAlpha = opac * (o.strokeOpacity == null ? 1 : o.strokeOpacity); + g.strokeStyle = color(g, o, stroke); + g.lineWidth = lw; + g.lineCap = (lc = o.strokeCap) != null ? lc : vg.config.render.lineCap; + g.vgLineDash(o.strokeDash || null); + g.vgLineDashOffset(o.strokeDashOffset || 0); + g.stroke(); + } + } + } + + function drawPathAll(path, g, scene, bounds) { + var i, len, item; + for (i = 0, len = scene.items.length; i < len; ++i) { + item = scene.items[i]; + if (bounds && !bounds.intersects(item.bounds)) + continue; // bounds check + drawPathOne(path, g, item, item); + } + } + + function drawRect(g, scene, bounds) { + if (!scene.items.length) return; + var items = scene.items, + o, fill, stroke, opac, lc, lw, x, y, w, h; + + for (var i = 0, len = items.length; i < len; ++i) { + o = items[i]; + if (bounds && !bounds.intersects(o.bounds)) + continue; // bounds check + + x = o.x || 0; + y = o.y || 0; + w = o.width || 0; + h = o.height || 0; + + opac = o.opacity == null ? 1 : o.opacity; + if (opac == 0) continue; + + if (fill = o.fill) { + g.globalAlpha = opac * (o.fillOpacity == null ? 1 : o.fillOpacity); + g.fillStyle = color(g, o, fill); + g.fillRect(x, y, w, h); + } + + if (stroke = o.stroke) { + lw = (lw = o.strokeWidth) != null ? lw : vg.config.render.lineWidth; + if (lw > 0) { + g.globalAlpha = opac * (o.strokeOpacity == null ? 1 : o.strokeOpacity); + g.strokeStyle = color(g, o, stroke); + g.lineWidth = lw; + g.lineCap = (lc = o.strokeCap) != null ? lc : vg.config.render.lineCap; + g.vgLineDash(o.strokeDash || null); + g.vgLineDashOffset(o.strokeDashOffset || 0); + g.strokeRect(x, y, w, h); + } + } + } + } + + function drawRule(g, scene, bounds) { + if (!scene.items.length) return; + var items = scene.items, + o, stroke, opac, lc, lw, x1, y1, x2, y2; + + for (var i = 0, len = items.length; i < len; ++i) { + o = items[i]; + if (bounds && !bounds.intersects(o.bounds)) + continue; // bounds check + + x1 = o.x || 0; + y1 = o.y || 0; + x2 = o.x2 != null ? o.x2 : x1; + y2 = o.y2 != null ? o.y2 : y1; + + opac = o.opacity == null ? 1 : o.opacity; + if (opac == 0) continue; + + if (stroke = o.stroke) { + lw = (lw = o.strokeWidth) != null ? lw : vg.config.render.lineWidth; + if (lw > 0) { + g.globalAlpha = opac * (o.strokeOpacity == null ? 1 : o.strokeOpacity); + g.strokeStyle = color(g, o, stroke); + g.lineWidth = lw; + g.lineCap = (lc = o.strokeCap) != null ? lc : vg.config.render.lineCap; + g.vgLineDash(o.strokeDash || null); + g.vgLineDashOffset(o.strokeDashOffset || 0); + g.beginPath(); + g.moveTo(x1, y1); + g.lineTo(x2, y2); + g.stroke(); + } + } + } + } + + function drawImage(g, scene, bounds) { + if (!scene.items.length) return; + var renderer = this, + items = scene.items, o; + + for (var i = 0, len = items.length; i < len; ++i) { + o = items[i]; + if (bounds && !bounds.intersects(o.bounds)) + continue; // bounds check + + if (!(o.image && o.image.url === o.url)) { + o.image = renderer.loadImage(o.url); + o.image.url = o.url; + } + + var x, y, w, h, opac; + w = o.width || (o.image && o.image.width) || 0; + h = o.height || (o.image && o.image.height) || 0; + x = (o.x || 0) - (o.align === "center" + ? w / 2 : (o.align === "right" ? w : 0)); + y = (o.y || 0) - (o.baseline === "middle" + ? h / 2 : (o.baseline === "bottom" ? h : 0)); + + if (o.image.loaded) { + g.globalAlpha = (opac = o.opacity) != null ? opac : 1; + g.drawImage(o.image, x, y, w, h); + } + } + } + + function drawText(g, scene, bounds) { + if (!scene.items.length) return; + var items = scene.items, + o, fill, stroke, opac, lw, x, y, r, t; + + for (var i = 0, len = items.length; i < len; ++i) { + o = items[i]; + if (bounds && !bounds.intersects(o.bounds)) + continue; // bounds check + + g.font = vg.scene.fontString(o); + g.textAlign = o.align || "left"; + g.textBaseline = o.baseline || "alphabetic"; + + opac = o.opacity == null ? 1 : o.opacity; + if (opac == 0) continue; + + x = o.x || 0; + y = o.y || 0; + if (r = o.radius) { + t = (o.theta || 0) - Math.PI / 2; + x += r * Math.cos(t); + y += r * Math.sin(t); + } + + if (o.angle) { + g.save(); + g.translate(x, y); + g.rotate(o.angle * Math.PI / 180); + x = o.dx || 0; + y = o.dy || 0; + } else { + x += (o.dx || 0); + y += (o.dy || 0); + } + + if (fill = o.fill) { + g.globalAlpha = opac * (o.fillOpacity == null ? 1 : o.fillOpacity); + g.fillStyle = color(g, o, fill); + g.fillText(o.text, x, y); + } + + if (stroke = o.stroke) { + lw = (lw = o.strokeWidth) != null ? lw : 1; + if (lw > 0) { + g.globalAlpha = opac * (o.strokeOpacity == null ? 1 : o.strokeOpacity); + g.strokeStyle = color(o, stroke); + g.lineWidth = lw; + g.strokeText(o.text, x, y); + } + } + + if (o.angle) g.restore(); + } + } + + function drawAll(pathFunc) { + return function (g, scene, bounds) { + drawPathAll(pathFunc, g, scene, bounds); + } + } + + function drawOne(pathFunc) { + return function (g, scene, bounds) { + if (!scene.items.length) return; + if (bounds && !bounds.intersects(scene.items[0].bounds)) + return; // bounds check + drawPathOne(pathFunc, g, scene.items[0], scene.items); + } + } + + function drawGroup(g, scene, bounds) { + if (!scene.items.length) return; + var items = scene.items, group, axes, legends, + renderer = this, gx, gy, gb, i, n, j, m; + + drawRect(g, scene, bounds); + + for (i = 0, n = items.length; i < n; ++i) { + group = items[i]; + axes = group.axisItems || []; + legends = group.legendItems || []; + gx = group.x || 0; + gy = group.y || 0; + + // render group contents + g.save(); + g.translate(gx, gy); + if (group.clip) { + g.beginPath(); + g.rect(0, 0, group.width || 0, group.height || 0); + g.clip(); + } + + if (bounds) bounds.translate(-gx, -gy); + + for (j = 0, m = axes.length; j < m; ++j) { + if (axes[j].def.layer === "back") { + renderer.draw(g, axes[j], bounds); + } + } + for (j = 0, m = group.items.length; j < m; ++j) { + renderer.draw(g, group.items[j], bounds); + } + for (j = 0, m = axes.length; j < m; ++j) { + if (axes[j].def.layer !== "back") { + renderer.draw(g, axes[j], bounds); + } + } + for (j = 0, m = legends.length; j < m; ++j) { + renderer.draw(g, legends[j], bounds); + } + + if (bounds) bounds.translate(gx, gy); + g.restore(); + } + } + + function color(g, o, value) { + return (value.id) + ? gradient(g, value, o.bounds) + : value; + } + + function gradient(g, p, b) { + var w = b.width(), + h = b.height(), + x1 = b.x1 + p.x1 * w, + y1 = b.y1 + p.y1 * h, + x2 = b.x1 + p.x2 * w, + y2 = b.y1 + p.y2 * h, + grad = g.createLinearGradient(x1, y1, x2, y2), + stop = p.stops, + i, n; + + for (i = 0, n = stop.length; i < n; ++i) { + grad.addColorStop(stop[i].offset, stop[i].color); + } + return grad; + } + + // hit testing + + function pickGroup(g, scene, x, y, gx, gy) { + if (scene.items.length === 0 || + scene.bounds && !scene.bounds.contains(gx, gy)) { + return false; + } + var items = scene.items, subscene, group, hit, dx, dy, + handler = this, i, j; + + for (i = items.length; --i >= 0;) { + group = items[i]; + dx = group.x || 0; + dy = group.y || 0; + + g.save(); + g.translate(dx, dy); + for (j = group.items.length; --j >= 0;) { + subscene = group.items[j]; + if (subscene.interactive === false) continue; + hit = handler.pick(subscene, x, y, gx - dx, gy - dy); + if (hit) { + g.restore(); + return hit; + } + } + g.restore(); + } + + return scene.interactive + ? pickAll(hitTests.group, g, scene, x, y, gx, gy) + : false; + } + + function pickAll(test, g, scene, x, y, gx, gy) { + if (!scene.items.length) return false; + var o, b, i; + + if (g._ratio !== 1) { + x *= g._ratio; + y *= g._ratio; + } + + for (i = scene.items.length; --i >= 0;) { + o = scene.items[i]; + b = o.bounds; + // first hit test against bounding box + if ((b && !b.contains(gx, gy)) || !b) continue; + // if in bounding box, perform more careful test + if (test(g, o, x, y, gx, gy)) return o; + } + return false; + } + + function pickArea(g, scene, x, y, gx, gy) { + if (!scene.items.length) return false; + var items = scene.items, + o, b, i, di, dd, od, dx, dy; + + b = items[0].bounds; + if (b && !b.contains(gx, gy)) return false; + if (g._ratio !== 1) { + x *= g._ratio; + y *= g._ratio; + } + if (!hitTests.area(g, items, x, y)) return false; + return items[0]; + } + + function pickLine(g, scene, x, y, gx, gy) { + if (!scene.items.length) return false; + var items = scene.items, + o, b, i, di, dd, od, dx, dy; + + b = items[0].bounds; + if (b && !b.contains(gx, gy)) return false; + if (g._ratio !== 1) { + x *= g._ratio; + y *= g._ratio; + } + if (!hitTests.line(g, items, x, y)) return false; + return items[0]; + } + + function pick(test) { + return function (g, scene, x, y, gx, gy) { + return pickAll(test, g, scene, x, y, gx, gy); + }; + } + + function textHit(g, o, x, y, gx, gy) { + if (!o.fontSize) return false; + if (!o.angle) return true; // bounds sufficient if no rotation + + var b = vg.scene.bounds.text(o, tmpBounds, true), + a = -o.angle * Math.PI / 180, + cos = Math.cos(a), + sin = Math.sin(a), + x = o.x, + y = o.y, + px = cos * gx - sin * gy + (x - x * cos + y * sin), + py = sin * gx + cos * gy + (y - x * sin - y * cos); + + return b.contains(px, py); + } + + var hitTests = { + text: textHit, + rect: function (g, o, x, y) { + return true; + }, // bounds test is sufficient + image: function (g, o, x, y) { + return true; + }, // bounds test is sufficient + group: function (g, o, x, y) { + return o.fill || o.stroke; + }, + rule: function (g, o, x, y) { + if (!g.isPointInStroke) return false; + ruleStroke(g, o); + return g.isPointInStroke(x, y); + }, + line: function (g, s, x, y) { + if (!g.isPointInStroke) return false; + lineStroke(g, s); + return g.isPointInStroke(x, y); + }, + arc: function (g, o, x, y) { + arcPath(g, o); + return g.isPointInPath(x, y); + }, + area: function (g, s, x, y) { + areaPath(g, s); + return g.isPointInPath(x, y); + }, + path: function (g, o, x, y) { + pathPath(g, o); + return g.isPointInPath(x, y); + }, + symbol: function (g, o, x, y) { + symbolPath(g, o); + return g.isPointInPath(x, y); + } + }; + + return { + draw: { + group: drawGroup, + area: drawOne(areaPath), + line: drawOne(linePath), + arc: drawAll(arcPath), + path: drawAll(pathPath), + symbol: drawAll(symbolPath), + rect: drawRect, + rule: drawRule, + text: drawText, + image: drawImage, + drawOne: drawOne, // expose for extensibility + drawAll: drawAll // expose for extensibility + }, + pick: { + group: pickGroup, + area: pickArea, + line: pickLine, + arc: pick(hitTests.arc), + path: pick(hitTests.path), + symbol: pick(hitTests.symbol), + rect: pick(hitTests.rect), + rule: pick(hitTests.rule), + text: pick(hitTests.text), + image: pick(hitTests.image), + pickAll: pickAll // expose for extensibility + } + }; + + })(); + vg.canvas.Renderer = (function () { + var renderer = function () { + this._ctx = null; + this._el = null; + this._imgload = 0; + }; + + var prototype = renderer.prototype; + + prototype.initialize = function (el, width, height, pad) { + this._el = el; + + if (!el) return this; // early exit if no DOM element + + // select canvas element + var canvas = d3.select(el) + .selectAll("canvas.marks") + .data([1]); + + // create new canvas element if needed + canvas.enter() + .append("canvas") + .attr("class", "marks"); + + // remove extraneous canvas if needed + canvas.exit().remove(); + + return this.resize(width, height, pad); + }; + + prototype.resize = function (width, height, pad) { + this._width = width; + this._height = height; + this._padding = pad; + + if (this._el) { + var canvas = d3.select(this._el).select("canvas.marks"); + + // initialize canvas attributes + canvas + .attr("width", width + pad.left + pad.right) + .attr("height", height + pad.top + pad.bottom); + + // get the canvas graphics context + var s; + this._ctx = canvas.node().getContext("2d"); + this._ctx._ratio = (s = scaleCanvas(canvas.node(), this._ctx) || 1); + this._ctx.setTransform(s, 0, 0, s, s * pad.left, s * pad.top); + } + + initializeLineDash(this._ctx); + return this; + }; + + function scaleCanvas(canvas, ctx) { + // get canvas pixel data + var devicePixelRatio = window.devicePixelRatio || 1, + backingStoreRatio = ( + ctx.webkitBackingStorePixelRatio || + ctx.mozBackingStorePixelRatio || + ctx.msBackingStorePixelRatio || + ctx.oBackingStorePixelRatio || + ctx.backingStorePixelRatio) || 1, + ratio = devicePixelRatio / backingStoreRatio; + + if (devicePixelRatio !== backingStoreRatio) { + var w = canvas.width, h = canvas.height; + // set actual and visible canvas size + canvas.setAttribute("width", w * ratio); + canvas.setAttribute("height", h * ratio); + canvas.style.width = w + 'px'; + canvas.style.height = h + 'px'; + } + return ratio; + } + + function initializeLineDash(ctx) { + if (ctx.vgLineDash) return; // already set + + var NODASH = []; + if (ctx.setLineDash) { + ctx.vgLineDash = function (dash) { + this.setLineDash(dash || NODASH); + }; + ctx.vgLineDashOffset = function (off) { + this.lineDashOffset = off; + }; + } else if (ctx.webkitLineDash !== undefined) { + ctx.vgLineDash = function (dash) { + this.webkitLineDash = dash || NODASH; + }; + ctx.vgLineDashOffset = function (off) { + this.webkitLineDashOffset = off; + }; + } else if (ctx.mozDash !== undefined) { + ctx.vgLineDash = function (dash) { + this.mozDash = dash; + }; + ctx.vgLineDashOffset = function (off) { /* unsupported */ + }; + } else { + ctx.vgLineDash = function (dash) { /* unsupported */ + }; + ctx.vgLineDashOffset = function (off) { /* unsupported */ + }; + } + } + + prototype.context = function (ctx) { + if (ctx) { + this._ctx = ctx; + return this; + } + else return this._ctx; + }; + + prototype.element = function () { + return this._el; + }; + + prototype.pendingImages = function () { + return this._imgload; + }; + + function translatedBounds(item, bounds) { + var b = new vg.Bounds(bounds); + while ((item = item.mark.group) != null) { + b.translate(item.x || 0, item.y || 0); + } + return b; + } + + function getBounds(items) { + return !items ? null : + vg.array(items).reduce(function (b, item) { + return b.union(translatedBounds(item, item.bounds)) + .union(translatedBounds(item, item['bounds:prev'])); + }, new vg.Bounds()); + } + + function setBounds(g, bounds) { + var bbox = null; + if (bounds) { + bbox = (new vg.Bounds(bounds)).round(); + g.beginPath(); + g.rect(bbox.x1, bbox.y1, bbox.width(), bbox.height()); + g.clip(); + } + return bbox; + } + + prototype.render = function (scene, items) { + var g = this._ctx, + pad = this._padding, + w = this._width + pad.left + pad.right, + h = this._height + pad.top + pad.bottom, + bb = null, bb2; + + // setup + this._scene = scene; + g.save(); + bb = setBounds(g, getBounds(items)); + g.clearRect(-pad.left, -pad.top, w, h); + + // render + this.draw(g, scene, bb); + + // render again to handle possible bounds change + if (items) { + g.restore(); + g.save(); + bb2 = setBounds(g, getBounds(items)); + if (!bb.encloses(bb2)) { + g.clearRect(-pad.left, -pad.top, w, h); + this.draw(g, scene, bb2); + } + } + + // takedown + g.restore(); + this._scene = null; + }; + + prototype.draw = function (ctx, scene, bounds) { + var marktype = scene.marktype, + renderer = vg.canvas.marks.draw[marktype]; + renderer.call(this, ctx, scene, bounds); + }; + + prototype.renderAsync = function (scene) { + // TODO make safe for multiple scene rendering? + var renderer = this; + if (renderer._async_id) { + clearTimeout(renderer._async_id); + } + renderer._async_id = setTimeout(function () { + renderer.render(scene); + delete renderer._async_id; + }, 50); + }; + + prototype.loadImage = function (uri) { + var renderer = this, + scene = renderer._scene, + image = null, url; + + renderer._imgload += 1; + if (vg.config.isNode) { + image = new (require("canvas").Image)(); + vg.data.load(uri, function (err, data) { + if (err) { + vg.error(err); + return; + } + image.src = data; + image.loaded = true; + renderer._imgload -= 1; + }); + } else { + image = new Image(); + url = vg.config.baseURL + uri; + image.onload = function () { + vg.log("LOAD IMAGE: " + url); + image.loaded = true; + renderer._imgload -= 1; + renderer.renderAsync(scene); + }; + image.src = url; + } + + return image; + }; + + return renderer; + })(); + vg.canvas.Handler = (function () { + var handler = function (el, model) { + this._active = null; + this._handlers = {}; + if (el) this.initialize(el); + if (model) this.model(model); + }; + + var prototype = handler.prototype; + + prototype.initialize = function (el, pad, obj) { + this._el = d3.select(el).node(); + this._canvas = d3.select(el).select("canvas.marks").node(); + this._padding = pad; + this._obj = obj || null; + + // add event listeners + var canvas = this._canvas, that = this; + events.forEach(function (type) { + canvas.addEventListener(type, function (evt) { + prototype[type].call(that, evt); + }); + }); + + return this; + }; + + prototype.padding = function (pad) { + this._padding = pad; + return this; + }; + + prototype.model = function (model) { + if (!arguments.length) return this._model; + this._model = model; + return this; + }; + + prototype.handlers = function () { + var h = this._handlers; + return vg.keys(h).reduce(function (a, k) { + return h[k].reduce(function (a, x) { + return (a.push(x), a); + }, a); + }, []); + }; + + // setup events + var events = [ + "mousedown", + "mouseup", + "click", + "dblclick", + "wheel", + "keydown", + "keypress", + "keyup", + "mousewheel" + ]; + events.forEach(function (type) { + prototype[type] = function (evt) { + this.fire(type, evt); + }; + }); + events.push("mousemove"); + events.push("mouseout"); + + function eventName(name) { + var i = name.indexOf("."); + return i < 0 ? name : name.slice(0, i); + } + + prototype.mousemove = function (evt) { + var pad = this._padding, + b = evt.target.getBoundingClientRect(), + x = evt.clientX - b.left, + y = evt.clientY - b.top, + a = this._active, + p = this.pick(this._model.scene(), x, y, x - pad.left, y - pad.top); + + if (p === a) { + this.fire("mousemove", evt); + return; + } else if (a) { + this.fire("mouseout", evt); + } + this._active = p; + if (p) { + this.fire("mouseover", evt); + } + }; + + prototype.mouseout = function (evt) { + if (this._active) { + this.fire("mouseout", evt); + } + this._active = null; + }; + + // to keep firefox happy + prototype.DOMMouseScroll = function (evt) { + this.fire("mousewheel", evt); + }; + + // fire an event + prototype.fire = function (type, evt) { + var a = this._active, + h = this._handlers[type]; + if (a && h) { + for (var i = 0, len = h.length; i < len; ++i) { + h[i].handler.call(this._obj, evt, a); + } + } + }; + + // add an event handler + prototype.on = function (type, handler) { + var name = eventName(type), + h = this._handlers; + h = h[name] || (h[name] = []); + h.push({ + type: type, + handler: handler + }); + return this; + }; + + // remove an event handler + prototype.off = function (type, handler) { + var name = eventName(type), + h = this._handlers[name]; + if (!h) return; + for (var i = h.length; --i >= 0;) { + if (h[i].type !== type) continue; + if (!handler || h[i].handler === handler) h.splice(i, 1); + } + return this; + }; + + // retrieve the current canvas context + prototype.context = function () { + return this._canvas.getContext("2d");
<TRUNCATED>
