summaryrefslogtreecommitdiffstats
path: root/hyperkitty/static
diff options
context:
space:
mode:
Diffstat (limited to 'hyperkitty/static')
-rw-r--r--hyperkitty/static/css/hyperkitty-overview.css10
-rw-r--r--hyperkitty/static/js/hyperkitty.js130
-rw-r--r--hyperkitty/static/libs/d3.v2.min.js4
-rw-r--r--hyperkitty/static/libs/protovis-d3.1.js7725
4 files changed, 92 insertions, 7777 deletions
diff --git a/hyperkitty/static/css/hyperkitty-overview.css b/hyperkitty/static/css/hyperkitty-overview.css
index 458586b..cb489da 100644
--- a/hyperkitty/static/css/hyperkitty-overview.css
+++ b/hyperkitty/static/css/hyperkitty-overview.css
@@ -21,6 +21,16 @@
height: 330px;
}
+#fig .axis path, #fig .axis line {
+ fill: none;
+ stroke: #000;
+ shape-rendering: geometricPrecision;
+}
+
+#fig .area {
+ fill: steelBlue;
+}
+
#top-discussion,
#discussion-by-topic,
diff --git a/hyperkitty/static/js/hyperkitty.js b/hyperkitty/static/js/hyperkitty.js
index eeec569..becc171 100644
--- a/hyperkitty/static/js/hyperkitty.js
+++ b/hyperkitty/static/js/hyperkitty.js
@@ -210,62 +210,88 @@ function setup_replies() {
/*
* Recent activity graph
*/
-
-function activity_graph(dates, data, baseurl) {
- var w = 500,
- h = 300,
- x = pv.Scale.ordinal(pv.range(32)).splitBanded(0, w, 4/5),
- y = pv.Scale.linear(0, Math.max.apply(null, data)+1).range(0, h);
-
- var vis = new pv.Panel()
- .width(w)
- .height(250)
- .bottom(60)
- .left(30)
- .right(5)
- .top(5);
-
- var bar = vis.add(pv.Bar)
- .data(data)
- .event("click", function(n) { self.location = baseurl + "/" + dates[this.index].replace(/-/g, '/') + "/"; })
- .left(function() x(this.index))
- .width(x.range().band)
- .bottom(0)
- .height(y);
-
- bar.anchor("bottom").add(pv.Label)
- .textMargin(5)
- .textAlign("right")
- .textBaseline("middle")
- .textAngle(-Math.PI / 3)
- .text(function() xlabel(this.index));
-
- function xlabel(ind) {
- if (!dates[ind -1]) {
- return dates[ind];
- }
- prev = dates[ind - 1];
- cur = dates[ind];
- if (prev.substring(0,7) == cur.substring(0,7)) {
- cur = cur.substring(8);
+function activity_graph(elem_id, dates, counts, baseurl) {
+ function merge(dates, counts) {
+ result = []
+ for(i = 0; i < dates.length; i++) {
+ result.push({
+ "date": dates[i],
+ "count": counts[i]
+ })
}
- return cur;
+ return result;
}
+ var data = merge(dates, counts);
+ var margin = {top: 20, right: 20, bottom: 100, left: 50},
+ width = 540 - margin.left - margin.right,
+ height = 330 - margin.top - margin.bottom;
+
+ var format_in = d3.time.format("%Y-%m-%d");
+ var format_out = d3.time.format("%m-%d");
+
+ var x = d3.time.scale()
+ .range([0, width]);
+
+ var y = d3.scale.linear()
+ .range([height, 0]);
+
+ var xAxis = d3.svg.axis()
+ .scale(x)
+ .orient("bottom")
+ .tickFormat(format_out)
+ .ticks(d3.time.days, 2);
+
+ var yAxis = d3.svg.axis()
+ .scale(y)
+ .orient("left")
+ .ticks(5)
+ .tickSubdivide(1);
+
+ var area = d3.svg.area()
+ .x(function(d) { return x(d.date); })
+ .y0(height)
+ .y1(function(d) { return y(d.count); });
+
+ var svg = d3.select(elem_id).append("svg")
+ .attr("width", width + margin.left + margin.right)
+ .attr("height", height + margin.top + margin.bottom)
+ .append("g")
+ .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
+
+ data.forEach(function(d) {
+ d.date = format_in.parse(d.date);
+ d.count = parseInt(d.count);
+ });
- vis.add(pv.Rule)
- .data(y.ticks())
- .bottom(function(d) Math.round(y(d)) - .5)
- .strokeStyle(function(d) d ? "rgba(255,255,255,.3)" : "#000")
- .add(pv.Rule)
- .left(0)
- .width(5)
- .strokeStyle("#000")
- .anchor("left").add(pv.Label)
- .text(function(d) d.toFixed(1));
-
- vis.render();
-}
+ x.domain(d3.extent(data, function(d) { return d.date; }));
+ y.domain([0, d3.max(data, function(d) { return d.count; })]);
+
+ svg.append("path")
+ .datum(data)
+ .attr("class", "area")
+ .attr("d", area);
+
+ svg.append("g")
+ .attr("class", "x axis")
+ .attr("transform", "translate(0," + height + ")")
+ .call(xAxis)
+ .selectAll("text")
+ .attr("y", -5)
+ .attr("x", -30)
+ .attr("transform", function(d) {
+ return "rotate(-90)"
+ });
+ svg.append("g")
+ .attr("class", "y axis")
+ .call(yAxis)
+ .append("text")
+ .attr("transform", "rotate(-90)")
+ .attr("y", 6)
+ .attr("dy", ".71em")
+ .style("text-anchor", "end")
+ .text("Messages");
+}
/*
* Misc.
diff --git a/hyperkitty/static/libs/d3.v2.min.js b/hyperkitty/static/libs/d3.v2.min.js
new file mode 100644
index 0000000..cc47f1e
--- /dev/null
+++ b/hyperkitty/static/libs/d3.v2.min.js
@@ -0,0 +1,4 @@
+(function(){function e(e,t){try{for(var n in t)Object.defineProperty(e.prototype,n,{value:t[n],enumerable:!1})}catch(r){e.prototype=t}}function t(e){var t=-1,n=e.length,r=[];while(++t<n)r.push(e[t]);return r}function n(e){return Array.prototype.slice.call(e)}function r(){}function i(e){return e}function s(){return this}function o(){return!0}function u(e){return typeof e=="function"?e:function(){return e}}function a(e,t,n){return function(){var r=n.apply(t,arguments);return arguments.length?e:r}}function f(e){return e!=null&&!isNaN(e)}function l(e){return e.length}function c(e){return e==null}function h(e){return e.trim().replace(/\s+/g," ")}function p(e){var t=1;while(e*t%1)t*=10;return t}function d(){}function v(e){function t(){var t=n,r=-1,i=t.length,s;while(++r<i)(s=t[r].on)&&s.apply(this,arguments);return e}var n=[],i=new r;return t.on=function(t,r){var s=i.get(t),o;return arguments.length<2?s&&s.on:(s&&(s.on=null,n=n.slice(0,o=n.indexOf(s)).concat(n.slice(o+1)),i.remove(t)),r&&n.push(i.set(t,{on:r})),e)},t}function m(e,t){return t-(e?1+Math.floor(Math.log(e+Math.pow(10,1+Math.floor(Math.log(e)/Math.LN10)-t))/Math.LN10):1)}function g(e){return e+""}function y(e){var t=e.lastIndexOf("."),n=t>=0?e.substring(t):(t=e.length,""),r=[];while(t>0)r.push(e.substring(t-=3,t+3));return r.reverse().join(",")+n}function b(e,t){var n=Math.pow(10,Math.abs(8-t)*3);return{scale:t>8?function(e){return e/n}:function(e){return e*n},symbol:e}}function w(e){return function(t){return t<=0?0:t>=1?1:e(t)}}function E(e){return function(t){return 1-e(1-t)}}function S(e){return function(t){return.5*(t<.5?e(2*t):2-e(2-2*t))}}function x(e){return e}function T(e){return function(t){return Math.pow(t,e)}}function N(e){return 1-Math.cos(e*Math.PI/2)}function C(e){return Math.pow(2,10*(e-1))}function k(e){return 1-Math.sqrt(1-e*e)}function L(e,t){var n;return arguments.length<2&&(t=.45),arguments.length<1?(e=1,n=t/4):n=t/(2*Math.PI)*Math.asin(1/e),function(r){return 1+e*Math.pow(2,10*-r)*Math.sin((r-n)*2*Math.PI/t)}}function A(e){return e||(e=1.70158),function(t){return t*t*((e+1)*t-e)}}function O(e){return e<1/2.75?7.5625*e*e:e<2/2.75?7.5625*(e-=1.5/2.75)*e+.75:e<2.5/2.75?7.5625*(e-=2.25/2.75)*e+.9375:7.5625*(e-=2.625/2.75)*e+.984375}function M(){d3.event.stopPropagation(),d3.event.preventDefault()}function _(){var e=d3.event,t;while(t=e.sourceEvent)e=t;return e}function D(e){var t=new d,n=0,r=arguments.length;while(++n<r)t[arguments[n]]=v(t);return t.of=function(n,r){return function(i){try{var s=i.sourceEvent=d3.event;i.target=e,d3.event=i,t[i.type].apply(n,r)}finally{d3.event=s}}},t}function P(e){var t=[e.a,e.b],n=[e.c,e.d],r=B(t),i=H(t,n),s=B(j(n,t,-i))||0;t[0]*n[1]<n[0]*t[1]&&(t[0]*=-1,t[1]*=-1,r*=-1,i*=-1),this.rotate=(r?Math.atan2(t[1],t[0]):Math.atan2(-n[0],n[1]))*fs,this.translate=[e.e,e.f],this.scale=[r,s],this.skew=s?Math.atan2(i,s)*fs:0}function H(e,t){return e[0]*t[0]+e[1]*t[1]}function B(e){var t=Math.sqrt(H(e,e));return t&&(e[0]/=t,e[1]/=t),t}function j(e,t,n){return e[0]+=n*t[0],e[1]+=n*t[1],e}function F(e){return e=="transform"?d3.interpolateTransform:d3.interpolate}function I(e,t){return t=t-(e=+e)?1/(t-e):0,function(n){return(n-e)*t}}function q(e,t){return t=t-(e=+e)?1/(t-e):0,function(n){return Math.max(0,Math.min(1,(n-e)*t))}}function R(e,t,n){return new U(e,t,n)}function U(e,t,n){this.r=e,this.g=t,this.b=n}function z(e){return e<16?"0"+Math.max(0,e).toString(16):Math.min(255,e).toString(16)}function W(e,t,n){var r=0,i=0,s=0,o,u,a;o=/([a-z]+)\((.*)\)/i.exec(e);if(o){u=o[2].split(",");switch(o[1]){case"hsl":return n(parseFloat(u[0]),parseFloat(u[1])/100,parseFloat(u[2])/100);case"rgb":return t(J(u[0]),J(u[1]),J(u[2]))}}return(a=hs.get(e))?t(a.r,a.g,a.b):(e!=null&&e.charAt(0)==="#"&&(e.length===4?(r=e.charAt(1),r+=r,i=e.charAt(2),i+=i,s=e.charAt(3),s+=s):e.length===7&&(r=e.substring(1,3),i=e.substring(3,5),s=e.substring(5,7)),r=parseInt(r,16),i=parseInt(i,16),s=parseInt(s,16)),t(r,i,s))}function X(e,t,n){var r=Math.min(e/=255,t/=255,n/=255),i=Math.max(e,t,n),s=i-r,o,u,a=(i+r)/2;return s?(u=a<.5?s/(i+r):s/(2-i-r),e==i?o=(t-n)/s+(t<n?6:0):t==i?o=(n-e)/s+2:o=(e-t)/s+4,o*=60):u=o=0,K(o,u,a)}function V(e,t,n){e=$(e),t=$(t),n=$(n);var r=ot((.4124564*e+.3575761*t+.1804375*n)/ds),i=ot((.2126729*e+.7151522*t+.072175*n)/vs),s=ot((.0193339*e+.119192*t+.9503041*n)/ms);return tt(116*i-16,500*(r-i),200*(i-s))}function $(e){return(e/=255)<=.04045?e/12.92:Math.pow((e+.055)/1.055,2.4)}function J(e){var t=parseFloat(e);return e.charAt(e.length-1)==="%"?Math.round(t*2.55):t}function K(e,t,n){return new Q(e,t,n)}function Q(e,t,n){this.h=e,this.s=t,this.l=n}function G(e,t,n){function r(e){return e>360?e-=360:e<0&&(e+=360),e<60?s+(o-s)*e/60:e<180?o:e<240?s+(o-s)*(240-e)/60:s}function i(e){return Math.round(r(e)*255)}var s,o;return e%=360,e<0&&(e+=360),t=t<0?0:t>1?1:t,n=n<0?0:n>1?1:n,o=n<=.5?n*(1+t):n+t-n*t,s=2*n-o,R(i(e+120),i(e),i(e-120))}function Y(e,t,n){return new Z(e,t,n)}function Z(e,t,n){this.h=e,this.c=t,this.l=n}function et(e,t,n){return tt(n,Math.cos(e*=Math.PI/180)*t,Math.sin(e)*t)}function tt(e,t,n){return new nt(e,t,n)}function nt(e,t,n){this.l=e,this.a=t,this.b=n}function rt(e,t,n){var r=(e+16)/116,i=r+t/500,s=r-n/200;return i=st(i)*ds,r=st(r)*vs,s=st(s)*ms,R(ut(3.2404542*i-1.5371385*r-.4985314*s),ut(-0.969266*i+1.8760108*r+.041556*s),ut(.0556434*i-.2040259*r+1.0572252*s))}function it(e,t,n){return Y(Math.atan2(n,t)/Math.PI*180,Math.sqrt(t*t+n*n),e)}function st(e){return e>.206893034?e*e*e:(e-4/29)/7.787037}function ot(e){return e>.008856?Math.pow(e,1/3):7.787037*e+4/29}function ut(e){return Math.round(255*(e<=.00304?12.92*e:1.055*Math.pow(e,1/2.4)-.055))}function at(e){return Ki(e,Ss),e}function ft(e){return function(){return gs(e,this)}}function lt(e){return function(){return ys(e,this)}}function ct(e,t){function n(){this.removeAttribute(e)}function r(){this.removeAttributeNS(e.space,e.local)}function i(){this.setAttribute(e,t)}function s(){this.setAttributeNS(e.space,e.local,t)}function o(){var n=t.apply(this,arguments);n==null?this.removeAttribute(e):this.setAttribute(e,n)}function u(){var n=t.apply(this,arguments);n==null?this.removeAttributeNS(e.space,e.local):this.setAttributeNS(e.space,e.local,n)}return e=d3.ns.qualify(e),t==null?e.local?r:n:typeof t=="function"?e.local?u:o:e.local?s:i}function ht(e){return new RegExp("(?:^|\\s+)"+d3.requote(e)+"(?:\\s+|$)","g")}function pt(e,t){function n(){var n=-1;while(++n<i)e[n](this,t)}function r(){var n=-1,r=t.apply(this,arguments);while(++n<i)e[n](this,r)}e=e.trim().split(/\s+/).map(dt);var i=e.length;return typeof t=="function"?r:n}function dt(e){var t=ht(e);return function(n,r){if(i=n.classList)return r?i.add(e):i.remove(e);var i=n.className,s=i.baseVal!=null,o=s?i.baseVal:i;r?(t.lastIndex=0,t.test(o)||(o=h(o+" "+e),s?i.baseVal=o:n.className=o)):o&&(o=h(o.replace(t," ")),s?i.baseVal=o:n.className=o)}}function vt(e,t,n){function r(){this.style.removeProperty(e)}function i(){this.style.setProperty(e,t,n)}function s(){var r=t.apply(this,arguments);r==null?this.style.removeProperty(e):this.style.setProperty(e,r,n)}return t==null?r:typeof t=="function"?s:i}function mt(e,t){function n(){delete this[e]}function r(){this[e]=t}function i(){var n=t.apply(this,arguments);n==null?delete this[e]:this[e]=n}return t==null?n:typeof t=="function"?i:r}function gt(e){return{__data__:e}}function yt(e){return function(){return Es(this,e)}}function bt(e){return arguments.length||(e=d3.ascending),function(t,n){return e(t&&t.__data__,n&&n.__data__)}}function wt(e,t,n){function r(){var t=this[s];t&&(this.removeEventListener(e,t,t.$),delete this[s])}function i(){function i(e){var n=d3.event;d3.event=e,u[0]=o.__data__;try{t.apply(o,u)}finally{d3.event=n}}var o=this,u=arguments;r.call(this),this.addEventListener(e,this[s]=i,i.$=n),i._=t}var s="__on"+e,o=e.indexOf(".");return o>0&&(e=e.substring(0,o)),t?i:r}function Et(e,t){for(var n=0,r=e.length;n<r;n++)for(var i=e[n],s=0,o=i.length,u;s<o;s++)(u=i[s])&&t(u,s,n);return e}function St(e){return Ki(e,Ts),e}function xt(e,t,n){Ki(e,Ns);var i=new r,s=d3.dispatch("start","end"),o=Ds;return e.id=t,e.time=n,e.tween=function(t,n){return arguments.length<2?i.get(t):(n==null?i.remove(t):i.set(t,n),e)},e.ease=function(t){return arguments.length?(o=typeof t=="function"?t:d3.ease.apply(d3,arguments),e):o},e.each=function(t,n){return arguments.length<2?Tt.call(e,t):(s.on(t,n),e)},d3.timer(function(r){return Et(e,function(e,u,a){function f(r){return v.active>t?c():(v.active=t,i.forEach(function(t,n){(n=n.call(e,m,u))&&h.push(n)}),s.start.call(e,m,u),l(r)||d3.timer(l,0,n),1)}function l(n){if(v.active!==t)return c();var r=(n-p)/d,i=o(r),a=h.length;while(a>0)h[--a].call(e,i);if(r>=1)return c(),ks=t,s.end.call(e,m,u),ks=0,1}function c(){return--v.count||delete e.__transition__,1}var h=[],p=e.delay,d=e.duration,v=(e=e.node).__transition__||(e.__transition__={active:0,count:0}),m=e.__data__;++v.count,p<=r?f(r):d3.timer(f,p,n)})},0,n),e}function Tt(e){var t=ks,n=Ds,r=Ms,i=_s;return ks=this.id,Ds=this.ease(),Et(this,function(t,n,r){Ms=t.delay,_s=t.duration,e.call(t=t.node,t.__data__,n,r)}),ks=t,Ds=n,Ms=r,_s=i,this}function Nt(e,t,n){return n!=""&&Ps}function Ct(e,t){return d3.tween(e,F(t))}function kt(){var e,t=Date.now(),n=Hs;while(n)e=t-n.then,e>=n.delay&&(n.flush=n.callback(e)),n=n.next;var r=Lt()-t;r>24?(isFinite(r)&&(clearTimeout(js),js=setTimeout(kt,r)),Bs=0):(Bs=1,Fs(kt))}function Lt(){var e=null,t=Hs,n=Infinity;while(t)t.flush?t=e?e.next=t.next:Hs=t.next:(n=Math.min(n,t.then+t.delay),t=(e=t).next);return n}function At(e,t){var n=e.ownerSVGElement||e;if(n.createSVGPoint){var r=n.createSVGPoint();if(Is<0&&(window.scrollX||window.scrollY)){n=d3.select(document.body).append("svg").style("position","absolute").style("top",0).style("left",0);var i=n[0][0].getScreenCTM();Is=!i.f&&!i.e,n.remove()}return Is?(r.x=t.pageX,r.y=t.pageY):(r.x=t.clientX,r.y=t.clientY),r=r.matrixTransform(e.getScreenCTM().inverse()),[r.x,r.y]}var s=e.getBoundingClientRect();return[t.clientX-s.left-e.clientLeft,t.clientY-s.top-e.clientTop]}function Ot(){}function Mt(e){var t=e[0],n=e[e.length-1];return t<n?[t,n]:[n,t]}function _t(e){return e.rangeExtent?e.rangeExtent():Mt(e.range())}function Dt(e,t){var n=0,r=e.length-1,i=e[n],s=e[r],o;s<i&&(o=n,n=r,r=o,o=i,i=s,s=o);if(t=t(s-i))e[n]=t.floor(i),e[r]=t.ceil(s);return e}function Pt(){return Math}function Ht(e,t,n,r){function i(){var i=Math.min(e.length,t.length)>2?Ut:Rt,a=r?q:I;return o=i(e,t,a,n),u=i(t,e,a,d3.interpolate),s}function s(e){return o(e)}var o,u;return s.invert=function(e){return u(e)},s.domain=function(t){return arguments.length?(e=t.map(Number),i()):e},s.range=function(e){return arguments.length?(t=e,i()):t},s.rangeRound=function(e){return s.range(e).interpolate(d3.interpolateRound)},s.clamp=function(e){return arguments.length?(r=e,i()):r},s.interpolate=function(e){return arguments.length?(n=e,i()):n},s.ticks=function(t){return It(e,t)},s.tickFormat=function(t){return qt(e,t)},s.nice=function(){return Dt(e,jt),i()},s.copy=function(){return Ht(e,t,n,r)},i()}function Bt(e,t){return d3.rebind(e,t,"range","rangeRound","interpolate","clamp")}function jt(e){return e=Math.pow(10,Math.round(Math.log(e)/Math.LN10)-1),e&&{floor:function(t){return Math.floor(t/e)*e},ceil:function(t){return Math.ceil(t/e)*e}}}function Ft(e,t){var n=Mt(e),r=n[1]-n[0],i=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),s=t/r*i;return s<=.15?i*=10:s<=.35?i*=5:s<=.75&&(i*=2),n[0]=Math.ceil(n[0]/i)*i,n[1]=Math.floor(n[1]/i)*i+i*.5,n[2]=i,n}function It(e,t){return d3.range.apply(d3,Ft(e,t))}function qt(e,t){return d3.format(",."+Math.max(0,-Math.floor(Math.log(Ft(e,t)[2])/Math.LN10+.01))+"f")}function Rt(e,t,n,r){var i=n(e[0],e[1]),s=r(t[0],t[1]);return function(e){return s(i(e))}}function Ut(e,t,n,r){var i=[],s=[],o=0,u=Math.min(e.length,t.length)-1;e[u]<e[0]&&(e=e.slice().reverse(),t=t.slice().reverse());while(++o<=u)i.push(n(e[o-1],e[o])),s.push(r(t[o-1],t[o]));return function(t){var n=d3.bisect(e,t,1,u)-1;return s[n](i[n](t))}}function zt(e,t){function n(n){return e(t(n))}var r=t.pow;return n.invert=function(t){return r(e.invert(t))},n.domain=function(i){return arguments.length?(t=i[0]<0?Xt:Wt,r=t.pow,e.domain(i.map(t)),n):e.domain().map(r)},n.nice=function(){return e.domain(Dt(e.domain(),Pt)),n},n.ticks=function(){var n=Mt(e.domain()),i=[];if(n.every(isFinite)){var s=Math.floor(n[0]),o=Math.ceil(n[1]),u=r(n[0]),a=r(n[1]);if(t===Xt){i.push(r(s));for(;s++<o;)for(var f=9;f>0;f--)i.push(r(s)*f)}else{for(;s<o;s++)for(var f=1;f<10;f++)i.push(r(s)*f);i.push(r(s))}for(s=0;i[s]<u;s++);for(o=i.length;i[o-1]>a;o--);i=i.slice(s,o)}return i},n.tickFormat=function(e,i){arguments.length<2&&(i=qs);if(arguments.length<1)return i;var s=Math.max(.1,e/n.ticks().length),o=t===Xt?(u=-1e-12,Math.floor):(u=1e-12,Math.ceil),u;return function(e){return e/r(o(t(e)+u))<=s?i(e):""}},n.copy=function(){return zt(e.copy(),t)},Bt(n,e)}function Wt(e){return Math.log(e<0?0:e)/Math.LN10}function Xt(e){return-Math.log(e>0?0:-e)/Math.LN10}function Vt(e,t){function n(t){return e(r(t))}var r=$t(t),i=$t(1/t);return n.invert=function(t){return i(e.invert(t))},n.domain=function(t){return arguments.length?(e.domain(t.map(r)),n):e.domain().map(i)},n.ticks=function(e){return It(n.domain(),e)},n.tickFormat=function(e){return qt(n.domain(),e)},n.nice=function(){return n.domain(Dt(n.domain(),jt))},n.exponent=function(e){if(!arguments.length)return t;var s=n.domain();return r=$t(t=e),i=$t(1/t),n.domain(s)},n.copy=function(){return Vt(e.copy(),t)},Bt(n,e)}function $t(e){return function(t){return t<0?-Math.pow(-t,e):Math.pow(t,e)}}function Jt(e,t){function n(t){return o[((s.get(t)||s.set(t,e.push(t)))-1)%o.length]}function i(t,n){return d3.range(e.length).map(function(e){return t+n*e})}var s,o,u;return n.domain=function(i){if(!arguments.length)return e;e=[],s=new r;var o=-1,u=i.length,a;while(++o<u)s.has(a=i[o])||s.set(a,e.push(a));return n[t.t].apply(n,t.a)},n.range=function(e){return arguments.length?(o=e,u=0,t={t:"range",a:arguments},n):o},n.rangePoints=function(r,s){arguments.length<2&&(s=0);var a=r[0],f=r[1],l=(f-a)/(Math.max(1,e.length-1)+s);return o=i(e.length<2?(a+f)/2:a+l*s/2,l),u=0,t={t:"rangePoints",a:arguments},n},n.rangeBands=function(r,s,a){arguments.length<2&&(s=0),arguments.length<3&&(a=s);var f=r[1]<r[0],l=r[f-0],c=r[1-f],h=(c-l)/(e.length-s+2*a);return o=i(l+h*a,h),f&&o.reverse(),u=h*(1-s),t={t:"rangeBands",a:arguments},n},n.rangeRoundBands=function(r,s,a){arguments.length<2&&(s=0),arguments.length<3&&(a=s);var f=r[1]<r[0],l=r[f-0],c=r[1-f],h=Math.floor((c-l)/(e.length-s+2*a)),p=c-l-(e.length-s)*h;return o=i(l+Math.round(p/2),h),f&&o.reverse(),u=Math.round(h*(1-s)),t={t:"rangeRoundBands",a:arguments},n},n.rangeBand=function(){return u},n.rangeExtent=function(){return Mt(t.a[0])},n.copy=function(){return Jt(e,t)},n.domain(e)}function Kt(e,t){function n(){var n=0,s=e.length,o=t.length;i=[];while(++n<o)i[n-1]=d3.quantile(e,n/o);return r}function r(e){return isNaN(e=+e)?NaN:t[d3.bisect(i,e)]}var i;return r.domain=function(t){return arguments.length?(e=t.filter(function(e){return!isNaN(e)}).sort(d3.ascending),n()):e},r.range=function(e){return arguments.length?(t=e,n()):t},r.quantiles=function(){return i},r.copy=function(){return Kt(e,t)},n()}function Qt(e,t,n){function r(t){return n[Math.max(0,Math.min(o,Math.floor(s*(t-e))))]}function i(){return s=n.length/(t-e),o=n.length-1,r}var s,o;return r.domain=function(n){return arguments.length?(e=+n[0],t=+n[n.length-1],i()):[e,t]},r.range=function(e){return arguments.length?(n=e,i()):n},r.copy=function(){return Qt(e,t,n)},i()}function Gt(e,t){function n(n){return t[d3.bisect(e,n)]}return n.domain=function(t){return arguments.length?(e=t,n):e},n.range=function(e){return arguments.length?(t=e,n):t},n.copy=function(){return Gt(e,t)},n}function Yt(e){function t(e){return+e}return t.invert=t,t.domain=t.range=function(n){return arguments.length?(e=n.map(t),t):e},t.ticks=function(t){return It(e,t)},t.tickFormat=function(t){return qt(e,t)},t.copy=function(){return Yt(e)},t}function Zt(e){return e.innerRadius}function en(e){return e.outerRadius}function tn(e){return e.startAngle}function nn(e){return e.endAngle}function rn(e){function t(t){function o(){a.push("M",s(e(l),f))}var a=[],l=[],c=-1,h=t.length,p,d=u(n),v=u(r);while(++c<h)i.call(this,p=t[c],c)?l.push([+d.call(this,p,c),+v.call(this,p,c)]):l.length&&(o(),l=[]);return l.length&&o(),a.length?a.join(""):null}var n=sn,r=on,i=o,s=un,a=s.key,f=.7;return t.x=function(e){return arguments.length?(n=e,t):n},t.y=function(e){return arguments.length?(r=e,t):r},t.defined=function(e){return arguments.length?(i=e,t):i},t.interpolate=function(e){return arguments.length?(typeof e=="function"?a=s=e:a=(s=$s.get(e)||un).key,t):a},t.tension=function(e){return arguments.length?(f=e,t):f},t}function sn(e){return e[0]}function on(e){return e[1]}function un(e){return e.join("L")}function an(e){return un(e)+"Z"}function fn(e){var t=0,n=e.length,r=e[0],i=[r[0],",",r[1]];while(++t<n)i.push("V",(r=e[t])[1],"H",r[0]);return i.join("")}function ln(e){var t=0,n=e.length,r=e[0],i=[r[0],",",r[1]];while(++t<n)i.push("H",(r=e[t])[0],"V",r[1]);return i.join("")}function cn(e,t){return e.length<4?un(e):e[1]+dn(e.slice(1,e.length-1),vn(e,t))}function hn(e,t){return e.length<3?un(e):e[0]+dn((e.push(e[0]),e),vn([e[e.length-2]].concat(e,[e[1]]),t))}function pn(e,t,n){return e.length<3?un(e):e[0]+dn(e,vn(e,t))}function dn(e,t){if(t.length<1||e.length!=t.length&&e.length!=t.length+2)return un(e);var n=e.length!=t.length,r="",i=e[0],s=e[1],o=t[0],u=o,a=1;n&&(r+="Q"+(s[0]-o[0]*2/3)+","+(s[1]-o[1]*2/3)+","+s[0]+","+s[1],i=e[1],a=2);if(t.length>1){u=t[1],s=e[a],a++,r+="C"+(i[0]+o[0])+","+(i[1]+o[1])+","+(s[0]-u[0])+","+(s[1]-u[1])+","+s[0]+","+s[1];for(var f=2;f<t.length;f++,a++)s=e[a],u=t[f],r+="S"+(s[0]-u[0])+","+(s[1]-u[1])+","+s[0]+","+s[1]}if(n){var l=e[a];r+="Q"+(s[0]+u[0]*2/3)+","+(s[1]+u[1]*2/3)+","+l[0]+","+l[1]}return r}function vn(e,t){var n=[],r=(1-t)/2,i,s=e[0],o=e[1],u=1,a=e.length;while(++u<a)i=s,s=o,o=e[u],n.push([r*(o[0]-i[0]),r*(o[1]-i[1])]);return n}function mn(e){if(e.length<3)return un(e);var t=1,n=e.length,r=e[0],i=r[0],s=r[1],o=[i,i,i,(r=e[1])[0]],u=[s,s,s,r[1]],a=[i,",",s];En(a,o,u);while(++t<n)r=e[t],o.shift(),o.push(r[0]),u.shift(),u.push(r[1]),En(a,o,u);t=-1;while(++t<2)o.shift(),o.push(r[0]),u.shift(),u.push(r[1]),En(a,o,u);return a.join("")}function gn(e){if(e.length<4)return un(e);var t=[],n=-1,r=e.length,i,s=[0],o=[0];while(++n<3)i=e[n],s.push(i[0]),o.push(i[1]);t.push(wn(Qs,s)+","+wn(Qs,o)),--n;while(++n<r)i=e[n],s.shift(),s.push(i[0]),o.shift(),o.push(i[1]),En(t,s,o);return t.join("")}function yn(e){var t,n=-1,r=e.length,i=r+4,s,o=[],u=[];while(++n<4)s=e[n%r],o.push(s[0]),u.push(s[1]);t=[wn(Qs,o),",",wn(Qs,u)],--n;while(++n<i)s=e[n%r],o.shift(),o.push(s[0]),u.shift(),u.push(s[1]),En(t,o,u);return t.join("")}function bn(e,t){var n=e.length-1;if(n){var r=e[0][0],i=e[0][1],s=e[n][0]-r,o=e[n][1]-i,u=-1,a,f;while(++u<=n)a=e[u],f=u/n,a[0]=t*a[0]+(1-t)*(r+f*s),a[1]=t*a[1]+(1-t)*(i+f*o)}return mn(e)}function wn(e,t){return e[0]*t[0]+e[1]*t[1]+e[2]*t[2]+e[3]*t[3]}function En(e,t,n){e.push("C",wn(Js,t),",",wn(Js,n),",",wn(Ks,t),",",wn(Ks,n),",",wn(Qs,t),",",wn(Qs,n))}function Sn(e,t){return(t[1]-e[1])/(t[0]-e[0])}function xn(e){var t=0,n=e.length-1,r=[],i=e[0],s=e[1],o=r[0]=Sn(i,s);while(++t<n)r[t]=(o+(o=Sn(i=s,s=e[t+1])))/2;return r[t]=o,r}function Tn(e){var t=[],n,r,i,s,o=xn(e),u=-1,a=e.length-1;while(++u<a)n=Sn(e[u],e[u+1]),Math.abs(n)<1e-6?o[u]=o[u+1]=0:(r=o[u]/n,i=o[u+1]/n,s=r*r+i*i,s>9&&(s=n*3/Math.sqrt(s),o[u]=s*r,o[u+1]=s*i));u=-1;while(++u<=a)s=(e[Math.min(a,u+1)][0]-e[Math.max(0,u-1)][0])/(6*(1+o[u]*o[u])),t.push([s||0,o[u]*s||0]);return t}function Nn(e){return e.length<3?un(e):e[0]+dn(e,Tn(e))}function Cn(e){var t,n=-1,r=e.length,i,s;while(++n<r)t=e[n],i=t[0],s=t[1]+Xs,t[0]=i*Math.cos(s),t[1]=i*Math.sin(s);return e}function kn(e){function t(t){function o(){l.push("M",f(e(v),p),h,c(e(d.reverse()),p),"Z")}var l=[],d=[],v=[],m=-1,g=t.length,y,b=u(n),w=u(i),E=n===r?function(){return x}:u(r),S=i===s?function(){return T}:u(s),x,T;while(++m<g)a.call(this,y=t[m],m)?(d.push([x=+b.call(this,y,m),T=+w.call(this,y,m)]),v.push([+E.call(this,y,m),+S.call(this,y,m)])):d.length&&(o(),d=[],v=[]);return d.length&&o(),l.length?l.join(""):null}var n=sn,r=sn,i=0,s=on,a=o,f=un,l=f.key,c=f,h="L",p=.7;return t.x=function(e){return arguments.length?(n=r=e,t):r},t.x0=function(e){return arguments.length?(n=e,t):n},t.x1=function(e){return arguments.length?(r=e,t):r},t.y=function(e){return arguments.length?(i=s=e,t):s},t.y0=function(e){return arguments.length?(i=e,t):i},t.y1=function(e){return arguments.length?(s=e,t):s},t.defined=function(e){return arguments.length?(a=e,t):a},t.interpolate=function(e){return arguments.length?(typeof e=="function"?l=f=e:l=(f=$s.get(e)||un).key,c=f.reverse||f,h=f.closed?"M":"L",t):l},t.tension=function(e){return arguments.length?(p=e,t):p},t}function Ln(e){return e.source}function An(e){return e.target}function On(e){return e.radius}function Mn(e){return e.startAngle}function _n(e){return e.endAngle}function Dn(e){return[e.x,e.y]}function Pn(e){return function(){var t=e.apply(this,arguments),n=t[0],r=t[1]+Xs;return[n*Math.cos(r),n*Math.sin(r)]}}function Hn(){return 64}function Bn(){return"circle"}function jn(e){var t=Math.sqrt(e/Math.PI);return"M0,"+t+"A"+t+","+t+" 0 1,1 0,"+ -t+"A"+t+","+t+" 0 1,1 0,"+t+"Z"}function Fn(e,t){e.attr("transform",function(e){return"translate("+t(e)+",0)"})}function In(e,t){e.attr("transform",function(e){return"translate(0,"+t(e)+")"})}function qn(e,t,n){i=[];if(n&&t.length>1){var r=Mt(e.domain()),i,s=-1,o=t.length,u=(t[1]-t[0])/++n,a,f;while(++s<o)for(a=n;--a>0;)(f=+t[s]-a*u)>=r[0]&&i.push(f);for(--s,a=0;++a<n&&(f=+t[s]+a*u)<r[1];)i.push(f)}return i}function Rn(){no||(no=d3.select("body").append("div").style("visibility","hidden").style("top",0).style("height",0).style("width",0).style("overflow-y","scroll").append("div").style("height","2000px").node().parentNode);var e=d3.event,t;try{no.scrollTop=1e3,no.dispatchEvent(e),t=1e3-no.scrollTop}catch(n){t=e.wheelDelta||-e.detail*5}return t}function Un(e){var t=e.source,n=e.target,r=Wn(t,n),i=[t];while(t!==r)t=t.parent,i.push(t);var s=i.length;while(n!==r)i.splice(s,0,n),n=n.parent;return i}function zn(e){var t=[],n=e.parent;while(n!=null)t.push(e),e=n,n=n.parent;return t.push(e),t}function Wn(e,t){if(e===t)return e;var n=zn(e),r=zn(t),i=n.pop(),s=r.pop(),o=null;while(i===s)o=i,i=n.pop(),s=r.pop();return o}function Xn(e){e.fixed|=2}function Vn(e){e.fixed&=1}function $n(e){e.fixed|=4}function Jn(e){e.fixed&=3}function Kn(e,t,n){var r=0,i=0;e.charge=0;if(!e.leaf){var s=e.nodes,o=s.length,u=-1,a;while(++u<o){a=s[u];if(a==null)continue;Kn(a,t,n),e.charge+=a.charge,r+=a.charge*a.cx,i+=a.charge*a.cy}}if(e.point){e.leaf||(e.point.x+=Math.random()-.5,e.point.y+=Math.random()-.5);var f=t*n[e.point.index];e.charge+=e.pointCharge=f,r+=f*e.point.x,i+=f*e.point.y}e.cx=r/e.charge,e.cy=i/e.charge}function Qn(e){return 20}function Gn(e){return 1}function Yn(e){return e.x}function Zn(e){return e.y}function er(e,t,n){e.y0=t,e.y=n}function tr(e){return d3.range(e.length)}function nr(e){var t=-1,n=e[0].length,r=[];while(++t<n)r[t]=0;return r}function rr(e){var t=1,n=0,r=e[0][1],i,s=e.length;for(;t<s;++t)(i=e[t][1])>r&&(n=t,r=i);return n}function ir(e){return e.reduce(sr,0)}function sr(e,t){return e+t[1]}function or(e,t){return ur(e,Math.ceil(Math.log(t.length)/Math.LN2+1))}function ur(e,t){var n=-1,r=+e[0],i=(e[1]-r)/t,s=[];while(++n<=t)s[n]=i*n+r;return s}function ar(e){return[d3.min(e),d3.max(e)]}function fr(e,t){return d3.rebind(e,t,"sort","children","value"),e.links=pr,e.nodes=function(t){return uo=!0,(e.nodes=e)(t)},e}function lr(e){return e.children}function cr(e){return e.value}function hr(e,t){return t.value-e.value}function pr(e){return d3.merge(e.map(function(e){return(e.children||[]).map(function(t){return{source:e,target:t}})}))}function dr(e,t){return e.value-t.value}function vr(e,t){var n=e._pack_next;e._pack_next=t,t._pack_prev=e,t._pack_next=n,n._pack_prev=t}function mr(e,t){e._pack_next=t,t._pack_prev=e}function gr(e,t){var n=t.x-e.x,r=t.y-e.y,i=e.r+t.r;return i*i-n*n-r*r>.001}function yr(e){function t(e){r=Math.min(e.x-e.r,r),i=Math.max(e.x+e.r,i),s=Math.min(e.y-e.r,s),o=Math.max(e.y+e.r,o)}if(!(n=e.children)||!(p=n.length))return;var n,r=Infinity,i=-Infinity,s=Infinity,o=-Infinity,u,a,f,l,c,h,p;n.forEach(br),u=n[0],u.x=-u.r,u.y=0,t(u);if(p>1){a=n[1],a.x=a.r,a.y=0,t(a);if(p>2){f=n[2],Sr(u,a,f),t(f),vr(u,f),u._pack_prev=f,vr(f,a),a=u._pack_next;for(l=3;l<p;l++){Sr(u,a,f=n[l]);var d=0,v=1,m=1;for(c=a._pack_next;c!==a;c=c._pack_next,v++)if(gr(c,f)){d=1;break}if(d==1)for(h=u._pack_prev;h!==c._pack_prev;h=h._pack_prev,m++)if(gr(h,f))break;d?(v<m||v==m&&a.r<u.r?mr(u,a=c):mr(u=h,a),l--):(vr(u,f),a=f,t(f))}}}var g=(r+i)/2,y=(s+o)/2,b=0;for(l=0;l<p;l++)f=n[l],f.x-=g,f.y-=y,b=Math.max(b,f.r+Math.sqrt(f.x*f.x+f.y*f.y));e.r=b,n.forEach(wr)}function br(e){e._pack_next=e._pack_prev=e}function wr(e){delete e._pack_next,delete e._pack_prev}function Er(e,t,n,r){var i=e.children;e.x=t+=r*e.x,e.y=n+=r*e.y,e.r*=r;if(i){var s=-1,o=i.length;while(++s<o)Er(i[s],t,n,r)}}function Sr(e,t,n){var r=e.r+n.r,i=t.x-e.x,s=t.y-e.y;if(r&&(i||s)){var o=t.r+n.r,u=i*i+s*s;o*=o,r*=r;var a=.5+(r-o)/(2*u),f=Math.sqrt(Math.max(0,2*o*(r+u)-(r-=u)*r-o*o))/(2*u);n.x=e.x+a*i+f*s,n.y=e.y+a*s-f*i}else n.x=e.x+r,n.y=e.y}function xr(e){return 1+d3.max(e,function(e){return e.y})}function Tr(e){return e.reduce(function(e,t){return e+t.x},0)/e.length}function Nr(e){var t=e.children;return t&&t.length?Nr(t[0]):e}function Cr(e){var t=e.children,n;return t&&(n=t.length)?Cr(t[n-1]):e}function kr(e,t){return e.parent==t.parent?1:2}function Lr(e){var t=e.children;return t&&t.length?t[0]:e._tree.thread}function Ar(e){var t=e.children,n;return t&&(n=t.length)?t[n-1]:e._tree.thread}function Or(e,t){var n=e.children;if(n&&(i=n.length)){var r,i,s=-1;while(++s<i)t(r=Or(n[s],t),e)>0&&(e=r)}return e}function Mr(e,t){return e.x-t.x}function _r(e,t){return t.x-e.x}function Dr(e,t){return e.depth-t.depth}function Pr(e,t){function n(e,r){var i=e.children;if(i&&(a=i.length)){var s,o=null,u=-1,a;while(++u<a)s=i[u],n(s,o),o=s}t(e,r)}n(e,null)}function Hr(e){var t=0,n=0,r=e.children,i=r.length,s;while(--i>=0)s=r[i]._tree,s.prelim+=t,s.mod+=t,t+=s.shift+(n+=s.change)}function Br(e,t,n){e=e._tree,t=t._tree;var r=n/(t.number-e.number);e.change+=r,t.change-=r,t.shift+=n,t.prelim+=n,t.mod+=n}function jr(e,t,n){return e._tree.ancestor.parent==t.parent?e._tree.ancestor:n}function Fr(e){return{x:e.x,y:e.y,dx:e.dx,dy:e.dy}}function Ir(e,t){var n=e.x+t[3],r=e.y+t[0],i=e.dx-t[1]-t[3],s=e.dy-t[0]-t[2];return i<0&&(n+=i/2,i=0),s<0&&(r+=s/2,s=0),{x:n,y:r,dx:i,dy:s}}function qr(e,t){function n(e,r){d3.text(e,t,function(e){r(e&&n.parse(e))})}function r(t){return t.map(i).join(e)}function i(e){return o.test(e)?'"'+e.replace(/\"/g,'""')+'"':e}var s=new RegExp("\r\n|["+e+"\r\n]","g"),o=new RegExp('["'+e+"\n]"),u=e.charCodeAt(0);return n.parse=function(e){var t;return n.parseRows(e,function(e,n){if(n){var r={},i=-1,s=t.length;while(++i<s)r[t[i]]=e[i];return r}return t=e,null})},n.parseRows=function(e,t){function n(){if(s.lastIndex>=e.length)return i;if(l)return l=!1,r;var t=s.lastIndex;if(e.charCodeAt(t)===34){var n=t;while(n++<e.length)if(e.charCodeAt(n)===34){if(e.charCodeAt(n+1)!==34)break;n++}s.lastIndex=n+2;var o=e.charCodeAt(n+1);return o===13?(l=!0,e.charCodeAt(n+2)===10&&s.lastIndex++):o===10&&(l=!0),e.substring(t+1,n).replace(/""/g,'"')}var a=s.exec(e);return a?(l=a[0].charCodeAt(0)!==u,e.substring(t,a.index)):(s.lastIndex=e.length,e.substring(t))}var r={},i={},o=[],a=0,f,l;s.lastIndex=0;while((f=n())!==i){var c=[];while(f!==r&&f!==i)c.push(f),f=n();if(t&&!(c=t(c,a++)))continue;o.push(c)}return o},n.format=function(e){return e.map(r).join("\n")},n}function Rr(e,t){return function(n){return n&&e.hasOwnProperty(n.type)?e[n.type](n):t}}function Ur(e){return"m0,"+e+"a"+e+","+e+" 0 1,1 0,"+ -2*e+"a"+e+","+e+" 0 1,1 0,"+2*e+"z"}function zr(e,t){fo.hasOwnProperty(e.type)&&fo[e.type](e,t)}function Wr(e,t){zr(e.geometry,t)}function Xr(e,t){for(var n=e.features,r=0,i=n.length;r<i;r++)zr(n[r].geometry,t)}function Vr(e,t){for(var n=e.geometries,r=0,i=n.length;r<i;r++)zr(n[r],t)}function $r(e,t){for(var n=e.coordinates,r=0,i=n.length;r<i;r++)t.apply(null,n[r])}function Jr(e,t){for(var n=e.coordinates,r=0,i=n.length;r<i;r++)for(var s=n[r],o=0,u=s.length;o<u;o++)t.apply(null,s[o])}function Kr(e,t){for(var n=e.coordinates,r=0,i=n.length;r<i;r++)for(var s=n[r][0],o=0,u=s.length;o<u;o++)t.apply(null,s[o])}function Qr(e,t){t.apply(null,e.coordinates)}function Gr(e,t){for(var n=e.coordinates[0],r=0,i=n.length;r<i;r++)t.apply(null,n[r])}function Yr(e){return e.source}function Zr(e){return e.target}function ei(){function e(e){var t=Math.sin(e*=p)*d,n=Math.sin(p-e)*d,r=n*s+t*c,u=n*o+t*h,a=n*i+t*l;return[Math.atan2(u,r)/ao,Math.atan2(a,Math.sqrt(r*r+u*u))/ao]}var t,n,r,i,s,o,u,a,f,l,c,h,p,d;return e.distance=function(){return p==null&&(d=1/Math.sin(p=Math.acos(Math.max(-1,Math.min(1,i*l+r*f*Math.cos(u-t)))))),p},e.source=function(u){var a=Math.cos(t=u[0]*ao),f=Math.sin(t);return r=Math.cos(n=u[1]*ao),i=Math.sin(n),s=r*a,o=r*f,p=null,e},e.target=function(t){var n=Math.cos(u=t[0]*ao),r=Math.sin(u);return f=Math.cos(a=t[1]*ao),l=Math.sin(a),c=f*n,h=f*r,p=null,e},e}function ti(e,t){var n=ei().source(e).target(t);return n.distance(),n}function ni(e){var t=0,n=0;for(;;){if(e(t,n))return[t,n];t===0?(t=n+1,n=0):(t-=1,n+=1)}}function ri(e,t,n,r){var i,s,o,u,a,f,l;return i=r[e],s=i[0],o=i[1],i=r[t],u=i[0],a=i[1],i=r[n],f=i[0],l=i[1],(l-o)*(u-s)-(a-o)*(f-s)>0}function ii(e,t,n){return(n[0]-t[0])*(e[1]-t[1])<(n[1]-t[1])*(e[0]-t[0])}function si(e,t,n,r){var i=e[0],s=t[0],o=n[0],u=r[0],a=e[1],f=t[1],l=n[1],c=r[1],h=i-o,p=s-i,d=u-o,v=a-l,m=f-a,g=c-l,y=(d*v-g*h)/(g*p-d*m);return[i+y*p,a+y*m]}function oi(e,t){var n={list:e.map(function(e,t){return{index:t,x:e[0],y:e[1]}}).sort(function(e,t){return e.y<t.y?-1:e.y>t.y?1:e.x<t.x?-1:e.x>t.x?1:0}),bottomSite:null},r={list:[],leftEnd:null,rightEnd:null,init:function(){r.leftEnd=r.createHalfEdge(null,"l"),r.rightEnd=r.createHalfEdge(null,"l"),r.leftEnd.r=r.rightEnd,r.rightEnd.l=r.leftEnd,r.list.unshift(r.leftEnd,r.rightEnd)},createHalfEdge:function(e,t){return{edge:e,side:t,vertex:null,l:null,r:null}},insert:function(e,t){t.l=e,t.r=e.r,e.r.l=t,e.r=t},leftBound:function(e){var t=r.leftEnd;do t=t.r;while(t!=r.rightEnd&&i.rightOf(t,e));return t=t.l,t},del:function(e){e.l.r=e.r,e.r.l=e.l,e.edge=null},right:function(e){return e.r},left:function(e){return e.l},leftRegion:function(e){return e.edge==null?n.bottomSite:e.edge.region[e.side]},rightRegion:function(e){return e.edge==null?n.bottomSite:e.edge.region[ho[e.side]]}},i={bisect:function(e,t){var n={region:{l:e,r:t},ep:{l:null,r:null}},r=t.x-e.x,i=t.y-e.y,s=r>0?r:-r,o=i>0?i:-i;return n.c=e.x*r+e.y*i+(r*r+i*i)*.5,s>o?(n.a=1,n.b=i/r,n.c/=r):(n.b=1,n.a=r/i,n.c/=i),n},intersect:function(e,t){var n=e.edge,r=t.edge;if(!n||!r||n.region.r==r.region.r)return null;var i=n.a*r.b-n.b*r.a;if(Math.abs(i)<1e-10)return null;var s=(n.c*r.b-r.c*n.b)/i,o=(r.c*n.a-n.c*r.a)/i,u=n.region.r,a=r.region.r,f,l;u.y<a.y||u.y==a.y&&u.x<a.x?(f=e,l=n):(f=t,l=r);var c=s>=l.region.r.x;return c&&f.side==="l"||!c&&f.side==="r"?null:{x:s,y:o}},rightOf:function(e,t){var n=e.edge,r=n.region.r,i=t.x>r.x;if(i&&e.side==="l")return 1;if(!i&&e.side==="r")return 0;if(n.a===1){var s=t.y-r.y,o=t.x-r.x,u=0,a=0;!i&&n.b<0||i&&n.b>=0?a=u=s>=n.b*o:(a=t.x+t.y*n.b>n.c,n.b<0&&(a=!a),a||(u=1));if(!u){var f=r.x-n.region.l.x;a=n.b*(o*o-s*s)<f*s*(1+2*o/f+n.b*n.b),n.b<0&&(a=!a)}}else{var l=n.c-n.a*t.x,c=t.y-l,h=t.x-r.x,p=l-r.y;a=c*c>h*h+p*p}return e.side==="l"?a:!a},endPoint:function(e,n,r){e.ep[n]=r;if(!e.ep[ho[n]])return;t(e)},distance:function(e,t){var n=e.x-t.x,r=e.y-t.y;return Math.sqrt(n*n+r*r)}},s={list:[],insert:function(e,t,n){e.vertex=t,e.ystar=t.y+n;for(var r=0,i=s.list,o=i.length;r<o;r++){var u=i[r];if(e.ystar>u.ystar||e.ystar==u.ystar&&t.x>u.vertex.x)continue;break}i.splice(r,0,e)},del:function(e){for(var t=0,n=s.list,r=n.length;t<r&&n[t]!=e;++t);n.splice(t,1)},empty:function(){return s.list.length===0},nextEvent:function(e){for(var t=0,n=s.list,r=n.length;t<r;++t)if(n[t]==e)return n[t+1];return null},min:function(){var e=s.list[0];return{x:e.vertex.x,y:e.ystar}},extractMin:function(){return s.list.shift()}};r.init(),n.bottomSite=n.list.shift();var o=n.list.shift(),u,a,f,l,c,h,p,d,v,m,g,y,b;for(;;){s.empty()||(u=s.min());if(o&&(s.empty()||o.y<u.y||o.y==u.y&&o.x<u.x))a=r.leftBound(o),f=r.right(a),p=r.rightRegion(a),y=i.bisect(p,o),h=r.createHalfEdge(y,"l"),r.insert(a,h),m=i.intersect(a,h),m&&(s.del(a),s.insert(a,m,i.distance(m,o))),a=h,h=r.createHalfEdge(y,"r"
+),r.insert(a,h),m=i.intersect(h,f),m&&s.insert(h,m,i.distance(m,o)),o=n.list.shift();else{if(!!s.empty())break;a=s.extractMin(),l=r.left(a),f=r.right(a),c=r.right(f),p=r.leftRegion(a),d=r.rightRegion(f),g=a.vertex,i.endPoint(a.edge,a.side,g),i.endPoint(f.edge,f.side,g),r.del(a),s.del(f),r.del(f),b="l",p.y>d.y&&(v=p,p=d,d=v,b="r"),y=i.bisect(p,d),h=r.createHalfEdge(y,b),r.insert(l,h),i.endPoint(y,ho[b],g),m=i.intersect(l,h),m&&(s.del(l),s.insert(l,m,i.distance(m,p))),m=i.intersect(h,c),m&&s.insert(h,m,i.distance(m,p))}}for(a=r.right(r.leftEnd);a!=r.rightEnd;a=r.right(a))t(a.edge)}function ui(){return{leaf:!0,nodes:[],point:null}}function ai(e,t,n,r,i,s){if(!e(t,n,r,i,s)){var o=(n+i)*.5,u=(r+s)*.5,a=t.nodes;a[0]&&ai(e,a[0],n,r,o,u),a[1]&&ai(e,a[1],o,r,i,u),a[2]&&ai(e,a[2],n,u,o,s),a[3]&&ai(e,a[3],o,u,i,s)}}function fi(e){return{x:e[0],y:e[1]}}function li(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function ci(e){return e.substring(0,3)}function hi(e,t,n,r){var i,s,o=0,u=t.length,a=n.length;while(o<u){if(r>=a)return-1;i=t.charCodeAt(o++);if(i==37){s=Ho[t.charAt(o++)];if(!s||(r=s(e,n,r))<0)return-1}else if(i!=n.charCodeAt(r++))return-1}return r}function pi(e){return new RegExp("^(?:"+e.map(d3.requote).join("|")+")","i")}function di(e){var t=new r,n=-1,i=e.length;while(++n<i)t.set(e[n].toLowerCase(),n);return t}function vi(e,t,n){Ao.lastIndex=0;var r=Ao.exec(t.substring(n));return r?n+=r[0].length:-1}function mi(e,t,n){Lo.lastIndex=0;var r=Lo.exec(t.substring(n));return r?n+=r[0].length:-1}function gi(e,t,n){_o.lastIndex=0;var r=_o.exec(t.substring(n));return r?(e.m=Do.get(r[0].toLowerCase()),n+=r[0].length):-1}function yi(e,t,n){Oo.lastIndex=0;var r=Oo.exec(t.substring(n));return r?(e.m=Mo.get(r[0].toLowerCase()),n+=r[0].length):-1}function bi(e,t,n){return hi(e,Po.c.toString(),t,n)}function wi(e,t,n){return hi(e,Po.x.toString(),t,n)}function Ei(e,t,n){return hi(e,Po.X.toString(),t,n)}function Si(e,t,n){Bo.lastIndex=0;var r=Bo.exec(t.substring(n,n+4));return r?(e.y=+r[0],n+=r[0].length):-1}function xi(e,t,n){Bo.lastIndex=0;var r=Bo.exec(t.substring(n,n+2));return r?(e.y=Ti(+r[0]),n+=r[0].length):-1}function Ti(e){return e+(e>68?1900:2e3)}function Ni(e,t,n){Bo.lastIndex=0;var r=Bo.exec(t.substring(n,n+2));return r?(e.m=r[0]-1,n+=r[0].length):-1}function Ci(e,t,n){Bo.lastIndex=0;var r=Bo.exec(t.substring(n,n+2));return r?(e.d=+r[0],n+=r[0].length):-1}function ki(e,t,n){Bo.lastIndex=0;var r=Bo.exec(t.substring(n,n+2));return r?(e.H=+r[0],n+=r[0].length):-1}function Li(e,t,n){Bo.lastIndex=0;var r=Bo.exec(t.substring(n,n+2));return r?(e.M=+r[0],n+=r[0].length):-1}function Ai(e,t,n){Bo.lastIndex=0;var r=Bo.exec(t.substring(n,n+2));return r?(e.S=+r[0],n+=r[0].length):-1}function Oi(e,t,n){Bo.lastIndex=0;var r=Bo.exec(t.substring(n,n+3));return r?(e.L=+r[0],n+=r[0].length):-1}function Mi(e,t,n){var r=jo.get(t.substring(n,n+=2).toLowerCase());return r==null?-1:(e.p=r,n)}function _i(e){var t=e.getTimezoneOffset(),n=t>0?"-":"+",r=~~(Math.abs(t)/60),i=Math.abs(t)%60;return n+To(r)+To(i)}function Di(e){return e.toISOString()}function Pi(e,t,n){function r(t){var n=e(t),r=s(n,1);return t-n<r-t?n:r}function i(n){return t(n=e(new po(n-1)),1),n}function s(e,n){return t(e=new po(+e),n),e}function o(e,r,s){var o=i(e),u=[];if(s>1)while(o<r)n(o)%s||u.push(new Date(+o)),t(o,1);else while(o<r)u.push(new Date(+o)),t(o,1);return u}function u(e,t,n){try{po=li;var r=new li;return r._=e,o(r,t,n)}finally{po=Date}}e.floor=e,e.round=r,e.ceil=i,e.offset=s,e.range=o;var a=e.utc=Hi(e);return a.floor=a,a.round=Hi(r),a.ceil=Hi(i),a.offset=Hi(s),a.range=u,e}function Hi(e){return function(t,n){try{po=li;var r=new li;return r._=t,e(r,n)._}finally{po=Date}}}function Bi(e,t,n){function r(t){return e(t)}return r.invert=function(t){return Fi(e.invert(t))},r.domain=function(t){return arguments.length?(e.domain(t),r):e.domain().map(Fi)},r.nice=function(e){return r.domain(Dt(r.domain(),function(){return e}))},r.ticks=function(n,i){var s=ji(r.domain());if(typeof n!="function"){var o=s[1]-s[0],u=o/n,a=d3.bisect(Io,u);if(a==Io.length)return t.year(s,n);if(!a)return e.ticks(n).map(Fi);Math.log(u/Io[a-1])<Math.log(Io[a]/u)&&--a,n=t[a],i=n[1],n=n[0].range}return n(s[0],new Date(+s[1]+1),i)},r.tickFormat=function(){return n},r.copy=function(){return Bi(e.copy(),t,n)},d3.rebind(r,e,"range","rangeRound","interpolate","clamp")}function ji(e){var t=e[0],n=e[e.length-1];return t<n?[t,n]:[n,t]}function Fi(e){return new Date(e)}function Ii(e){return function(t){var n=e.length-1,r=e[n];while(!r[1](t))r=e[--n];return r[0](t)}}function qi(e){var t=new Date(e,0,1);return t.setFullYear(e),t}function Ri(e){var t=e.getFullYear(),n=qi(t),r=qi(t+1);return t+(e-n)/(r-n)}function Ui(e){var t=new Date(Date.UTC(e,0,1));return t.setUTCFullYear(e),t}function zi(e){var t=e.getUTCFullYear(),n=Ui(t),r=Ui(t+1);return t+(e-n)/(r-n)}Date.now||(Date.now=function(){return+(new Date)});try{document.createElement("div").style.setProperty("opacity",0,"")}catch(Wi){var Xi=CSSStyleDeclaration.prototype,Vi=Xi.setProperty;Xi.setProperty=function(e,t,n){Vi.call(this,e,t+"",n)}}d3={version:"2.10.2"};var $i=n;try{$i(document.documentElement.childNodes)[0].nodeType}catch(Ji){$i=t}var Ki=[].__proto__?function(e,t){e.__proto__=t}:function(e,t){for(var n in t)e[n]=t[n]};d3.map=function(e){var t=new r;for(var n in e)t.set(n,e[n]);return t},e(r,{has:function(e){return Qi+e in this},get:function(e){return this[Qi+e]},set:function(e,t){return this[Qi+e]=t},remove:function(e){return e=Qi+e,e in this&&delete this[e]},keys:function(){var e=[];return this.forEach(function(t){e.push(t)}),e},values:function(){var e=[];return this.forEach(function(t,n){e.push(n)}),e},entries:function(){var e=[];return this.forEach(function(t,n){e.push({key:t,value:n})}),e},forEach:function(e){for(var t in this)t.charCodeAt(0)===Gi&&e.call(this,t.substring(1),this[t])}});var Qi="\0",Gi=Qi.charCodeAt(0);d3.functor=u,d3.rebind=function(e,t){var n=1,r=arguments.length,i;while(++n<r)e[i=arguments[n]]=a(e,t,t[i]);return e},d3.ascending=function(e,t){return e<t?-1:e>t?1:e>=t?0:NaN},d3.descending=function(e,t){return t<e?-1:t>e?1:t>=e?0:NaN},d3.mean=function(e,t){var n=e.length,r,i=0,s=-1,o=0;if(arguments.length===1)while(++s<n)f(r=e[s])&&(i+=(r-i)/++o);else while(++s<n)f(r=t.call(e,e[s],s))&&(i+=(r-i)/++o);return o?i:undefined},d3.median=function(e,t){return arguments.length>1&&(e=e.map(t)),e=e.filter(f),e.length?d3.quantile(e.sort(d3.ascending),.5):undefined},d3.min=function(e,t){var n=-1,r=e.length,i,s;if(arguments.length===1){while(++n<r&&((i=e[n])==null||i!=i))i=undefined;while(++n<r)(s=e[n])!=null&&i>s&&(i=s)}else{while(++n<r&&((i=t.call(e,e[n],n))==null||i!=i))i=undefined;while(++n<r)(s=t.call(e,e[n],n))!=null&&i>s&&(i=s)}return i},d3.max=function(e,t){var n=-1,r=e.length,i,s;if(arguments.length===1){while(++n<r&&((i=e[n])==null||i!=i))i=undefined;while(++n<r)(s=e[n])!=null&&s>i&&(i=s)}else{while(++n<r&&((i=t.call(e,e[n],n))==null||i!=i))i=undefined;while(++n<r)(s=t.call(e,e[n],n))!=null&&s>i&&(i=s)}return i},d3.extent=function(e,t){var n=-1,r=e.length,i,s,o;if(arguments.length===1){while(++n<r&&((i=o=e[n])==null||i!=i))i=o=undefined;while(++n<r)(s=e[n])!=null&&(i>s&&(i=s),o<s&&(o=s))}else{while(++n<r&&((i=o=t.call(e,e[n],n))==null||i!=i))i=undefined;while(++n<r)(s=t.call(e,e[n],n))!=null&&(i>s&&(i=s),o<s&&(o=s))}return[i,o]},d3.random={normal:function(e,t){var n=arguments.length;return n<2&&(t=1),n<1&&(e=0),function(){var n,r,i;do n=Math.random()*2-1,r=Math.random()*2-1,i=n*n+r*r;while(!i||i>1);return e+t*n*Math.sqrt(-2*Math.log(i)/i)}},logNormal:function(e,t){var n=arguments.length;n<2&&(t=1),n<1&&(e=0);var r=d3.random.normal();return function(){return Math.exp(e+t*r())}},irwinHall:function(e){return function(){for(var t=0,n=0;n<e;n++)t+=Math.random();return t/e}}},d3.sum=function(e,t){var n=0,r=e.length,i,s=-1;if(arguments.length===1)while(++s<r)isNaN(i=+e[s])||(n+=i);else while(++s<r)isNaN(i=+t.call(e,e[s],s))||(n+=i);return n},d3.quantile=function(e,t){var n=(e.length-1)*t+1,r=Math.floor(n),i=e[r-1],s=n-r;return s?i+s*(e[r]-i):i},d3.transpose=function(e){return d3.zip.apply(d3,e)},d3.zip=function(){if(!(i=arguments.length))return[];for(var e=-1,t=d3.min(arguments,l),n=new Array(t);++e<t;)for(var r=-1,i,s=n[e]=new Array(i);++r<i;)s[r]=arguments[r][e];return n},d3.bisector=function(e){return{left:function(t,n,r,i){arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);while(r<i){var s=r+i>>>1;e.call(t,t[s],s)<n?r=s+1:i=s}return r},right:function(t,n,r,i){arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);while(r<i){var s=r+i>>>1;n<e.call(t,t[s],s)?i=s:r=s+1}return r}}};var Yi=d3.bisector(function(e){return e});d3.bisectLeft=Yi.left,d3.bisect=d3.bisectRight=Yi.right,d3.first=function(e,t){var n=0,r=e.length,i=e[0],s;arguments.length===1&&(t=d3.ascending);while(++n<r)t.call(e,i,s=e[n])>0&&(i=s);return i},d3.last=function(e,t){var n=0,r=e.length,i=e[0],s;arguments.length===1&&(t=d3.ascending);while(++n<r)t.call(e,i,s=e[n])<=0&&(i=s);return i},d3.nest=function(){function e(t,s){if(s>=i.length)return u?u.call(n,t):o?t.sort(o):t;var a=-1,f=t.length,l=i[s++],c,h,p=new r,d,v={};while(++a<f)(d=p.get(c=l(h=t[a])))?d.push(h):p.set(c,[h]);return p.forEach(function(t,n){v[t]=e(n,s)}),v}function t(e,n){if(n>=i.length)return e;var r=[],o=s[n++],u;for(u in e)r.push({key:u,values:t(e[u],n)});return o&&r.sort(function(e,t){return o(e.key,t.key)}),r}var n={},i=[],s=[],o,u;return n.map=function(t){return e(t,0)},n.entries=function(n){return t(e(n,0),0)},n.key=function(e){return i.push(e),n},n.sortKeys=function(e){return s[i.length-1]=e,n},n.sortValues=function(e){return o=e,n},n.rollup=function(e){return u=e,n},n},d3.keys=function(e){var t=[];for(var n in e)t.push(n);return t},d3.values=function(e){var t=[];for(var n in e)t.push(e[n]);return t},d3.entries=function(e){var t=[];for(var n in e)t.push({key:n,value:e[n]});return t},d3.permute=function(e,t){var n=[],r=-1,i=t.length;while(++r<i)n[r]=e[t[r]];return n},d3.merge=function(e){return Array.prototype.concat.apply([],e)},d3.split=function(e,t){var n=[],r=[],i,s=-1,o=e.length;arguments.length<2&&(t=c);while(++s<o)t.call(r,i=e[s],s)?r=[]:(r.length||n.push(r),r.push(i));return n},d3.range=function(e,t,n){arguments.length<3&&(n=1,arguments.length<2&&(t=e,e=0));if((t-e)/n===Infinity)throw new Error("infinite range");var r=[],i=p(Math.abs(n)),s=-1,o;e*=i,t*=i,n*=i;if(n<0)while((o=e+n*++s)>t)r.push(o/i);else while((o=e+n*++s)<t)r.push(o/i);return r},d3.requote=function(e){return e.replace(Zi,"\\$&")};var Zi=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;d3.round=function(e,t){return t?Math.round(e*(t=Math.pow(10,t)))/t:Math.round(e)},d3.xhr=function(e,t,n){var r=new XMLHttpRequest;arguments.length<3?(n=t,t=null):t&&r.overrideMimeType&&r.overrideMimeType(t),r.open("GET",e,!0),t&&r.setRequestHeader("Accept",t),r.onreadystatechange=function(){if(r.readyState===4){var e=r.status;n(!e&&r.response||e>=200&&e<300||e===304?r:null)}},r.send(null)},d3.text=function(e,t,n){function r(e){n(e&&e.responseText)}arguments.length<3&&(n=t,t=null),d3.xhr(e,t,r)},d3.json=function(e,t){d3.text(e,"application/json",function(e){t(e?JSON.parse(e):null)})},d3.html=function(e,t){d3.text(e,"text/html",function(e){if(e!=null){var n=document.createRange();n.selectNode(document.body),e=n.createContextualFragment(e)}t(e)})},d3.xml=function(e,t,n){function r(e){n(e&&e.responseXML)}arguments.length<3&&(n=t,t=null),d3.xhr(e,t,r)};var es={svg:"http://www.w3.org/2000/svg",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};d3.ns={prefix:es,qualify:function(e){var t=e.indexOf(":"),n=e;return t>=0&&(n=e.substring(0,t),e=e.substring(t+1)),es.hasOwnProperty(n)?{space:es[n],local:e}:e}},d3.dispatch=function(){var e=new d,t=-1,n=arguments.length;while(++t<n)e[arguments[t]]=v(e);return e},d.prototype.on=function(e,t){var n=e.indexOf("."),r="";return n>0&&(r=e.substring(n+1),e=e.substring(0,n)),arguments.length<2?this[e].on(r):this[e].on(r,t)},d3.format=function(e){var t=ts.exec(e),n=t[1]||" ",r=t[3]||"",i=t[5],s=+t[6],o=t[7],u=t[8],a=t[9],f=1,l="",c=!1;u&&(u=+u.substring(1)),i&&(n="0",o&&(s-=Math.floor((s-1)/4)));switch(a){case"n":o=!0,a="g";break;case"%":f=100,l="%",a="f";break;case"p":f=100,l="%",a="r";break;case"d":c=!0,u=0;break;case"s":f=-1,a="r"}return a=="r"&&!u&&(a="g"),a=ns.get(a)||g,function(e){if(c&&e%1)return"";var t=e<0&&(e=-e)?"-":r;if(f<0){var h=d3.formatPrefix(e,u);e=h.scale(e),l=h.symbol}else e*=f;e=a(e,u);if(i){var p=e.length+t.length;p<s&&(e=(new Array(s-p+1)).join(n)+e),o&&(e=y(e)),e=t+e}else{o&&(e=y(e)),e=t+e;var p=e.length;p<s&&(e=(new Array(s-p+1)).join(n)+e)}return e+l}};var ts=/(?:([^{])?([<>=^]))?([+\- ])?(#)?(0)?([0-9]+)?(,)?(\.[0-9]+)?([a-zA-Z%])?/,ns=d3.map({g:function(e,t){return e.toPrecision(t)},e:function(e,t){return e.toExponential(t)},f:function(e,t){return e.toFixed(t)},r:function(e,t){return d3.round(e,t=m(e,t)).toFixed(Math.max(0,Math.min(20,t)))}}),rs=["y","z","a","f","p","n","μ","m","","k","M","G","T","P","E","Z","Y"].map(b);d3.formatPrefix=function(e,t){var n=0;return e&&(e<0&&(e*=-1),t&&(e=d3.round(e,m(e,t))),n=1+Math.floor(1e-12+Math.log(e)/Math.LN10),n=Math.max(-24,Math.min(24,Math.floor((n<=0?n+1:n-1)/3)*3))),rs[8+n/3]};var is=T(2),ss=T(3),os=function(){return x},us=d3.map({linear:os,poly:T,quad:function(){return is},cubic:function(){return ss},sin:function(){return N},exp:function(){return C},circle:function(){return k},elastic:L,back:A,bounce:function(){return O}}),as=d3.map({"in":x,out:E,"in-out":S,"out-in":function(e){return S(E(e))}});d3.ease=function(e){var t=e.indexOf("-"),n=t>=0?e.substring(0,t):e,r=t>=0?e.substring(t+1):"in";return n=us.get(n)||os,r=as.get(r)||x,w(r(n.apply(null,Array.prototype.slice.call(arguments,1))))},d3.event=null,d3.transform=function(e){var t=document.createElementNS(d3.ns.prefix.svg,"g");return(d3.transform=function(e){t.setAttribute("transform",e);var n=t.transform.baseVal.consolidate();return new P(n?n.matrix:ls)})(e)},P.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var fs=180/Math.PI,ls={a:1,b:0,c:0,d:1,e:0,f:0};d3.interpolate=function(e,t){var n=d3.interpolators.length,r;while(--n>=0&&!(r=d3.interpolators[n](e,t)));return r},d3.interpolateNumber=function(e,t){return t-=e,function(n){return e+t*n}},d3.interpolateRound=function(e,t){return t-=e,function(n){return Math.round(e+t*n)}},d3.interpolateString=function(e,t){var n,r,i,s=0,o=0,u=[],a=[],f,l;cs.lastIndex=0;for(r=0;n=cs.exec(t);++r)n.index&&u.push(t.substring(s,o=n.index)),a.push({i:u.length,x:n[0]}),u.push(null),s=cs.lastIndex;s<t.length&&u.push(t.substring(s));for(r=0,f=a.length;(n=cs.exec(e))&&r<f;++r){l=a[r];if(l.x==n[0]){if(l.i)if(u[l.i+1]==null){u[l.i-1]+=l.x,u.splice(l.i,1);for(i=r+1;i<f;++i)a[i].i--}else{u[l.i-1]+=l.x+u[l.i+1],u.splice(l.i,2);for(i=r+1;i<f;++i)a[i].i-=2}else if(u[l.i+1]==null)u[l.i]=l.x;else{u[l.i]=l.x+u[l.i+1],u.splice(l.i+1,1);for(i=r+1;i<f;++i)a[i].i--}a.splice(r,1),f--,r--}else l.x=d3.interpolateNumber(parseFloat(n[0]),parseFloat(l.x))}while(r<f)l=a.pop(),u[l.i+1]==null?u[l.i]=l.x:(u[l.i]=l.x+u[l.i+1],u.splice(l.i+1,1)),f--;return u.length===1?u[0]==null?a[0].x:function(){return t}:function(e){for(r=0;r<f;++r)u[(l=a[r]).i]=l.x(e);return u.join("")}},d3.interpolateTransform=function(e,t){var n=[],r=[],i,s=d3.transform(e),o=d3.transform(t),u=s.translate,a=o.translate,f=s.rotate,l=o.rotate,c=s.skew,h=o.skew,p=s.scale,d=o.scale;return u[0]!=a[0]||u[1]!=a[1]?(n.push("translate(",null,",",null,")"),r.push({i:1,x:d3.interpolateNumber(u[0],a[0])},{i:3,x:d3.interpolateNumber(u[1],a[1])})):a[0]||a[1]?n.push("translate("+a+")"):n.push(""),f!=l?(f-l>180?l+=360:l-f>180&&(f+=360),r.push({i:n.push(n.pop()+"rotate(",null,")")-2,x:d3.interpolateNumber(f,l)})):l&&n.push(n.pop()+"rotate("+l+")"),c!=h?r.push({i:n.push(n.pop()+"skewX(",null,")")-2,x:d3.interpolateNumber(c,h)}):h&&n.push(n.pop()+"skewX("+h+")"),p[0]!=d[0]||p[1]!=d[1]?(i=n.push(n.pop()+"scale(",null,",",null,")"),r.push({i:i-4,x:d3.interpolateNumber(p[0],d[0])},{i:i-2,x:d3.interpolateNumber(p[1],d[1])})):(d[0]!=1||d[1]!=1)&&n.push(n.pop()+"scale("+d+")"),i=r.length,function(e){var t=-1,s;while(++t<i)n[(s=r[t]).i]=s.x(e);return n.join("")}},d3.interpolateRgb=function(e,t){e=d3.rgb(e),t=d3.rgb(t);var n=e.r,r=e.g,i=e.b,s=t.r-n,o=t.g-r,u=t.b-i;return function(e){return"#"+z(Math.round(n+s*e))+z(Math.round(r+o*e))+z(Math.round(i+u*e))}},d3.interpolateHsl=function(e,t){e=d3.hsl(e),t=d3.hsl(t);var n=e.h,r=e.s,i=e.l,s=t.h-n,o=t.s-r,u=t.l-i;return s>180?s-=360:s<-180&&(s+=360),function(e){return G(n+s*e,r+o*e,i+u*e)+""}},d3.interpolateLab=function(e,t){e=d3.lab(e),t=d3.lab(t);var n=e.l,r=e.a,i=e.b,s=t.l-n,o=t.a-r,u=t.b-i;return function(e){return rt(n+s*e,r+o*e,i+u*e)+""}},d3.interpolateHcl=function(e,t){e=d3.hcl(e),t=d3.hcl(t);var n=e.h,r=e.c,i=e.l,s=t.h-n,o=t.c-r,u=t.l-i;return s>180?s-=360:s<-180&&(s+=360),function(e){return et(n+s*e,r+o*e,i+u*e)+""}},d3.interpolateArray=function(e,t){var n=[],r=[],i=e.length,s=t.length,o=Math.min(e.length,t.length),u;for(u=0;u<o;++u)n.push(d3.interpolate(e[u],t[u]));for(;u<i;++u)r[u]=e[u];for(;u<s;++u)r[u]=t[u];return function(e){for(u=0;u<o;++u)r[u]=n[u](e);return r}},d3.interpolateObject=function(e,t){var n={},r={},i;for(i in e)i in t?n[i]=F(i)(e[i],t[i]):r[i]=e[i];for(i in t)i in e||(r[i]=t[i]);return function(e){for(i in n)r[i]=n[i](e);return r}};var cs=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g;d3.interpolators=[d3.interpolateObject,function(e,t){return t instanceof Array&&d3.interpolateArray(e,t)},function(e,t){return(typeof e=="string"||typeof t=="string")&&d3.interpolateString(e+"",t+"")},function(e,t){return(typeof t=="string"?hs.has(t)||/^(#|rgb\(|hsl\()/.test(t):t instanceof U||t instanceof Q)&&d3.interpolateRgb(e,t)},function(e,t){return!isNaN(e=+e)&&!isNaN(t=+t)&&d3.interpolateNumber(e,t)}],d3.rgb=function(e,t,n){return arguments.length===1?e instanceof U?R(e.r,e.g,e.b):W(""+e,R,G):R(~~e,~~t,~~n)},U.prototype.brighter=function(e){e=Math.pow(.7,arguments.length?e:1);var t=this.r,n=this.g,r=this.b,i=30;return!t&&!n&&!r?R(i,i,i):(t&&t<i&&(t=i),n&&n<i&&(n=i),r&&r<i&&(r=i),R(Math.min(255,Math.floor(t/e)),Math.min(255,Math.floor(n/e)),Math.min(255,Math.floor(r/e))))},U.prototype.darker=function(e){return e=Math.pow(.7,arguments.length?e:1),R(Math.floor(e*this.r),Math.floor(e*this.g),Math.floor(e*this.b))},U.prototype.hsl=function(){return X(this.r,this.g,this.b)},U.prototype.toString=function(){return"#"+z(this.r)+z(this.g)+z(this.b)};var hs=d3.map({aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",darkgrey:"#a9a9a9",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",grey:"#808080",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgreen:"#90ee90",lightgrey:"#d3d3d3",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370db",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#db7093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"});hs.forEach(function(e,t){hs.set(e,W(t,R,G))}),d3.hsl=function(e,t,n){return arguments.length===1?e instanceof Q?K(e.h,e.s,e.l):W(""+e,X,K):K(+e,+t,+n)},Q.prototype.brighter=function(e){return e=Math.pow(.7,arguments.length?e:1),K(this.h,this.s,this.l/e)},Q.prototype.darker=function(e){return e=Math.pow(.7,arguments.length?e:1),K(this.h,this.s,e*this.l)},Q.prototype.rgb=function(){return G(this.h,this.s,this.l)},Q.prototype.toString=function(){return this.rgb().toString()},d3.hcl=function(e,t,n){return arguments.length===1?e instanceof Z?Y(e.h,e.c,e.l):e instanceof nt?it(e.l,e.a,e.b):it((e=V((e=d3.rgb(e)).r,e.g,e.b)).l,e.a,e.b):Y(+e,+t,+n)},Z.prototype.brighter=function(e){return Y(this.h,this.c,Math.min(100,this.l+ps*(arguments.length?e:1)))},Z.prototype.darker=function(e){return Y(this.h,this.c,Math.max(0,this.l-ps*(arguments.length?e:1)))},Z.prototype.rgb=function(){return et(this.h,this.c,this.l).rgb()},Z.prototype.toString=function(){return this.rgb()+""},d3.lab=function(e,t,n){return arguments.length===1?e instanceof nt?tt(e.l,e.a,e.b):e instanceof Z?et(e.l,e.c,e.h):V((e=d3.rgb(e)).r,e.g,e.b):tt(+e,+t,+n)};var ps=18,ds=.95047,vs=1,ms=1.08883;nt.prototype.brighter=function(e){return tt(Math.min(100,this.l+ps*(arguments.length?e:1)),this.a,this.b)},nt.prototype.darker=function(e){return tt(Math.max(0,this.l-ps*(arguments.length?e:1)),this.a,this.b)},nt.prototype.rgb=function(){return rt(this.l,this.a,this.b)},nt.prototype.toString=function(){return this.rgb()+""};var gs=function(e,t){return t.querySelector(e)},ys=function(e,t){return t.querySelectorAll(e)},bs=document.documentElement,ws=bs.matchesSelector||bs.webkitMatchesSelector||bs.mozMatchesSelector||bs.msMatchesSelector||bs.oMatchesSelector,Es=function(e,t){return ws.call(e,t)};typeof Sizzle=="function"&&(gs=function(e,t){return Sizzle(e,t)[0]||null},ys=function(e,t){return Sizzle.uniqueSort(Sizzle(e,t))},Es=Sizzle.matchesSelector);var Ss=[];d3.selection=function(){return xs},d3.selection.prototype=Ss,Ss.select=function(e){var t=[],n,r,i,s;typeof e!="function"&&(e=ft(e));for(var o=-1,u=this.length;++o<u;){t.push(n=[]),n.parentNode=(i=this[o]).parentNode;for(var a=-1,f=i.length;++a<f;)(s=i[a])?(n.push(r=e.call(s,s.__data__,a)),r&&"__data__"in s&&(r.__data__=s.__data__)):n.push(null)}return at(t)},Ss.selectAll=function(e){var t=[],n,r;typeof e!="function"&&(e=lt(e));for(var i=-1,s=this.length;++i<s;)for(var o=this[i],u=-1,a=o.length;++u<a;)if(r=o[u])t.push(n=$i(e.call(r,r.__data__,u))),n.parentNode=r;return at(t)},Ss.attr=function(e,t){if(arguments.length<2){if(typeof e=="string"){var n=this.node();return e=d3.ns.qualify(e),e.local?n.getAttributeNS(e.space,e.local):n.getAttribute(e)}for(t in e)this.each(ct(t,e[t]));return this}return this.each(ct(e,t))},Ss.classed=function(e,t){if(arguments.length<2){if(typeof e=="string"){var n=this.node(),r=(e=e.trim().split(/^|\s+/g)).length,i=-1;if(t=n.classList){while(++i<r)if(!t.contains(e[i]))return!1}else{t=n.className,t.baseVal!=null&&(t=t.baseVal);while(++i<r)if(!ht(e[i]).test(t))return!1}return!0}for(t in e)this.each(pt(t,e[t]));return this}return this.each(pt(e,t))},Ss.style=function(e,t,n){var r=arguments.length;if(r<3){if(typeof e!="string"){r<2&&(t="");for(n in e)this.each(vt(n,e[n],t));return this}if(r<2)return window.getComputedStyle(this.node(),null).getPropertyValue(e);n=""}return this.each(vt(e,t,n))},Ss.property=function(e,t){if(arguments.length<2){if(typeof e=="string")return this.node()[e];for(t in e)this.each(mt(t,e[t]));return this}return this.each(mt(e,t))},Ss.text=function(e){return arguments.length<1?this.node().textContent:this.each(typeof e=="function"?function(){var t=e.apply(this,arguments);this.textContent=t==null?"":t}:e==null?function(){this.textContent=""}:function(){this.textContent=e})},Ss.html=function(e){return arguments.length<1?this.node().innerHTML:this.each(typeof e=="function"?function(){var t=e.apply(this,arguments);this.innerHTML=t==null?"":t}:e==null?function(){this.innerHTML=""}:function(){this.innerHTML=e})},Ss.append=function(e){function t(){return this.appendChild(document.createElementNS(this.namespaceURI,e))}function n(){return this.appendChild(document.createElementNS(e.space,e.local))}return e=d3.ns.qualify(e),this.select(e.local?n:t)},Ss.insert=function(e,t){function n(){return this.insertBefore(document.createElementNS(this.namespaceURI,e),gs(t,this))}function r(){return this.insertBefore(document.createElementNS(e.space,e.local),gs(t,this))}return e=d3.ns.qualify(e),this.select(e.local?r:n)},Ss.remove=function(){return this.each(function(){var e=this.parentNode;e&&e.removeChild(this)})},Ss.data=function(e,t){function n(e,n){var i,s=e.length,o=n.length,u=Math.min(s,o),c=Math.max(s,o),h=[],p=[],d=[],v,m;if(t){var g=new r,y=[],b,w=n.length;for(i=-1;++i<s;)b=t.call(v=e[i],v.__data__,i),g.has(b)?d[w++]=v:g.set(b,v),y.push(b);for(i=-1;++i<o;)b=t.call(n,m=n[i],i),g.has(b)?(h[i]=v=g.get(b),v.__data__=m,p[i]=d[i]=null):(p[i]=gt(m),h[i]=d[i]=null),g.remove(b);for(i=-1;++i<s;)g.has(y[i])&&(d[i]=e[i])}else{for(i=-1;++i<u;)v=e[i],m=n[i],v?(v.__data__=m,h[i]=v,p[i]=d[i]=null):(p[i]=gt(m),h[i]=d[i]=null);for(;i<o;++i)p[i]=gt(n[i]),h[i]=d[i]=null;for(;i<c;++i)d[i]=e[i],p[i]=h[i]=null}p.update=h,p.parentNode=h.parentNode=d.parentNode=e.parentNode,a.push(p),f.push(h),l.push(d)}var i=-1,s=this.length,o,u;if(!arguments.length){e=new Array(s=(o=this[0]).length);while(++i<s)if(u=o[i])e[i]=u.__data__;return e}var a=St([]),f=at([]),l=at([]);if(typeof e=="function")while(++i<s)n(o=this[i],e.call(o,o.parentNode.__data__,i));else while(++i<s)n(o=this[i],e);return f.enter=function(){return a},f.exit=function(){return l},f},Ss.datum=Ss.map=function(e){return arguments.length<1?this.property("__data__"):this.property("__data__",e)},Ss.filter=function(e){var t=[],n,r,i;typeof e!="function"&&(e=yt(e));for(var s=0,o=this.length;s<o;s++){t.push(n=[]),n.parentNode=(r=this[s]).parentNode;for(var u=0,a=r.length;u<a;u++)(i=r[u])&&e.call(i,i.__data__,u)&&n.push(i)}return at(t)},Ss.order=function(){for(var e=-1,t=this.length;++e<t;)for(var n=this[e],r=n.length-1,i=n[r],s;--r>=0;)if(s=n[r])i&&i!==s.nextSibling&&i.parentNode.insertBefore(s,i),i=s;return this},Ss.sort=function(e){e=bt.apply(this,arguments);for(var t=-1,n=this.length;++t<n;)this[t].sort(e);return this.order()},Ss.on=function(e,t,n){var r=arguments.length;if(r<3){if(typeof e!="string"){r<2&&(t=!1);for(n in e)this.each(wt(n,e[n],t));return this}if(r<2)return(r=this.node()["__on"+e])&&r._;n=!1}return this.each(wt(e,t,n))},Ss.each=function(e){return Et(this,function(t,n,r){e.call(t,t.__data__,n,r)})},Ss.call=function(e){return e.apply(this,(arguments[0]=this,arguments)),this},Ss.empty=function(){return!this.node()},Ss.node=function(e){for(var t=0,n=this.length;t<n;t++)for(var r=this[t],i=0,s=r.length;i<s;i++){var o=r[i];if(o)return o}return null},Ss.transition=function(){var e=[],t,n;for(var r=-1,i=this.length;++r<i;){e.push(t=[]);for(var s=this[r],o=-1,u=s.length;++o<u;)t.push((n=s[o])?{node:n,delay:Ms,duration:_s}:null)}return xt(e,ks||++Cs,Date.now())};var xs=at([[document]]);xs[0].parentNode=bs,d3.select=function(e){return typeof e=="string"?xs.select(e):at([[e]])},d3.selectAll=function(e){return typeof e=="string"?xs.selectAll(e):at([$i(e)])};var Ts=[];d3.selection.enter=St,d3.selection.enter.prototype=Ts,Ts.append=Ss.append,Ts.insert=Ss.insert,Ts.empty=Ss.empty,Ts.node=Ss.node,Ts.select=function(e){var t=[],n,r,i,s,o;for(var u=-1,a=this.length;++u<a;){i=(s=this[u]).update,t.push(n=[]),n.parentNode=s.parentNode;for(var f=-1,l=s.length;++f<l;)(o=s[f])?(n.push(i[f]=r=e.call(s.parentNode,o.__data__,f)),r.__data__=o.__data__):n.push(null)}return at(t)};var Ns=[],Cs=0,ks=0,Ls=0,As=250,Os=d3.ease("cubic-in-out"),Ms=Ls,_s=As,Ds=Os;Ns.call=Ss.call,d3.transition=function(e){return arguments.length?ks?e.transition():e:xs.transition()},d3.transition.prototype=Ns,Ns.select=function(e){var t=[],n,r,i;typeof e!="function"&&(e=ft(e));for(var s=-1,o=this.length;++s<o;){t.push(n=[]);for(var u=this[s],a=-1,f=u.length;++a<f;)(i=u[a])&&(r=e.call(i.node,i.node.__data__,a))?("__data__"in i.node&&(r.__data__=i.node.__data__),n.push({node:r,delay:i.delay,duration:i.duration})):n.push(null)}return xt(t,this.id,this.time).ease(this.ease())},Ns.selectAll=function(e){var t=[],n,r,i;typeof e!="function"&&(e=lt(e));for(var s=-1,o=this.length;++s<o;)for(var u=this[s],a=-1,f=u.length;++a<f;)if(i=u[a]){r=e.call(i.node,i.node.__data__,a),t.push(n=[]);for(var l=-1,c=r.length;++l<c;)n.push({node:r[l],delay:i.delay,duration:i.duration})}return xt(t,this.id,this.time).ease(this.ease())},Ns.filter=function(e){var t=[],n,r,i;typeof e!="function"&&(e=yt(e));for(var s=0,o=this.length;s<o;s++){t.push(n=[]);for(var r=this[s],u=0,a=r.length;u<a;u++)(i=r[u])&&e.call(i.node,i.node.__data__,u)&&n.push(i)}return xt(t,this.id,this.time).ease(this.ease())},Ns.attr=function(e,t){if(arguments.length<2){for(t in e)this.attrTween(t,Ct(e[t],t));return this}return this.attrTween(e,Ct(t,e))},Ns.attrTween=function(e,t){function n(e,n){var r=t.call(this,e,n,this.getAttribute(i));return r===Ps?(this.removeAttribute(i),null):r&&function(e){this.setAttribute(i,r(e))}}function r(e,n){var r=t.call(this,e,n,this.getAttributeNS(i.space,i.local));return r===Ps?(this.removeAttributeNS(i.space,i.local),null):r&&function(e){this.setAttributeNS(i.space,i.local,r(e))}}var i=d3.ns.qualify(e);return this.tween("attr."+e,i.local?r:n)},Ns.style=function(e,t,n){var r=arguments.length;if(r<3){if(typeof e!="string"){r<2&&(t="");for(n in e)this.styleTween(n,Ct(e[n],n),t);return this}n=""}return this.styleTween(e,Ct(t,e),n)},Ns.styleTween=function(e,t,n){return arguments.length<3&&(n=""),this.tween("style."+e,function(r,i){var s=t.call(this,r,i,window.getComputedStyle(this,null).getPropertyValue(e));return s===Ps?(this.style.removeProperty(e),null):s&&function(t){this.style.setProperty(e,s(t),n)}})},Ns.text=function(e){return this.tween("text",function(t,n){this.textContent=typeof e=="function"?e.call(this,t,n):e})},Ns.remove=function(){return this.each("end.transition",function(){var e;!this.__transition__&&(e=this.parentNode)&&e.removeChild(this)})},Ns.delay=function(e){return Et(this,typeof e=="function"?function(t,n,r){t.delay=e.call(t=t.node,t.__data__,n,r)|0}:(e|=0,function(t){t.delay=e}))},Ns.duration=function(e){return Et(this,typeof e=="function"?function(t,n,r){t.duration=Math.max(1,e.call(t=t.node,t.__data__,n,r)|0)}:(e=Math.max(1,e|0),function(t){t.duration=e}))},Ns.transition=function(){return this.select(s)},d3.tween=function(e,t){function n(n,r,i){var s=e.call(this,n,r);return s==null?i!=""&&Ps:i!=s&&t(i,s)}function r(n,r,i){return i!=e&&t(i,e)}return typeof e=="function"?n:e==null?Nt:(e+="",r)};var Ps={},Hs=null,Bs,js;d3.timer=function(e,t,n){var r=!1,i,s=Hs;if(arguments.length<3){if(arguments.length<2)t=0;else if(!isFinite(t))return;n=Date.now()}while(s){if(s.callback===e){s.then=n,s.delay=t,r=!0;break}i=s,s=s.next}r||(Hs={callback:e,then:n,delay:t,next:Hs}),Bs||(js=clearTimeout(js),Bs=1,Fs(kt))},d3.timer.flush=function(){var e,t=Date.now(),n=Hs;while(n)e=t-n.then,n.delay||(n.flush=n.callback(e)),n=n.next;Lt()};var Fs=window.requestAnimationFrame||window.webkitRequestAnimationFrame||
+window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(e){setTimeout(e,17)};d3.mouse=function(e){return At(e,_())};var Is=/WebKit/.test(navigator.userAgent)?-1:0;d3.touches=function(e,t){return arguments.length<2&&(t=_().touches),t?$i(t).map(function(t){var n=At(e,t);return n.identifier=t.identifier,n}):[]},d3.scale={},d3.scale.linear=function(){return Ht([0,1],[0,1],d3.interpolate,!1)},d3.scale.log=function(){return zt(d3.scale.linear(),Wt)};var qs=d3.format(".0e");Wt.pow=function(e){return Math.pow(10,e)},Xt.pow=function(e){return-Math.pow(10,-e)},d3.scale.pow=function(){return Vt(d3.scale.linear(),1)},d3.scale.sqrt=function(){return d3.scale.pow().exponent(.5)},d3.scale.ordinal=function(){return Jt([],{t:"range",a:[[]]})},d3.scale.category10=function(){return d3.scale.ordinal().range(Rs)},d3.scale.category20=function(){return d3.scale.ordinal().range(Us)},d3.scale.category20b=function(){return d3.scale.ordinal().range(zs)},d3.scale.category20c=function(){return d3.scale.ordinal().range(Ws)};var Rs=["#1f77b4","#ff7f0e","#2ca02c","#d62728","#9467bd","#8c564b","#e377c2","#7f7f7f","#bcbd22","#17becf"],Us=["#1f77b4","#aec7e8","#ff7f0e","#ffbb78","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5","#8c564b","#c49c94","#e377c2","#f7b6d2","#7f7f7f","#c7c7c7","#bcbd22","#dbdb8d","#17becf","#9edae5"],zs=["#393b79","#5254a3","#6b6ecf","#9c9ede","#637939","#8ca252","#b5cf6b","#cedb9c","#8c6d31","#bd9e39","#e7ba52","#e7cb94","#843c39","#ad494a","#d6616b","#e7969c","#7b4173","#a55194","#ce6dbd","#de9ed6"],Ws=["#3182bd","#6baed6","#9ecae1","#c6dbef","#e6550d","#fd8d3c","#fdae6b","#fdd0a2","#31a354","#74c476","#a1d99b","#c7e9c0","#756bb1","#9e9ac8","#bcbddc","#dadaeb","#636363","#969696","#bdbdbd","#d9d9d9"];d3.scale.quantile=function(){return Kt([],[])},d3.scale.quantize=function(){return Qt(0,1,[0,1])},d3.scale.threshold=function(){return Gt([.5],[0,1])},d3.scale.identity=function(){return Yt([0,1])},d3.svg={},d3.svg.arc=function(){function e(){var e=t.apply(this,arguments),s=n.apply(this,arguments),o=r.apply(this,arguments)+Xs,u=i.apply(this,arguments)+Xs,a=(u<o&&(a=o,o=u,u=a),u-o),f=a<Math.PI?"0":"1",l=Math.cos(o),c=Math.sin(o),h=Math.cos(u),p=Math.sin(u);return a>=Vs?e?"M0,"+s+"A"+s+","+s+" 0 1,1 0,"+ -s+"A"+s+","+s+" 0 1,1 0,"+s+"M0,"+e+"A"+e+","+e+" 0 1,0 0,"+ -e+"A"+e+","+e+" 0 1,0 0,"+e+"Z":"M0,"+s+"A"+s+","+s+" 0 1,1 0,"+ -s+"A"+s+","+s+" 0 1,1 0,"+s+"Z":e?"M"+s*l+","+s*c+"A"+s+","+s+" 0 "+f+",1 "+s*h+","+s*p+"L"+e*h+","+e*p+"A"+e+","+e+" 0 "+f+",0 "+e*l+","+e*c+"Z":"M"+s*l+","+s*c+"A"+s+","+s+" 0 "+f+",1 "+s*h+","+s*p+"L0,0"+"Z"}var t=Zt,n=en,r=tn,i=nn;return e.innerRadius=function(n){return arguments.length?(t=u(n),e):t},e.outerRadius=function(t){return arguments.length?(n=u(t),e):n},e.startAngle=function(t){return arguments.length?(r=u(t),e):r},e.endAngle=function(t){return arguments.length?(i=u(t),e):i},e.centroid=function(){var e=(t.apply(this,arguments)+n.apply(this,arguments))/2,s=(r.apply(this,arguments)+i.apply(this,arguments))/2+Xs;return[Math.cos(s)*e,Math.sin(s)*e]},e};var Xs=-Math.PI/2,Vs=2*Math.PI-1e-6;d3.svg.line=function(){return rn(i)};var $s=d3.map({linear:un,"linear-closed":an,"step-before":fn,"step-after":ln,basis:mn,"basis-open":gn,"basis-closed":yn,bundle:bn,cardinal:pn,"cardinal-open":cn,"cardinal-closed":hn,monotone:Nn});$s.forEach(function(e,t){t.key=e,t.closed=/-closed$/.test(e)});var Js=[0,2/3,1/3,0],Ks=[0,1/3,2/3,0],Qs=[0,1/6,2/3,1/6];d3.svg.line.radial=function(){var e=rn(Cn);return e.radius=e.x,delete e.x,e.angle=e.y,delete e.y,e},fn.reverse=ln,ln.reverse=fn,d3.svg.area=function(){return kn(i)},d3.svg.area.radial=function(){var e=kn(Cn);return e.radius=e.x,delete e.x,e.innerRadius=e.x0,delete e.x0,e.outerRadius=e.x1,delete e.x1,e.angle=e.y,delete e.y,e.startAngle=e.y0,delete e.y0,e.endAngle=e.y1,delete e.y1,e},d3.svg.chord=function(){function e(e,u){var a=t(this,s,e,u),f=t(this,o,e,u);return"M"+a.p0+r(a.r,a.p1,a.a1-a.a0)+(n(a,f)?i(a.r,a.p1,a.r,a.p0):i(a.r,a.p1,f.r,f.p0)+r(f.r,f.p1,f.a1-f.a0)+i(f.r,f.p1,a.r,a.p0))+"Z"}function t(e,t,n,r){var i=t.call(e,n,r),s=a.call(e,i,r),o=f.call(e,i,r)+Xs,u=l.call(e,i,r)+Xs;return{r:s,a0:o,a1:u,p0:[s*Math.cos(o),s*Math.sin(o)],p1:[s*Math.cos(u),s*Math.sin(u)]}}function n(e,t){return e.a0==t.a0&&e.a1==t.a1}function r(e,t,n){return"A"+e+","+e+" 0 "+ +(n>Math.PI)+",1 "+t}function i(e,t,n,r){return"Q 0,0 "+r}var s=Ln,o=An,a=On,f=tn,l=nn;return e.radius=function(t){return arguments.length?(a=u(t),e):a},e.source=function(t){return arguments.length?(s=u(t),e):s},e.target=function(t){return arguments.length?(o=u(t),e):o},e.startAngle=function(t){return arguments.length?(f=u(t),e):f},e.endAngle=function(t){return arguments.length?(l=u(t),e):l},e},d3.svg.diagonal=function(){function e(e,i){var s=t.call(this,e,i),o=n.call(this,e,i),u=(s.y+o.y)/2,a=[s,{x:s.x,y:u},{x:o.x,y:u},o];return a=a.map(r),"M"+a[0]+"C"+a[1]+" "+a[2]+" "+a[3]}var t=Ln,n=An,r=Dn;return e.source=function(n){return arguments.length?(t=u(n),e):t},e.target=function(t){return arguments.length?(n=u(t),e):n},e.projection=function(t){return arguments.length?(r=t,e):r},e},d3.svg.diagonal.radial=function(){var e=d3.svg.diagonal(),t=Dn,n=e.projection;return e.projection=function(e){return arguments.length?n(Pn(t=e)):t},e},d3.svg.mouse=d3.mouse,d3.svg.touches=d3.touches,d3.svg.symbol=function(){function e(e,r){return(Gs.get(t.call(this,e,r))||jn)(n.call(this,e,r))}var t=Bn,n=Hn;return e.type=function(n){return arguments.length?(t=u(n),e):t},e.size=function(t){return arguments.length?(n=u(t),e):n},e};var Gs=d3.map({circle:jn,cross:function(e){var t=Math.sqrt(e/5)/2;return"M"+ -3*t+","+ -t+"H"+ -t+"V"+ -3*t+"H"+t+"V"+ -t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+ -t+"V"+t+"H"+ -3*t+"Z"},diamond:function(e){var t=Math.sqrt(e/(2*Zs)),n=t*Zs;return"M0,"+ -t+"L"+n+",0"+" 0,"+t+" "+ -n+",0"+"Z"},square:function(e){var t=Math.sqrt(e)/2;return"M"+ -t+","+ -t+"L"+t+","+ -t+" "+t+","+t+" "+ -t+","+t+"Z"},"triangle-down":function(e){var t=Math.sqrt(e/Ys),n=t*Ys/2;return"M0,"+n+"L"+t+","+ -n+" "+ -t+","+ -n+"Z"},"triangle-up":function(e){var t=Math.sqrt(e/Ys),n=t*Ys/2;return"M0,"+ -n+"L"+t+","+n+" "+ -t+","+n+"Z"}});d3.svg.symbolTypes=Gs.keys();var Ys=Math.sqrt(3),Zs=Math.tan(30*Math.PI/180);d3.svg.axis=function(){function e(e){e.each(function(){var e=d3.select(this),c=a==null?t.ticks?t.ticks.apply(t,u):t.domain():a,h=f==null?t.tickFormat?t.tickFormat.apply(t,u):String:f,p=qn(t,c,l),d=e.selectAll(".minor").data(p,String),v=d.enter().insert("line","g").attr("class","tick minor").style("opacity",1e-6),m=d3.transition(d.exit()).style("opacity",1e-6).remove(),g=d3.transition(d).style("opacity",1),y=e.selectAll("g").data(c,String),b=y.enter().insert("g","path").style("opacity",1e-6),w=d3.transition(y.exit()).style("opacity",1e-6).remove(),E=d3.transition(y).style("opacity",1),S,x=_t(t),T=e.selectAll(".domain").data([0]),N=T.enter().append("path").attr("class","domain"),C=d3.transition(T),k=t.copy(),L=this.__chart__||k;this.__chart__=k,b.append("line").attr("class","tick"),b.append("text");var A=b.select("line"),O=E.select("line"),M=y.select("text").text(h),_=b.select("text"),D=E.select("text");switch(n){case"bottom":S=Fn,v.attr("y2",i),g.attr("x2",0).attr("y2",i),A.attr("y2",r),_.attr("y",Math.max(r,0)+o),O.attr("x2",0).attr("y2",r),D.attr("x",0).attr("y",Math.max(r,0)+o),M.attr("dy",".71em").attr("text-anchor","middle"),C.attr("d","M"+x[0]+","+s+"V0H"+x[1]+"V"+s);break;case"top":S=Fn,v.attr("y2",-i),g.attr("x2",0).attr("y2",-i),A.attr("y2",-r),_.attr("y",-(Math.max(r,0)+o)),O.attr("x2",0).attr("y2",-r),D.attr("x",0).attr("y",-(Math.max(r,0)+o)),M.attr("dy","0em").attr("text-anchor","middle"),C.attr("d","M"+x[0]+","+ -s+"V0H"+x[1]+"V"+ -s);break;case"left":S=In,v.attr("x2",-i),g.attr("x2",-i).attr("y2",0),A.attr("x2",-r),_.attr("x",-(Math.max(r,0)+o)),O.attr("x2",-r).attr("y2",0),D.attr("x",-(Math.max(r,0)+o)).attr("y",0),M.attr("dy",".32em").attr("text-anchor","end"),C.attr("d","M"+ -s+","+x[0]+"H0V"+x[1]+"H"+ -s);break;case"right":S=In,v.attr("x2",i),g.attr("x2",i).attr("y2",0),A.attr("x2",r),_.attr("x",Math.max(r,0)+o),O.attr("x2",r).attr("y2",0),D.attr("x",Math.max(r,0)+o).attr("y",0),M.attr("dy",".32em").attr("text-anchor","start"),C.attr("d","M"+s+","+x[0]+"H0V"+x[1]+"H"+s)}if(t.ticks)b.call(S,L),E.call(S,k),w.call(S,k),v.call(S,L),g.call(S,k),m.call(S,k);else{var P=k.rangeBand()/2,H=function(e){return k(e)+P};b.call(S,H),E.call(S,H)}})}var t=d3.scale.linear(),n="bottom",r=6,i=6,s=6,o=3,u=[10],a=null,f,l=0;return e.scale=function(n){return arguments.length?(t=n,e):t},e.orient=function(t){return arguments.length?(n=t,e):n},e.ticks=function(){return arguments.length?(u=arguments,e):u},e.tickValues=function(t){return arguments.length?(a=t,e):a},e.tickFormat=function(t){return arguments.length?(f=t,e):f},e.tickSize=function(t,n,o){if(!arguments.length)return r;var u=arguments.length-1;return r=+t,i=u>1?+n:r,s=u>0?+arguments[u]:r,e},e.tickPadding=function(t){return arguments.length?(o=+t,e):o},e.tickSubdivide=function(t){return arguments.length?(l=+t,e):l},e},d3.svg.brush=function(){function e(s){s.each(function(){var s=d3.select(this),f=s.selectAll(".background").data([0]),l=s.selectAll(".extent").data([0]),c=s.selectAll(".resize").data(a,String),h;s.style("pointer-events","all").on("mousedown.brush",i).on("touchstart.brush",i),f.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),l.enter().append("rect").attr("class","extent").style("cursor","move"),c.enter().append("g").attr("class",function(e){return"resize "+e}).style("cursor",function(e){return eo[e]}).append("rect").attr("x",function(e){return/[ew]$/.test(e)?-3:null}).attr("y",function(e){return/^[ns]/.test(e)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),c.style("display",e.empty()?"none":null),c.exit().remove(),o&&(h=_t(o),f.attr("x",h[0]).attr("width",h[1]-h[0]),n(s)),u&&(h=_t(u),f.attr("y",h[0]).attr("height",h[1]-h[0]),r(s)),t(s)})}function t(e){e.selectAll(".resize").attr("transform",function(e){return"translate("+f[+/e$/.test(e)][0]+","+f[+/^s/.test(e)][1]+")"})}function n(e){e.select(".extent").attr("x",f[0][0]),e.selectAll(".extent,.n>rect,.s>rect").attr("width",f[1][0]-f[0][0])}function r(e){e.select(".extent").attr("y",f[0][1]),e.selectAll(".extent,.e>rect,.w>rect").attr("height",f[1][1]-f[0][1])}function i(){function i(){var e=d3.event.changedTouches;return e?d3.touches(v,e)[0]:d3.mouse(v)}function a(){d3.event.keyCode==32&&(S||(x=null,T[0]-=f[1][0],T[1]-=f[1][1],S=2),M())}function c(){d3.event.keyCode==32&&S==2&&(T[0]+=f[1][0],T[1]+=f[1][1],S=0,M())}function h(){var e=i(),s=!1;N&&(e[0]+=N[0],e[1]+=N[1]),S||(d3.event.altKey?(x||(x=[(f[0][0]+f[1][0])/2,(f[0][1]+f[1][1])/2]),T[0]=f[+(e[0]<x[0])][0],T[1]=f[+(e[1]<x[1])][1]):x=null),w&&p(e,o,0)&&(n(y),s=!0),E&&p(e,u,1)&&(r(y),s=!0),s&&(t(y),g({type:"brush",mode:S?"move":"resize"}))}function p(e,t,n){var r=_t(t),i=r[0],s=r[1],o=T[n],u=f[1][n]-f[0][n],a,c;S&&(i-=o,s-=u+o),a=Math.max(i,Math.min(s,e[n])),S?c=(a+=o)+u:(x&&(o=Math.max(i,Math.min(s,2*x[n]-a))),o<a?(c=a,a=o):c=o);if(f[0][n]!==a||f[1][n]!==c)return l=null,f[0][n]=a,f[1][n]=c,!0}function d(){h(),y.style("pointer-events","all").selectAll(".resize").style("display",e.empty()?"none":null),d3.select("body").style("cursor",null),C.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),g({type:"brushend"}),M()}var v=this,m=d3.select(d3.event.target),g=s.of(v,arguments),y=d3.select(v),b=m.datum(),w=!/^(n|s)$/.test(b)&&o,E=!/^(e|w)$/.test(b)&&u,S=m.classed("extent"),x,T=i(),N,C=d3.select(window).on("mousemove.brush",h).on("mouseup.brush",d).on("touchmove.brush",h).on("touchend.brush",d).on("keydown.brush",a).on("keyup.brush",c);if(S)T[0]=f[0][0]-T[0],T[1]=f[0][1]-T[1];else if(b){var k=+/w$/.test(b),L=+/^n/.test(b);N=[f[1-k][0]-T[0],f[1-L][1]-T[1]],T[0]=f[k][0],T[1]=f[L][1]}else d3.event.altKey&&(x=T.slice());y.style("pointer-events","none").selectAll(".resize").style("display",null),d3.select("body").style("cursor",m.style("cursor")),g({type:"brushstart"}),h(),M()}var s=D(e,"brushstart","brush","brushend"),o=null,u=null,a=to[0],f=[[0,0],[0,0]],l;return e.x=function(t){return arguments.length?(o=t,a=to[!o<<1|!u],e):o},e.y=function(t){return arguments.length?(u=t,a=to[!o<<1|!u],e):u},e.extent=function(t){var n,r,i,s,a;return arguments.length?(l=[[0,0],[0,0]],o&&(n=t[0],r=t[1],u&&(n=n[0],r=r[0]),l[0][0]=n,l[1][0]=r,o.invert&&(n=o(n),r=o(r)),r<n&&(a=n,n=r,r=a),f[0][0]=n|0,f[1][0]=r|0),u&&(i=t[0],s=t[1],o&&(i=i[1],s=s[1]),l[0][1]=i,l[1][1]=s,u.invert&&(i=u(i),s=u(s)),s<i&&(a=i,i=s,s=a),f[0][1]=i|0,f[1][1]=s|0),e):(t=l||f,o&&(n=t[0][0],r=t[1][0],l||(n=f[0][0],r=f[1][0],o.invert&&(n=o.invert(n),r=o.invert(r)),r<n&&(a=n,n=r,r=a))),u&&(i=t[0][1],s=t[1][1],l||(i=f[0][1],s=f[1][1],u.invert&&(i=u.invert(i),s=u.invert(s)),s<i&&(a=i,i=s,s=a))),o&&u?[[n,i],[r,s]]:o?[n,r]:u&&[i,s])},e.clear=function(){return l=null,f[0][0]=f[0][1]=f[1][0]=f[1][1]=0,e},e.empty=function(){return o&&f[0][0]===f[1][0]||u&&f[0][1]===f[1][1]},d3.rebind(e,s,"on")};var eo={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},to=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]];d3.behavior={},d3.behavior.drag=function(){function e(){this.on("mousedown.drag",t).on("touchstart.drag",t)}function t(){function e(){var e=o.parentNode;return f?d3.touches(e).filter(function(e){return e.identifier===f})[0]:d3.mouse(e)}function t(){if(!o.parentNode)return i();var t=e(),n=t[0]-c[0],r=t[1]-c[1];h|=n|r,c=t,M(),u({type:"drag",x:t[0]+l[0],y:t[1]+l[1],dx:n,dy:r})}function i(){u({type:"dragend"}),h&&(M(),d3.event.target===a&&p.on("click.drag",s,!0)),p.on(f?"touchmove.drag-"+f:"mousemove.drag",null).on(f?"touchend.drag-"+f:"mouseup.drag",null)}function s(){M(),p.on("click.drag",null)}var o=this,u=n.of(o,arguments),a=d3.event.target,f=d3.event.touches&&d3.event.changedTouches[0].identifier,l,c=e(),h=0,p=d3.select(window).on(f?"touchmove.drag-"+f:"mousemove.drag",t).on(f?"touchend.drag-"+f:"mouseup.drag",i,!0);r?(l=r.apply(o,arguments),l=[l.x-c[0],l.y-c[1]]):l=[0,0],f||M(),u({type:"dragstart"})}var n=D(e,"drag","dragstart","dragend"),r=null;return e.origin=function(t){return arguments.length?(r=t,e):r},d3.rebind(e,n,"on")},d3.behavior.zoom=function(){function e(){this.on("mousedown.zoom",o).on("mousewheel.zoom",u).on("mousemove.zoom",a).on("DOMMouseScroll.zoom",u).on("dblclick.zoom",f).on("touchstart.zoom",l).on("touchmove.zoom",c).on("touchend.zoom",l)}function t(e){return[(e[0]-h[0])/d,(e[1]-h[1])/d]}function n(e){return[e[0]*d+h[0],e[1]*d+h[1]]}function r(e){d=Math.max(m[0],Math.min(m[1],e))}function i(e,t){t=n(t),h[0]+=e[0]-t[0],h[1]+=e[1]-t[1]}function s(e){b&&b.domain(y.range().map(function(e){return(e-h[0])/d}).map(y.invert)),E&&E.domain(w.range().map(function(e){return(e-h[1])/d}).map(w.invert)),d3.event.preventDefault(),e({type:"zoom",scale:d,translate:h})}function o(){function e(){f=1,i(d3.mouse(o),c),s(u)}function n(){f&&M(),l.on("mousemove.zoom",null).on("mouseup.zoom",null),f&&d3.event.target===a&&l.on("click.zoom",r,!0)}function r(){M(),l.on("click.zoom",null)}var o=this,u=g.of(o,arguments),a=d3.event.target,f=0,l=d3.select(window).on("mousemove.zoom",e).on("mouseup.zoom",n),c=t(d3.mouse(o));window.focus(),M()}function u(){p||(p=t(d3.mouse(this))),r(Math.pow(2,Rn()*.002)*d),i(d3.mouse(this),p),s(g.of(this,arguments))}function a(){p=null}function f(){var e=d3.mouse(this),n=t(e);r(d3.event.shiftKey?d/2:d*2),i(e,n),s(g.of(this,arguments))}function l(){var e=d3.touches(this),n=Date.now();v=d,p={},e.forEach(function(e){p[e.identifier]=t(e)}),M();if(e.length===1){if(n-S<500){var o=e[0],u=t(e[0]);r(d*2),i(o,u),s(g.of(this,arguments))}S=n}}function c(){var e=d3.touches(this),t=e[0],n=p[t.identifier];if(o=e[1]){var o,u=p[o.identifier];t=[(t[0]+o[0])/2,(t[1]+o[1])/2],n=[(n[0]+u[0])/2,(n[1]+u[1])/2],r(d3.event.scale*v)}i(t,n),S=null,s(g.of(this,arguments))}var h=[0,0],p,d=1,v,m=ro,g=D(e,"zoom"),y,b,w,E,S;return e.translate=function(t){return arguments.length?(h=t.map(Number),e):h},e.scale=function(t){return arguments.length?(d=+t,e):d},e.scaleExtent=function(t){return arguments.length?(m=t==null?ro:t.map(Number),e):m},e.x=function(t){return arguments.length?(b=t,y=t.copy(),e):b},e.y=function(t){return arguments.length?(E=t,w=t.copy(),e):E},d3.rebind(e,g,"on")};var no,ro=[0,Infinity];d3.layout={},d3.layout.bundle=function(){return function(e){var t=[],n=-1,r=e.length;while(++n<r)t.push(Un(e[n]));return t}},d3.layout.chord=function(){function e(){var e={},n=[],c=d3.range(o),h=[],p,d,v,m,g;r=[],i=[],p=0,m=-1;while(++m<o){d=0,g=-1;while(++g<o)d+=s[m][g];n.push(d),h.push(d3.range(o)),p+=d}a&&c.sort(function(e,t){return a(n[e],n[t])}),f&&h.forEach(function(e,t){e.sort(function(e,n){return f(s[t][e],s[t][n])})}),p=(2*Math.PI-u*o)/p,d=0,m=-1;while(++m<o){v=d,g=-1;while(++g<o){var y=c[m],b=h[y][g],w=s[y][b],E=d,S=d+=w*p;e[y+"-"+b]={index:y,subindex:b,startAngle:E,endAngle:S,value:w}}i[y]={index:y,startAngle:v,endAngle:d,value:(d-v)/p},d+=u}m=-1;while(++m<o){g=m-1;while(++g<o){var x=e[m+"-"+g],T=e[g+"-"+m];(x.value||T.value)&&r.push(x.value<T.value?{source:T,target:x}:{source:x,target:T})}}l&&t()}function t(){r.sort(function(e,t){return l((e.source.value+e.target.value)/2,(t.source.value+t.target.value)/2)})}var n={},r,i,s,o,u=0,a,f,l;return n.matrix=function(e){return arguments.length?(o=(s=e)&&s.length,r=i=null,n):s},n.padding=function(e){return arguments.length?(u=e,r=i=null,n):u},n.sortGroups=function(e){return arguments.length?(a=e,r=i=null,n):a},n.sortSubgroups=function(e){return arguments.length?(f=e,r=null,n):f},n.sortChords=function(e){return arguments.length?(l=e,r&&t(),n):l},n.chords=function(){return r||e(),r},n.groups=function(){return i||e(),i},n},d3.layout.force=function(){function e(e){return function(t,n,r,i,s){if(t.point!==e){var o=t.cx-e.x,u=t.cy-e.y,a=1/Math.sqrt(o*o+u*u);if((i-n)*a<d){var f=t.charge*a*a;return e.px-=o*f,e.py-=u*f,!0}if(t.point&&isFinite(a)){var f=t.pointCharge*a*a;e.px-=o*f,e.py-=u*f}}return!t.charge}}function t(e){e.px=d3.event.x,e.py=d3.event.y,n.resume()}var n={},r=d3.dispatch("start","tick","end"),s=[1,1],o,a,f=.9,l=Qn,c=Gn,h=-30,p=.1,d=.8,v,m=[],g=[],y,b,w;return n.tick=function(){if((a*=.99)<.005)return r.end({type:"end",alpha:a=0}),!0;var t=m.length,n=g.length,i,o,u,l,c,d,v,E,S;for(o=0;o<n;++o){u=g[o],l=u.source,c=u.target,E=c.x-l.x,S=c.y-l.y;if(d=E*E+S*S)d=a*b[o]*((d=Math.sqrt(d))-y[o])/d,E*=d,S*=d,c.x-=E*(v=l.weight/(c.weight+l.weight)),c.y-=S*v,l.x+=E*(v=1-v),l.y+=S*v}if(v=a*p){E=s[0]/2,S=s[1]/2,o=-1;if(v)while(++o<t)u=m[o],u.x+=(E-u.x)*v,u.y+=(S-u.y)*v}if(h){Kn(i=d3.geom.quadtree(m),a,w),o=-1;while(++o<t)(u=m[o]).fixed||i.visit(e(u))}o=-1;while(++o<t)u=m[o],u.fixed?(u.x=u.px,u.y=u.py):(u.x-=(u.px-(u.px=u.x))*f,u.y-=(u.py-(u.py=u.y))*f);r.tick({type:"tick",alpha:a})},n.nodes=function(e){return arguments.length?(m=e,n):m},n.links=function(e){return arguments.length?(g=e,n):g},n.size=function(e){return arguments.length?(s=e,n):s},n.linkDistance=function(e){return arguments.length?(l=u(e),n):l},n.distance=n.linkDistance,n.linkStrength=function(e){return arguments.length?(c=u(e),n):c},n.friction=function(e){return arguments.length?(f=e,n):f},n.charge=function(e){return arguments.length?(h=typeof e=="function"?e:+e,n):h},n.gravity=function(e){return arguments.length?(p=e,n):p},n.theta=function(e){return arguments.length?(d=e,n):d},n.alpha=function(e){return arguments.length?(a?e>0?a=e:a=0:e>0&&(r.start({type:"start",alpha:a=e}),d3.timer(n.tick)),n):a},n.start=function(){function e(e,n){var i=t(r),s=-1,o=i.length,u;while(++s<o)if(!isNaN(u=i[s][e]))return u;return Math.random()*n}function t(){if(!p){p=[];for(i=0;i<o;++i)p[i]=[];for(i=0;i<u;++i){var e=g[i];p[e.source.index].push(e.target),p[e.target.index].push(e.source)}}return p[r]}var r,i,o=m.length,u=g.length,a=s[0],f=s[1],p,d;for(r=0;r<o;++r)(d=m[r]).index=r,d.weight=0;y=[],b=[];for(r=0;r<u;++r)d=g[r],typeof d.source=="number"&&(d.source=m[d.source]),typeof d.target=="number"&&(d.target=m[d.target]),y[r]=l.call(this,d,r),b[r]=c.call(this,d,r),++d.source.weight,++d.target.weight;for(r=0;r<o;++r)d=m[r],isNaN(d.x)&&(d.x=e("x",a)),isNaN(d.y)&&(d.y=e("y",f)),isNaN(d.px)&&(d.px=d.x),isNaN(d.py)&&(d.py=d.y);w=[];if(typeof h=="function")for(r=0;r<o;++r)w[r]=+h.call(this,m[r],r);else for(r=0;r<o;++r)w[r]=h;return n.resume()},n.resume=function(){return n.alpha(.1)},n.stop=function(){return n.alpha(0)},n.drag=function(){o||(o=d3.behavior.drag().origin(i).on("dragstart",Xn).on("drag",t).on("dragend",Vn)),this.on("mouseover.force",$n).on("mouseout.force",Jn).call(o)},d3.rebind(n,r,"on")},d3.layout.partition=function(){function e(t,n,r,i){var s=t.children;t.x=n,t.y=t.depth*i,t.dx=r,t.dy=i;if(s&&(u=s.length)){var o=-1,u,a,f;r=t.value?r/t.value:0;while(++o<u)e(a=s[o],n,f=a.value*r,i),n+=f}}function t(e){var n=e.children,r=0;if(n&&(s=n.length)){var i=-1,s;while(++i<s)r=Math.max(r,t(n[i]))}return 1+r}function n(n,s){var o=r.call(this,n,s);return e(o[0],0,i[0],i[1]/t(o[0])),o}var r=d3.layout.hierarchy(),i=[1,1];return n.size=function(e){return arguments.length?(i=e,n):i},fr(n,r)},d3.layout.pie=function(){function e(s,o){var u=s.map(function(n,r){return+t.call(e,n,r)}),a=+(typeof r=="function"?r.apply(this,arguments):r),f=((typeof i=="function"?i.apply(this,arguments):i)-r)/d3.sum(u),l=d3.range(s.length);n!=null&&l.sort(n===io?function(e,t){return u[t]-u[e]}:function(e,t){return n(s[e],s[t])});var c=[];return l.forEach(function(e){var t;c[e]={data:s[e],value:t=u[e],startAngle:a,endAngle:a+=t*f}}),c}var t=Number,n=io,r=0,i=2*Math.PI;return e.value=function(n){return arguments.length?(t=n,e):t},e.sort=function(t){return arguments.length?(n=t,e):n},e.startAngle=function(t){return arguments.length?(r=t,e):r},e.endAngle=function(t){return arguments.length?(i=t,e):i},e};var io={};d3.layout.stack=function(){function e(i,a){var f=i.map(function(n,r){return t.call(e,n,r)}),l=f.map(function(t,n){return t.map(function(t,n){return[o.call(e,t,n),u.call(e,t,n)]})}),c=n.call(e,l,a);f=d3.permute(f,c),l=d3.permute(l,c);var h=r.call(e,l,a),p=f.length,d=f[0].length,v,m,g;for(m=0;m<d;++m){s.call(e,f[0][m],g=h[m],l[0][m][1]);for(v=1;v<p;++v)s.call(e,f[v][m],g+=l[v-1][m][1],l[v][m][1])}return i}var t=i,n=tr,r=nr,s=er,o=Yn,u=Zn;return e.values=function(n){return arguments.length?(t=n,e):t},e.order=function(t){return arguments.length?(n=typeof t=="function"?t:so.get(t)||tr,e):n},e.offset=function(t){return arguments.length?(r=typeof t=="function"?t:oo.get(t)||nr,e):r},e.x=function(t){return arguments.length?(o=t,e):o},e.y=function(t){return arguments.length?(u=t,e):u},e.out=function(t){return arguments.length?(s=t,e):s},e};var so=d3.map({"inside-out":function(e){var t=e.length,n,r,i=e.map(rr),s=e.map(ir),o=d3.range(t).sort(function(e,t){return i[e]-i[t]}),u=0,a=0,f=[],l=[];for(n=0;n<t;++n)r=o[n],u<a?(u+=s[r],f.push(r)):(a+=s[r],l.push(r));return l.reverse().concat(f)},reverse:function(e){return d3.range(e.length).reverse()},"default":tr}),oo=d3.map({silhouette:function(e){var t=e.length,n=e[0].length,r=[],i=0,s,o,u,a=[];for(o=0;o<n;++o){for(s=0,u=0;s<t;s++)u+=e[s][o][1];u>i&&(i=u),r.push(u)}for(o=0;o<n;++o)a[o]=(i-r[o])/2;return a},wiggle:function(e){var t=e.length,n=e[0],r=n.length,i=0,s,o,u,a,f,l,c,h,p,d=[];d[0]=h=p=0;for(o=1;o<r;++o){for(s=0,a=0;s<t;++s)a+=e[s][o][1];for(s=0,f=0,c=n[o][0]-n[o-1][0];s<t;++s){for(u=0,l=(e[s][o][1]-e[s][o-1][1])/(2*c);u<s;++u)l+=(e[u][o][1]-e[u][o-1][1])/c;f+=l*e[s][o][1]}d[o]=h-=a?f/a*c:0,h<p&&(p=h)}for(o=0;o<r;++o)d[o]-=p;return d},expand:function(e){var t=e.length,n=e[0].length,r=1/t,i,s,o,u=[];for(s=0;s<n;++s){for(i=0,o=0;i<t;i++)o+=e[i][s][1];if(o)for(i=0;i<t;i++)e[i][s][1]/=o;else for(i=0;i<t;i++)e[i][s][1]=r}for(s=0;s<n;++s)u[s]=0;return u},zero:nr});d3.layout.histogram=function(){function e(e,s){var o=[],u=e.map(n,this),a=r.call(this,u,s),f=i.call(this,a,u,s),l,s=-1,c=u.length,h=f.length-1,p=t?1:1/c,d;while(++s<h)l=o[s]=[],l.dx=f[s+1]-(l.x=f[s]),l.y=0;if(h>0){s=-1;while(++s<c)d=u[s],d>=a[0]&&d<=a[1]&&(l=o[d3.bisect(f,d,1,h)-1],l.y+=p,l.push(e[s]))}return o}var t=!0,n=Number,r=ar,i=or;return e.value=function(t){return arguments.length?(n=t,e):n},e.range=function(t){return arguments.length?(r=u(t),e):r},e.bins=function(t){return arguments.length?(i=typeof t=="number"?function(e){return ur(e,t)}:u(t),e):i},e.frequency=function(n){return arguments.length?(t=!!n,e):t},e},d3.layout.hierarchy=function(){function e(t,o,u){var a=i.call(n,t,o),f=uo?t:{data:t};f.depth=o,u.push(f);if(a&&(c=a.length)){var l=-1,c,h=f.children=[],p=0,d=o+1,v;while(++l<c)v=e(a[l],d,u),v.parent=f,h.push(v),p+=v.value;r&&h.sort(r),s&&(f.value=p)}else s&&(f.value=+s.call(n,t,o)||0);return f}function t(e,r){var i=e.children,o=0;if(i&&(a=i.length)){var u=-1,a,f=r+1;while(++u<a)o+=t(i[u],f)}else s&&(o=+s.call(n,uo?e:e.data,r)||0);return s&&(e.value=o),o}function n(t){var n=[];return e(t,0,n),n}var r=hr,i=lr,s=cr;return n.sort=function(e){return arguments.length?(r=e,n):r},n.children=function(e){return arguments.length?(i=e,n):i},n.value=function(e){return arguments.length?(s=e,n):s},n.revalue=function(e){return t(e,0),e},n};var uo=!1;d3.layout.pack=function(){function e(e,i){var s=t.call(this,e,i),o=s[0];o.x=0,o.y=0,Pr(o,function(e){e.r=Math.sqrt(e.value)}),Pr(o,yr);var u=r[0],a=r[1],f=Math.max(2*o.r/u,2*o.r/a);if(n>0){var l=n*f/2;Pr(o,function(e){e.r+=l}),Pr(o,yr),Pr(o,function(e){e.r-=l}),f=Math.max(2*o.r/u,2*o.r/a)}return Er(o,u/2,a/2,1/f),s}var t=d3.layout.hierarchy().sort(dr),n=0,r=[1,1];return e.size=function(t){return arguments.length?(r=t,e):r},e.padding=function(t){return arguments.length?(n=+t,e):n},fr(e,t)},d3.layout.cluster=function(){function e(e,i){var s=t.call(this,e,i),o=s[0],u,a=0,f,l;Pr(o,function(e){var t=e.children;t&&t.length?(e.x=Tr(t),e.y=xr(t)):(e.x=u?a+=n(e,u):0,e.y=0,u=e)});var c=Nr(o),h=Cr(o),p=c.x-n(c,h)/2,d=h.x+n(h,c)/2;return Pr(o,function(e){e.x=(e.x-p)/(d-p)*r[0],e.y=(1-(o.y?e.y/o.y:1))*r[1]}),s}var t=d3.layout.hierarchy().sort(null).value(null),n=kr,r=[1,1];return e.separation=function(t){return arguments.length?(n=t,e):n},e.size=function(t){return arguments.length?(r=t,e):r},fr(e,t)},d3.layout.tree=function(){function e(e,i){function s(e,t){var r=e.children,i=e._tree;if(r&&(o=r.length)){var o,a=r[0],f,l=a,c,h=-1;while(++h<o)c=r[h],s(c,f),l=u(c,f,l),f=c;Hr(e);var p=.5*(a._tree.prelim+c._tree.prelim);t?(i.prelim=t._tree.prelim+n(e,t),i.mod=i.prelim-p):i.prelim=p}else t&&(i.prelim=t._tree.prelim+n(e,t))}function o(e,t){e.x=e._tree.prelim+t;var n=e.children;if(n&&(i=n.length)){var r=-1,i;t+=e._tree.mod;while(++r<i)o(n[r],t)}}function u(e,t,r){if(t){var i=e,s=e,o=t,u=e.parent.children[0],a=i._tree.mod,f=s._tree.mod,l=o._tree.mod,c=u._tree.mod,h;while(o=Ar(o),i=Lr(i),o&&i)u=Lr(u),s=Ar(s),s._tree.ancestor=e,h=o._tree.prelim+l-i._tree.prelim-a+n(o,i),h>0&&(Br(jr(o,e,r),e,h),a+=h,f+=h),l+=o._tree.mod,a+=i._tree.mod,c+=u._tree.mod,f+=s._tree.mod;o&&!Ar(s)&&(s._tree.thread=o,s._tree.mod+=l-f),i&&!Lr(u)&&(u._tree.thread=i,u._tree.mod+=a-c,r=e)}return r}var a=t.call(this,e,i),f=a[0];Pr(f,function(e,t){e._tree={ancestor:e,prelim:0,mod:0,change:0,shift:0,number:t?t._tree.number+1:0}}),s(f),o(f,-f._tree.prelim);var l=Or(f,_r),c=Or(f,Mr),h=Or(f,Dr),p=l.x-n(l,c)/2,d=c.x+n(c,l)/2,v=h.depth||1;return Pr(f,function(e){e.x=(e.x-p)/(d-p)*r[0],e.y=e.depth/v*r[1],delete e._tree}),a}var t=d3.layout.hierarchy().sort(null).value(null),n=kr,r=[1,1];return e.separation=function(t){return arguments.length?(n=t,e):n},e.size=function(t){return arguments.length?(r=t,e):r},fr(e,t)},d3.layout.treemap=function(){function e(e,t){var n=-1,r=e.length,i,s;while(++n<r)s=(i=e[n]).value*(t<0?0:t),i.area=isNaN(s)||s<=0?0:s}function t(n){var s=n.children;if(s&&s.length){var o=l(n),u=[],a=s.slice(),f,c=Infinity,h,p=Math.min(o.dx,o.dy),d;e(a,o.dx*o.dy/n.value),u.area=0;while((d=a.length)>0)u.push(f=a[d-1]),u.area+=f.area,(h=r(u,p))<=c?(a.pop(),c=h):(u.area-=u.pop().area,i(u,p,o,!1),p=Math.min(o.dx,o.dy),u.length=u.area=0,c=Infinity);u.length&&(i(u,p,o,!0),u.length=u.area=0),s.forEach(t)}}function n(t){var r=t.children;if(r&&r.length){var s=l(t),o=r.slice(),u,a=[];e(o,s.dx*s.dy/t.value),a.area=0;while(u=o.pop())a.push(u),a.area+=u.area,u.z!=null&&(i(a,u.z?s.dx:s.dy,s,!o.length),a.length=a.area=0);r.forEach(n)}}function r(e,t){var n=e.area,r,i=0,s=Infinity,o=-1,u=e.length;while(++o<u){if(!(r=e[o].area))continue;r<s&&(s=r),r>i&&(i=r)}return n*=n,t*=t,n?Math.max(t*i*p/n,n/(t*s*p)):Infinity}function i(e,t,n,r){var i=-1,s=e.length,o=n.x,a=n.y,f=t?u(e.area/t):0,l;if(t==n.dx){if(r||f>n.dy)f=n.dy;while(++i<s)l=e[i],l.x=o,l.y=a,l.dy=f,o+=l.dx=Math.min(n.x+n.dx-o,f?u(l.area/f):0);l.z=!0,l.dx+=n.x+n.dx-o,n.y+=f,n.dy-=f}else{if(r||f>n.dx)f=n.dx;while(++i<s)l=e[i],l.x=o,l.y=a,l.dx=f,a+=l.dy=Math.min(n.y+n.dy-a,f?u(l.area/f):0);l.z=!1,l.dy+=n.y+n.dy-a,n.x+=f,n.dx-=f}}function s(r){var i=h||o(r),s=i[0];return s.x=0,s.y=0,s.dx=a[0],s.dy=a[1],h&&o.revalue(s),e([s],s.dx*s.dy/s.value),(h?n:t)(s),c&&(h=i),i}var o=d3.layout.hierarchy(),u=Math.round,a=[1,1],f=null,l=Fr,c=!1,h,p=.5*(1+Math.sqrt(5));return s.size=function(e){return arguments.length?(a=e,s):a},s.padding=function(e){function t(t){var n=e.call(s,t,t.depth);return n==null?Fr(t):Ir(t,typeof n=="number"?[n,n,n,n]:n)}function n(t){return Ir(t,e)}if(!arguments.length)return f;var r;return l=(f=e)==null?Fr:(r=typeof e)==="function"?t:r==="number"?(e=[e,e,e,e],n):n,s},s.round=function(e){return arguments.length?(u=e?Math.round:Number,s):u!=Number},s.sticky=function(e){return arguments.length?(c=e,h=null,s):c},s.ratio=function(e){return arguments.length?(p=e,s):p},fr(s,o)},d3.csv=qr(",","text/csv"),d3.tsv=qr(" ","text/tab-separated-values"),d3.geo={};var ao=Math.PI/180;d3.geo.azimuthal=function(){function e(e){var n=e[0]*ao-s,o=e[1]*ao,f=Math.cos(n),l=Math.sin(n),c=Math.cos(o),h=Math.sin(o),p=t!=="orthographic"?a*h+u*c*f:null,d,v=t==="stereographic"?1/(1+p):t==="gnomonic"?1/p:t==="equidistant"?(d=Math.acos(p),d?d/Math.sin(d):0):t==="equalarea"?Math.sqrt(2/(1+p)):1,m=v*c*l,g=v*(a*c*f-u*h);return[r*m+i[0],r*g+i[1]]}var t="orthographic",n,r=200,i=[480,250],s,o,u,a;return e.invert=function(e){var n=(e[0]-i[0])/r,o=(e[1]-i[1])/r,f=Math.sqrt(n*n+o*o),l=t==="stereographic"?2*Math.atan(f):t==="gnomonic"?Math.atan(f):t==="equidistant"?f:t==="equalarea"?2*Math.asin(.5*f):Math.asin(f),c=Math.sin(l),h=Math.cos(l);return[(s+Math.atan2(n*c,f*u*h+o*a*c))/ao,Math.asin(h*a-(f?o*c*u/f:0))/ao]},e.mode=function(n){return arguments.length?(t=n+"",e):t},e.origin=function(t){return arguments.length?(n=t,s=n[0]*ao,o=n[1]*ao,u=Math.cos(o),a=Math.sin(o),e):n},e.scale=function(t){return arguments.length?(r=+t,e):r},e.translate=function(t){return arguments.length?(i=[+t[0],+t[1]],e):i},e.origin([0,0])},d3.geo.albers=function(){function e(e){var t=u*(ao*e[0]-o),n=Math.sqrt(a-2*u*Math.sin(ao*e[1]))/u;return[i*n*Math.sin(t)+s[0],i*(n*Math.cos(t)-f)+s[1]]}function t(){var t=ao*r[0],i=ao*r[1],s=ao*n[1],l=Math.sin(t),c=Math.cos(t);return o=ao*n[0],u=.5*(l+Math.sin(i)),a=c*c+2*u*l,f=Math.sqrt(a-2*u*Math.sin(s))/u,e}var n=[-98,38],r=[29.5,45.5],i=1e3,s=[480,250],o,u,a,f;return e.invert=function(e){var t=(e[0]-s[0])/i,n=(e[1]-s[1])/i,r=f+n,l=Math.atan2(t,r),c=Math.sqrt(t*t+r*r);return[(o+l/u)/ao,Math.asin((a-c*c*u*u)/(2*u))/ao]},e.origin=function(e){return arguments.length?(n=[+e[0],+e[1]],t()):n},e.parallels=function(e){return arguments.length?(r=[+e[0],+e[1]],t()):r},e.scale=function(t){return arguments.length?(i=+t,e):i},e.translate=function(t){return arguments.length?(s=[+t[0],+t[1]],e):s},t()},d3.geo.albersUsa=function(){function e(e){var s=e[0],o=e[1];return(o>50?n:s<-140?r:o<21?i:t)(e)}var t=d3.geo.albers(),n=d3.geo.albers().origin([-160,60]).parallels([55,65]),r=d3.geo.albers().origin([-160,20]).parallels([8,18]),i=d3.geo.albers().origin([-60,10]).parallels([8,18]);return e.scale=function(s){return arguments.length?(t.scale(s),n.scale(s*.6),r.scale(s),i.scale(s*1.5),e.translate(t.translate())):t.scale()},e.translate=function(s){if(!arguments.length)return t.translate();var o=t.scale()/1e3,u=s[0],a=s[1];return t.translate(s),n.translate([u-400*o,a+170*o]),r.translate([u-190*o,a+200*o]),i.translate([u+580*o,a+430*o]),e},e.scale(t.scale())},d3.geo.bonne=function(){function e(e){var u=e[0]*ao-r,a=e[1]*ao-i;if(s){var f=o+s-a,l=u*Math.cos(a)/f;u=f*Math.sin(l),a=f*Math.cos(l)-o}else u*=Math.cos(a),a*=-1;return[t*u+n[0],t*a+n[1]]}var t=200,n=[480,250],r,i,s,o;return e.invert=function(e){var i=(e[0]-n[0])/t,u=(e[1]-n[1])/t;if(s){var a=o+u,f=Math.sqrt(i*i+a*a);u=o+s-f,i=r+f*Math.atan2(i,a)/Math.cos(u)}else u*=-1,i/=Math.cos(u);return[i/ao,u/ao]},e.parallel=function(t){return arguments.length?(o=1/Math.tan
+(s=t*ao),e):s/ao},e.origin=function(t){return arguments.length?(r=t[0]*ao,i=t[1]*ao,e):[r/ao,i/ao]},e.scale=function(n){return arguments.length?(t=+n,e):t},e.translate=function(t){return arguments.length?(n=[+t[0],+t[1]],e):n},e.origin([0,0]).parallel(45)},d3.geo.equirectangular=function(){function e(e){var r=e[0]/360,i=-e[1]/360;return[t*r+n[0],t*i+n[1]]}var t=500,n=[480,250];return e.invert=function(e){var r=(e[0]-n[0])/t,i=(e[1]-n[1])/t;return[360*r,-360*i]},e.scale=function(n){return arguments.length?(t=+n,e):t},e.translate=function(t){return arguments.length?(n=[+t[0],+t[1]],e):n},e},d3.geo.mercator=function(){function e(e){var r=e[0]/360,i=-(Math.log(Math.tan(Math.PI/4+e[1]*ao/2))/ao)/360;return[t*r+n[0],t*Math.max(-0.5,Math.min(.5,i))+n[1]]}var t=500,n=[480,250];return e.invert=function(e){var r=(e[0]-n[0])/t,i=(e[1]-n[1])/t;return[360*r,2*Math.atan(Math.exp(-360*i*ao))/ao-90]},e.scale=function(n){return arguments.length?(t=+n,e):t},e.translate=function(t){return arguments.length?(n=[+t[0],+t[1]],e):n},e},d3.geo.path=function(){function e(e,t){typeof s=="function"&&(o=Ur(s.apply(this,arguments))),f(e);var n=a.length?a.join(""):null;return a=[],n}function t(e){return u(e).join(",")}function n(e){var t=i(e[0]),n=0,r=e.length;while(++n<r)t-=i(e[n]);return t}function r(e){var t=d3.geom.polygon(e[0].map(u)),n=t.area(),r=t.centroid(n<0?(n*=-1,1):-1),i=r[0],s=r[1],o=n,a=0,f=e.length;while(++a<f)t=d3.geom.polygon(e[a].map(u)),n=t.area(),r=t.centroid(n<0?(n*=-1,1):-1),i-=r[0],s-=r[1],o-=n;return[i,s,6*o]}function i(e){return Math.abs(d3.geom.polygon(e.map(u)).area())}var s=4.5,o=Ur(s),u=d3.geo.albersUsa(),a=[],f=Rr({FeatureCollection:function(e){var t=e.features,n=-1,r=t.length;while(++n<r)a.push(f(t[n].geometry))},Feature:function(e){f(e.geometry)},Point:function(e){a.push("M",t(e.coordinates),o)},MultiPoint:function(e){var n=e.coordinates,r=-1,i=n.length;while(++r<i)a.push("M",t(n[r]),o)},LineString:function(e){var n=e.coordinates,r=-1,i=n.length;a.push("M");while(++r<i)a.push(t(n[r]),"L");a.pop()},MultiLineString:function(e){var n=e.coordinates,r=-1,i=n.length,s,o,u;while(++r<i){s=n[r],o=-1,u=s.length,a.push("M");while(++o<u)a.push(t(s[o]),"L");a.pop()}},Polygon:function(e){var n=e.coordinates,r=-1,i=n.length,s,o,u;while(++r<i){s=n[r],o=-1;if((u=s.length-1)>0){a.push("M");while(++o<u)a.push(t(s[o]),"L");a[a.length-1]="Z"}}},MultiPolygon:function(e){var n=e.coordinates,r=-1,i=n.length,s,o,u,f,l,c;while(++r<i){s=n[r],o=-1,u=s.length;while(++o<u){f=s[o],l=-1;if((c=f.length-1)>0){a.push("M");while(++l<c)a.push(t(f[l]),"L");a[a.length-1]="Z"}}}},GeometryCollection:function(e){var t=e.geometries,n=-1,r=t.length;while(++n<r)a.push(f(t[n]))}}),l=e.area=Rr({FeatureCollection:function(e){var t=0,n=e.features,r=-1,i=n.length;while(++r<i)t+=l(n[r]);return t},Feature:function(e){return l(e.geometry)},Polygon:function(e){return n(e.coordinates)},MultiPolygon:function(e){var t=0,r=e.coordinates,i=-1,s=r.length;while(++i<s)t+=n(r[i]);return t},GeometryCollection:function(e){var t=0,n=e.geometries,r=-1,i=n.length;while(++r<i)t+=l(n[r]);return t}},0),c=e.centroid=Rr({Feature:function(e){return c(e.geometry)},Polygon:function(e){var t=r(e.coordinates);return[t[0]/t[2],t[1]/t[2]]},MultiPolygon:function(e){var t=0,n=e.coordinates,i,s=0,o=0,u=0,a=-1,f=n.length;while(++a<f)i=r(n[a]),s+=i[0],o+=i[1],u+=i[2];return[s/u,o/u]}});return e.projection=function(t){return u=t,e},e.pointRadius=function(t){return typeof t=="function"?s=t:(s=+t,o=Ur(s)),e},e},d3.geo.bounds=function(e){var t=Infinity,n=Infinity,r=-Infinity,i=-Infinity;return zr(e,function(e,s){e<t&&(t=e),e>r&&(r=e),s<n&&(n=s),s>i&&(i=s)}),[[t,n],[r,i]]};var fo={Feature:Wr,FeatureCollection:Xr,GeometryCollection:Vr,LineString:$r,MultiLineString:Jr,MultiPoint:$r,MultiPolygon:Kr,Point:Qr,Polygon:Gr};d3.geo.circle=function(){function e(){}function t(e){return a.distance(e)<u}function n(e){var t=-1,n=e.length,i=[],s,o,f,l,c;while(++t<n)c=a.distance(f=e[t]),c<u?(o&&i.push(ti(o,f)((l-u)/(l-c))),i.push(f),s=o=null):(o=f,!s&&i.length&&(i.push(ti(i[i.length-1],o)((u-l)/(c-l))),s=o)),l=c;return s=e[0],o=i[0],o&&f[0]===s[0]&&f[1]===s[1]&&(f[0]!==o[0]||f[1]!==o[1])&&i.push(o),r(i)}function r(e){var t=0,n=e.length,r,i,s=n?[e[0]]:e,o,u=a.source();while(++t<n){o=a.source(e[t-1])(e[t]).coordinates;for(r=0,i=o.length;++r<i;)s.push(o[r])}return a.source(u),s}var s=[0,0],o=89.99,u=o*ao,a=d3.geo.greatArc().source(s).target(i);e.clip=function(e){return typeof s=="function"&&a.source(s.apply(this,arguments)),f(e)||null};var f=Rr({FeatureCollection:function(e){var t=e.features.map(f).filter(i);return t&&(e=Object.create(e),e.features=t,e)},Feature:function(e){var t=f(e.geometry);return t&&(e=Object.create(e),e.geometry=t,e)},Point:function(e){return t(e.coordinates)&&e},MultiPoint:function(e){var n=e.coordinates.filter(t);return n.length&&{type:e.type,coordinates:n}},LineString:function(e){var t=n(e.coordinates);return t.length&&(e=Object.create(e),e.coordinates=t,e)},MultiLineString:function(e){var t=e.coordinates.map(n).filter(function(e){return e.length});return t.length&&(e=Object.create(e),e.coordinates=t,e)},Polygon:function(e){var t=e.coordinates.map(n);return t[0].length&&(e=Object.create(e),e.coordinates=t,e)},MultiPolygon:function(e){var t=e.coordinates.map(function(e){return e.map(n)}).filter(function(e){return e[0].length});return t.length&&(e=Object.create(e),e.coordinates=t,e)},GeometryCollection:function(e){var t=e.geometries.map(f).filter(i);return t.length&&(e=Object.create(e),e.geometries=t,e)}});return e.origin=function(t){return arguments.length?(s=t,typeof s!="function"&&a.source(s),e):s},e.angle=function(t){return arguments.length?(u=(o=+t)*ao,e):o},d3.rebind(e,a,"precision")},d3.geo.greatArc=function(){function e(){var t=e.distance.apply(this,arguments),r=0,u=s/t,a=[n];while((r+=u)<1)a.push(o(r));return a.push(i),{type:"LineString",coordinates:a}}var t=Yr,n,r=Zr,i,s=6*ao,o=ei();return e.distance=function(){return typeof t=="function"&&o.source(n=t.apply(this,arguments)),typeof r=="function"&&o.target(i=r.apply(this,arguments)),o.distance()},e.source=function(r){return arguments.length?(t=r,typeof t!="function"&&o.source(n=t),e):t},e.target=function(t){return arguments.length?(r=t,typeof r!="function"&&o.target(i=r),e):r},e.precision=function(t){return arguments.length?(s=t*ao,e):s/ao},e},d3.geo.greatCircle=d3.geo.circle,d3.geom={},d3.geom.contour=function(e,t){var n=t||ni(e),r=[],i=n[0],s=n[1],o=0,u=0,a=NaN,f=NaN,l=0;do l=0,e(i-1,s-1)&&(l+=1),e(i,s-1)&&(l+=2),e(i-1,s)&&(l+=4),e(i,s)&&(l+=8),l===6?(o=f===-1?-1:1,u=0):l===9?(o=0,u=a===1?-1:1):(o=lo[l],u=co[l]),o!=a&&u!=f&&(r.push([i,s]),a=o,f=u),i+=o,s+=u;while(n[0]!=i||n[1]!=s);return r};var lo=[1,0,1,1,-1,0,-1,1,0,0,0,0,-1,0,-1,NaN],co=[0,-1,0,0,0,-1,0,0,1,-1,1,1,0,-1,0,NaN];d3.geom.hull=function(e){if(e.length<3)return[];var t=e.length,n=t-1,r=[],i=[],s,o,u=0,a,f,l,c,h,p,d,v;for(s=1;s<t;++s)e[s][1]<e[u][1]?u=s:e[s][1]==e[u][1]&&(u=e[s][0]<e[u][0]?s:u);for(s=0;s<t;++s){if(s===u)continue;f=e[s][1]-e[u][1],a=e[s][0]-e[u][0],r.push({angle:Math.atan2(f,a),index:s})}r.sort(function(e,t){return e.angle-t.angle}),d=r[0].angle,p=r[0].index,h=0;for(s=1;s<n;++s)o=r[s].index,d==r[s].angle?(a=e[p][0]-e[u][0],f=e[p][1]-e[u][1],l=e[o][0]-e[u][0],c=e[o][1]-e[u][1],a*a+f*f>=l*l+c*c?r[s].index=-1:(r[h].index=-1,d=r[s].angle,h=s,p=o)):(d=r[s].angle,h=s,p=o);i.push(u);for(s=0,o=0;s<2;++o)r[o].index!==-1&&(i.push(r[o].index),s++);v=i.length;for(;o<n;++o){if(r[o].index===-1)continue;while(!ri(i[v-2],i[v-1],r[o].index,e))--v;i[v++]=r[o].index}var m=[];for(s=0;s<v;++s)m.push(e[i[s]]);return m},d3.geom.polygon=function(e){return e.area=function(){var t=0,n=e.length,r=e[n-1][0]*e[0][1],i=e[n-1][1]*e[0][0];while(++t<n)r+=e[t-1][0]*e[t][1],i+=e[t-1][1]*e[t][0];return(i-r)*.5},e.centroid=function(t){var n=-1,r=e.length,i=0,s=0,o,u=e[r-1],a;arguments.length||(t=-1/(6*e.area()));while(++n<r)o=u,u=e[n],a=o[0]*u[1]-u[0]*o[1],i+=(o[0]+u[0])*a,s+=(o[1]+u[1])*a;return[i*t,s*t]},e.clip=function(t){var n,r=-1,i=e.length,s,o,u=e[i-1],a,f,l;while(++r<i){n=t.slice(),t.length=0,a=e[r],f=n[(o=n.length)-1],s=-1;while(++s<o)l=n[s],ii(l,u,a)?(ii(f,u,a)||t.push(si(f,l,u,a)),t.push(l)):ii(f,u,a)&&t.push(si(f,l,u,a)),f=l;u=a}return t},e},d3.geom.voronoi=function(e){var t=e.map(function(){return[]});return oi(e,function(e){var n,r,i,s,o,u;e.a===1&&e.b>=0?(n=e.ep.r,r=e.ep.l):(n=e.ep.l,r=e.ep.r),e.a===1?(o=n?n.y:-1e6,i=e.c-e.b*o,u=r?r.y:1e6,s=e.c-e.b*u):(i=n?n.x:-1e6,o=e.c-e.a*i,s=r?r.x:1e6,u=e.c-e.a*s);var a=[i,o],f=[s,u];t[e.region.l.index].push(a,f),t[e.region.r.index].push(a,f)}),t.map(function(t,n){var r=e[n][0],i=e[n][1];return t.forEach(function(e){e.angle=Math.atan2(e[0]-r,e[1]-i)}),t.sort(function(e,t){return e.angle-t.angle}).filter(function(e,n){return!n||e.angle-t[n-1].angle>1e-10})})};var ho={l:"r",r:"l"};d3.geom.delaunay=function(e){var t=e.map(function(){return[]}),n=[];return oi(e,function(n){t[n.region.l.index].push(e[n.region.r.index])}),t.forEach(function(t,r){var i=e[r],s=i[0],o=i[1];t.forEach(function(e){e.angle=Math.atan2(e[0]-s,e[1]-o)}),t.sort(function(e,t){return e.angle-t.angle});for(var u=0,a=t.length-1;u<a;u++)n.push([i,t[u],t[u+1]])}),n},d3.geom.quadtree=function(e,t,n,r,i){function s(e,t,n,r,i,s){if(isNaN(t.x)||isNaN(t.y))return;if(e.leaf){var u=e.point;u?Math.abs(u.x-t.x)+Math.abs(u.y-t.y)<.01?o(e,t,n,r,i,s):(e.point=null,o(e,u,n,r,i,s),o(e,t,n,r,i,s)):e.point=t}else o(e,t,n,r,i,s)}function o(e,t,n,r,i,o){var u=(n+i)*.5,a=(r+o)*.5,f=t.x>=u,l=t.y>=a,c=(l<<1)+f;e.leaf=!1,e=e.nodes[c]||(e.nodes[c]=ui()),f?n=u:i=u,l?r=a:o=a,s(e,t,n,r,i,o)}var u,a=-1,f=e.length;f&&isNaN(e[0].x)&&(e=e.map(fi));if(arguments.length<5)if(arguments.length===3)i=r=n,n=t;else{t=n=Infinity,r=i=-Infinity;while(++a<f)u=e[a],u.x<t&&(t=u.x),u.y<n&&(n=u.y),u.x>r&&(r=u.x),u.y>i&&(i=u.y);var l=r-t,c=i-n;l>c?i=n+l:r=t+c}var h=ui();return h.add=function(e){s(h,e,t,n,r,i)},h.visit=function(e){ai(e,h,t,n,r,i)},e.forEach(h.add),h},d3.time={};var po=Date,vo=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];li.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){mo.setUTCDate.apply(this._,arguments)},setDay:function(){mo.setUTCDay.apply(this._,arguments)},setFullYear:function(){mo.setUTCFullYear.apply(this._,arguments)},setHours:function(){mo.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){mo.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){mo.setUTCMinutes.apply(this._,arguments)},setMonth:function(){mo.setUTCMonth.apply(this._,arguments)},setSeconds:function(){mo.setUTCSeconds.apply(this._,arguments)},setTime:function(){mo.setTime.apply(this._,arguments)}};var mo=Date.prototype,go="%a %b %e %H:%M:%S %Y",yo="%m/%d/%y",bo="%H:%M:%S",wo=vo,Eo=wo.map(ci),So=["January","February","March","April","May","June","July","August","September","October","November","December"],xo=So.map(ci);d3.time.format=function(e){function t(t){var r=[],i=-1,s=0,o,u;while(++i<n)e.charCodeAt(i)==37&&(r.push(e.substring(s,i),(u=Po[o=e.charAt(++i)])?u(t):o),s=i+1);return r.push(e.substring(s,i)),r.join("")}var n=e.length;return t.parse=function(t){var n={y:1900,m:0,d:1,H:0,M:0,S:0,L:0},r=hi(n,e,t,0);if(r!=t.length)return null;"p"in n&&(n.H=n.H%12+n.p*12);var i=new po;return i.setFullYear(n.y,n.m,n.d),i.setHours(n.H,n.M,n.S,n.L),i},t.toString=function(){return e},t};var To=d3.format("02d"),No=d3.format("03d"),Co=d3.format("04d"),ko=d3.format("2d"),Lo=pi(wo),Ao=pi(Eo),Oo=pi(So),Mo=di(So),_o=pi(xo),Do=di(xo),Po={a:function(e){return Eo[e.getDay()]},A:function(e){return wo[e.getDay()]},b:function(e){return xo[e.getMonth()]},B:function(e){return So[e.getMonth()]},c:d3.time.format(go),d:function(e){return To(e.getDate())},e:function(e){return ko(e.getDate())},H:function(e){return To(e.getHours())},I:function(e){return To(e.getHours()%12||12)},j:function(e){return No(1+d3.time.dayOfYear(e))},L:function(e){return No(e.getMilliseconds())},m:function(e){return To(e.getMonth()+1)},M:function(e){return To(e.getMinutes())},p:function(e){return e.getHours()>=12?"PM":"AM"},S:function(e){return To(e.getSeconds())},U:function(e){return To(d3.time.sundayOfYear(e))},w:function(e){return e.getDay()},W:function(e){return To(d3.time.mondayOfYear(e))},x:d3.time.format(yo),X:d3.time.format(bo),y:function(e){return To(e.getFullYear()%100)},Y:function(e){return Co(e.getFullYear()%1e4)},Z:_i,"%":function(e){return"%"}},Ho={a:vi,A:mi,b:gi,B:yi,c:bi,d:Ci,e:Ci,H:ki,I:ki,L:Oi,m:Ni,M:Li,p:Mi,S:Ai,x:wi,X:Ei,y:xi,Y:Si},Bo=/^\s*\d+/,jo=d3.map({am:0,pm:1});d3.time.format.utc=function(e){function t(e){try{po=li;var t=new po;return t._=e,n(t)}finally{po=Date}}var n=d3.time.format(e);return t.parse=function(e){try{po=li;var t=n.parse(e);return t&&t._}finally{po=Date}},t.toString=n.toString,t};var Fo=d3.time.format.utc("%Y-%m-%dT%H:%M:%S.%LZ");d3.time.format.iso=Date.prototype.toISOString?Di:Fo,Di.parse=function(e){var t=new Date(e);return isNaN(t)?null:t},Di.toString=Fo.toString,d3.time.second=Pi(function(e){return new po(Math.floor(e/1e3)*1e3)},function(e,t){e.setTime(e.getTime()+Math.floor(t)*1e3)},function(e){return e.getSeconds()}),d3.time.seconds=d3.time.second.range,d3.time.seconds.utc=d3.time.second.utc.range,d3.time.minute=Pi(function(e){return new po(Math.floor(e/6e4)*6e4)},function(e,t){e.setTime(e.getTime()+Math.floor(t)*6e4)},function(e){return e.getMinutes()}),d3.time.minutes=d3.time.minute.range,d3.time.minutes.utc=d3.time.minute.utc.range,d3.time.hour=Pi(function(e){var t=e.getTimezoneOffset()/60;return new po((Math.floor(e/36e5-t)+t)*36e5)},function(e,t){e.setTime(e.getTime()+Math.floor(t)*36e5)},function(e){return e.getHours()}),d3.time.hours=d3.time.hour.range,d3.time.hours.utc=d3.time.hour.utc.range,d3.time.day=Pi(function(e){var t=new po(1970,0);return t.setFullYear(e.getFullYear(),e.getMonth(),e.getDate()),t},function(e,t){e.setDate(e.getDate()+t)},function(e){return e.getDate()-1}),d3.time.days=d3.time.day.range,d3.time.days.utc=d3.time.day.utc.range,d3.time.dayOfYear=function(e){var t=d3.time.year(e);return Math.floor((e-t-(e.getTimezoneOffset()-t.getTimezoneOffset())*6e4)/864e5)},vo.forEach(function(e,t){e=e.toLowerCase(),t=7-t;var n=d3.time[e]=Pi(function(e){return(e=d3.time.day(e)).setDate(e.getDate()-(e.getDay()+t)%7),e},function(e,t){e.setDate(e.getDate()+Math.floor(t)*7)},function(e){var n=d3.time.year(e).getDay();return Math.floor((d3.time.dayOfYear(e)+(n+t)%7)/7)-(n!==t)});d3.time[e+"s"]=n.range,d3.time[e+"s"].utc=n.utc.range,d3.time[e+"OfYear"]=function(e){var n=d3.time.year(e).getDay();return Math.floor((d3.time.dayOfYear(e)+(n+t)%7)/7)}}),d3.time.week=d3.time.sunday,d3.time.weeks=d3.time.sunday.range,d3.time.weeks.utc=d3.time.sunday.utc.range,d3.time.weekOfYear=d3.time.sundayOfYear,d3.time.month=Pi(function(e){return e=d3.time.day(e),e.setDate(1),e},function(e,t){e.setMonth(e.getMonth()+t)},function(e){return e.getMonth()}),d3.time.months=d3.time.month.range,d3.time.months.utc=d3.time.month.utc.range,d3.time.year=Pi(function(e){return e=d3.time.day(e),e.setMonth(0,1),e},function(e,t){e.setFullYear(e.getFullYear()+t)},function(e){return e.getFullYear()}),d3.time.years=d3.time.year.range,d3.time.years.utc=d3.time.year.utc.range;var Io=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],qo=[[d3.time.second,1],[d3.time.second,5],[d3.time.second,15],[d3.time.second,30],[d3.time.minute,1],[d3.time.minute,5],[d3.time.minute,15],[d3.time.minute,30],[d3.time.hour,1],[d3.time.hour,3],[d3.time.hour,6],[d3.time.hour,12],[d3.time.day,1],[d3.time.day,2],[d3.time.week,1],[d3.time.month,1],[d3.time.month,3],[d3.time.year,1]],Ro=[[d3.time.format("%Y"),function(e){return!0}],[d3.time.format("%B"),function(e){return e.getMonth()}],[d3.time.format("%b %d"),function(e){return e.getDate()!=1}],[d3.time.format("%a %d"),function(e){return e.getDay()&&e.getDate()!=1}],[d3.time.format("%I %p"),function(e){return e.getHours()}],[d3.time.format("%I:%M"),function(e){return e.getMinutes()}],[d3.time.format(":%S"),function(e){return e.getSeconds()}],[d3.time.format(".%L"),function(e){return e.getMilliseconds()}]],Uo=d3.scale.linear(),zo=Ii(Ro);qo.year=function(e,t){return Uo.domain(e.map(Ri)).ticks(t).map(qi)},d3.time.scale=function(){return Bi(d3.scale.linear(),qo,zo)};var Wo=qo.map(function(e){return[e[0].utc,e[1]]}),Xo=[[d3.time.format.utc("%Y"),function(e){return!0}],[d3.time.format.utc("%B"),function(e){return e.getUTCMonth()}],[d3.time.format.utc("%b %d"),function(e){return e.getUTCDate()!=1}],[d3.time.format.utc("%a %d"),function(e){return e.getUTCDay()&&e.getUTCDate()!=1}],[d3.time.format.utc("%I %p"),function(e){return e.getUTCHours()}],[d3.time.format.utc("%I:%M"),function(e){return e.getUTCMinutes()}],[d3.time.format.utc(":%S"),function(e){return e.getUTCSeconds()}],[d3.time.format.utc(".%L"),function(e){return e.getUTCMilliseconds()}]],Vo=Ii(Xo);Wo.year=function(e,t){return Uo.domain(e.map(zi)).ticks(t).map(Ui)},d3.time.scale.utc=function(){return Bi(d3.scale.linear(),Wo,Vo)}})(); \ No newline at end of file
diff --git a/hyperkitty/static/libs/protovis-d3.1.js b/hyperkitty/static/libs/protovis-d3.1.js
deleted file mode 100644
index af56eac..0000000
--- a/hyperkitty/static/libs/protovis-d3.1.js
+++ /dev/null
@@ -1,7725 +0,0 @@
-/**
- * @class The built-in Array class.
- * @name Array
- */
-
-if (!Array.prototype.map) {
- /**
- * Creates a new array with the results of calling a provided function on
- * every element in this array. Implemented in Javascript 1.6.
- *
- * @see <a
- * href="https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Array/Map">map</a>
- * documentation.
- * @param {function} f function that produces an element of the new Array from
- * an element of the current one.
- * @param [o] object to use as <tt>this</tt> when executing <tt>f</tt>.
- */
- Array.prototype.map = function(f, o) {
- var n = this.length;
- var result = new Array(n);
- for (var i = 0; i < n; i++) {
- if (i in this) {
- result[i] = f.call(o, this[i], i, this);
- }
- }
- return result;
- };
-}
-
-if (!Array.prototype.filter) {
- /**
- * Creates a new array with all elements that pass the test implemented by the
- * provided function. Implemented in Javascript 1.6.
- *
- * @see <a
- * href="https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Array/filter">filter</a>
- * documentation.
- * @param {function} f function to test each element of the array.
- * @param [o] object to use as <tt>this</tt> when executing <tt>f</tt>.
- */
- Array.prototype.filter = function(f, o) {
- var n = this.length;
- var result = new Array();
- for (var i = 0; i < n; i++) {
- if (i in this) {
- var v = this[i];
- if (f.call(o, v, i, this)) result.push(v);
- }
- }
- return result;
- };
-}
-
-if (!Array.prototype.forEach) {
- /**
- * Executes a provided function once per array element. Implemented in
- * Javascript 1.6.
- *
- * @see <a
- * href="https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Array/ForEach">forEach</a>
- * documentation.
- * @param {function} f function to execute for each element.
- * @param [o] object to use as <tt>this</tt> when executing <tt>f</tt>.
- */
- Array.prototype.forEach = function(f, o) {
- var n = this.length >>> 0;
- for (var i = 0; i < n; i++) {
- if (i in this) f.call(o, this[i], i, this);
- }
- };
-}
-
-if (!Array.prototype.reduce) {
- /**
- * Apply a function against an accumulator and each value of the array (from
- * left-to-right) as to reduce it to a single value. Implemented in Javascript
- * 1.8.
- *
- * @see <a
- * href="https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Array/Reduce">reduce</a>
- * documentation.
- * @param {function} f function to execute on each value in the array.
- * @param [v] object to use as the first argument to the first call of
- * <tt>t</tt>.
- */
- Array.prototype.reduce = function(f, v) {
- var len = this.length;
- if (!len && (arguments.length == 1)) {
- throw new Error("reduce: empty array, no initial value");
- }
-
- var i = 0;
- if (arguments.length < 2) {
- while (true) {
- if (i in this) {
- v = this[i++];
- break;
- }
- if (++i >= len) {
- throw new Error("reduce: no values, no initial value");
- }
- }
- }
-
- for (; i < len; i++) {
- if (i in this) {
- v = f(v, this[i], i, this);
- }
- }
- return v;
- };
-}
-/**
- * @class The built-in Date class.
- * @name Date
- */
-
-Date.__parse__ = Date.parse;
-
-/**
- * Parses a date from a string, optionally using the specified formatting. If
- * only a single argument is specified (i.e., <tt>format</tt> is not specified),
- * this method invokes the native implementation to guarantee
- * backwards-compatibility.
- *
- * <p>The format string is in the same format expected by the <tt>strptime</tt>
- * function in C. The following conversion specifications are supported:<ul>
- *
- * <li>%b - abbreviated month names.</li>
- * <li>%B - full month names.</li>
- * <li>%h - same as %b.</li>
- * <li>%d - day of month [1,31].</li>
- * <li>%e - same as %d.</li>
- * <li>%H - hour (24-hour clock) [0,23].</li>
- * <li>%m - month number [1,12].</li>
- * <li>%M - minute [0,59].</li>
- * <li>%S - second [0,61].</li>
- * <li>%y - year with century [0,99].</li>
- * <li>%Y - year including century.</li>
- * <li>%% - %.</li>
- *
- * </ul>The following conversion specifications are <i>unsupported</i> (for now):<ul>
- *
- * <li>%a - day of week, either abbreviated or full name.</li>
- * <li>%A - same as %a.</li>
- * <li>%c - locale's appropriate date and time.</li>
- * <li>%C - century number.</li>
- * <li>%D - same as %m/%d/%y.</li>
- * <li>%I - hour (12-hour clock) [1,12].</li>
- * <li>%j - day number [1,366].</li>
- * <li>%n - any white space.</li>
- * <li>%p - locale's equivalent of a.m. or p.m.</li>
- * <li>%r - same as %I:%M:%S %p.</li>
- * <li>%R - same as %H:%M.</li>
- * <li>%t - same as %n.</li>
- * <li>%T - same as %H:%M:%S.</li>
- * <li>%U - week number [0,53].</li>
- * <li>%w - weekday [0,6].</li>
- * <li>%W - week number [0,53].</li>
- * <li>%x - locale's equivalent to %m/%d/%y.</li>
- * <li>%X - locale's equivalent to %I:%M:%S %p.</li>
- *
- * </ul>
- *
- * @see <a
- * href="http://www.opengroup.org/onlinepubs/007908799/xsh/strptime.html">strptime</a>
- * documentation.
- * @param {string} s the string to parse as a date.
- * @param {string} [format] an optional format string.
- * @returns {Date} the parsed date.
- */
-Date.parse = function(s, format) {
- if (arguments.length == 1) {
- return Date.__parse__(s);
- }
-
- var year = 1970, month = 0, date = 1, hour = 0, minute = 0, second = 0;
- var fields = [function() {}];
- format = format.replace(/[\\\^\$\*\+\?\[\]\(\)\.\{\}]/g, "\\$&");
- format = format.replace(/%[a-zA-Z0-9]/g, function(s) {
- switch (s) {
- // TODO %a: day of week, either abbreviated or full name
- // TODO %A: same as %a
- case '%b': {
- fields.push(function(x) { month = {
- Jan: 0, Feb: 1, Mar: 2, Apr: 3, May: 4, Jun: 5, Jul: 6, Aug: 7,
- Sep: 8, Oct: 9, Nov: 10, Dec: 11
- }[x]; });
- return "([A-Za-z]+)";
- }
- case '%h':
- case '%B': {
- fields.push(function(x) { month = {
- January: 0, February: 1, March: 2, April: 3, May: 4, June: 5,
- July: 6, August: 7, September: 8, October: 9, November: 10,
- December: 11
- }[x]; });
- return "([A-Za-z]+)";
- }
- // TODO %c: locale's appropriate date and time
- // TODO %C: century number[0,99]
- case '%e':
- case '%d': {
- fields.push(function(x) { date = x; });
- return "([0-9]+)";
- }
- // TODO %D: same as %m/%d/%y
- case '%H': {
- fields.push(function(x) { hour = x; });
- return "([0-9]+)";
- }
- // TODO %I: hour (12-hour clock) [1,12]
- // TODO %j: day number [1,366]
- case '%m': {
- fields.push(function(x) { month = x - 1; });
- return "([0-9]+)";
- }
- case '%M': {
- fields.push(function(x) { minute = x; });
- return "([0-9]+)";
- }
- // TODO %n: any white space
- // TODO %p: locale's equivalent of a.m. or p.m.
- // TODO %r: %I:%M:%S %p
- // TODO %R: %H:%M
- case '%S': {
- fields.push(function(x) { second = x; });
- return "([0-9]+)";
- }
- // TODO %t: any white space
- // TODO %T: %H:%M:%S
- // TODO %U: week number [00,53]
- // TODO %w: weekday [0,6]
- // TODO %W: week number [00, 53]
- // TODO %x: locale date (%m/%d/%y)
- // TODO %X: locale time (%I:%M:%S %p)
- case '%y': {
- fields.push(function(x) {
- x = Number(x);
- year = x + (((0 <= x) && (x < 69)) ? 2000
- : (((x >= 69) && (x < 100) ? 1900 : 0)));
- });
- return "([0-9]+)";
- }
- case '%Y': {
- fields.push(function(x) { year = x; });
- return "([0-9]+)";
- }
- case '%%': {
- fields.push(function() {});
- return "%";
- }
- }
- return s;
- });
-
- var match = s.match(format);
- if (match) match.forEach(function(m, i) { fields[i](m); });
- return new Date(year, month, date, hour, minute, second);
-};
-
-if (Date.prototype.toLocaleFormat) {
- Date.prototype.format = Date.prototype.toLocaleFormat;
-} else {
-
-/**
- * Converts a date to a string using the specified formatting. If the
- * <tt>Date</tt> object already supports the <tt>toLocaleFormat</tt> method, as
- * in Firefox, this is simply an alias to the built-in method.
- *
- * <p>The format string is in the same format expected by the <tt>strftime</tt>
- * function in C. The following conversion specifications are supported:<ul>
- *
- * <li>%a - abbreviated weekday name.</li>
- * <li>%A - full weekday name.</li>
- * <li>%b - abbreviated month names.</li>
- * <li>%B - full month names.</li>
- * <li>%c - locale's appropriate date and time.</li>
- * <li>%C - century number.</li>
- * <li>%d - day of month [01,31] (zero padded).</li>
- * <li>%D - same as %m/%d/%y.</li>
- * <li>%e - day of month [ 1,31] (space padded).</li>
- * <li>%h - same as %b.</li>
- * <li>%H - hour (24-hour clock) [00,23] (zero padded).</li>
- * <li>%I - hour (12-hour clock) [01,12] (zero padded).</li>
- * <li>%m - month number [01,12] (zero padded).</li>
- * <li>%M - minute [0,59] (zero padded).</li>
- * <li>%n - newline character.</li>
- * <li>%p - locale's equivalent of a.m. or p.m.</li>
- * <li>%r - same as %I:%M:%S %p.</li>
- * <li>%R - same as %H:%M.</li>
- * <li>%S - second [00,61] (zero padded).</li>
- * <li>%t - tab character.</li>
- * <li>%T - same as %H:%M:%S.</li>
- * <li>%x - same as %m/%d/%y.</li>
- * <li>%X - same as %I:%M:%S %p.</li>
- * <li>%y - year with century [00,99] (zero padded).</li>
- * <li>%Y - year including century.</li>
- * <li>%% - %.</li>
- *
- * </ul>The following conversion specifications are <i>unsupported</i> (for now):<ul>
- *
- * <li>%j - day number [1,366].</li>
- * <li>%u - weekday number [1,7].</li>
- * <li>%U - week number [00,53].</li>
- * <li>%V - week number [01,53].</li>
- * <li>%w - weekday number [0,6].</li>
- * <li>%W - week number [00,53].</li>
- * <li>%Z - timezone name or abbreviation.</li>
- *
- * </ul>
- *
- * @see <a
- * href="http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/toLocaleFormat">Date.toLocaleFormat</a>
- * documentation.
- * @see <a
- * href="http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html">strftime</a>
- * documentation.
- * @param {string} format a format string.
- * @returns {string} the formatted date.
- */
-Date.prototype.format = function(format) {
- function pad(n, p) { return (n < 10) ? (p || "0") + n : n; }
- var d = this;
- return format.replace(/%[a-zA-Z0-9]/g, function(s) {
- switch (s) {
- case '%a': return [
- "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
- ][d.getDay()];
- case '%A': return [
- "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
- "Saturday"
- ][d.getDay()];
- case '%h':
- case '%b': return [
- "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
- "Oct", "Nov", "Dec"
- ][d.getMonth()];
- case '%B': return [
- "January", "February", "March", "April", "May", "June", "July",
- "August", "September", "October", "November", "December"
- ][d.getMonth()];
- case '%c': return d.toLocaleString();
- case '%C': return pad(Math.floor(d.getFullYear() / 100) % 100);
- case '%d': return pad(d.getDate());
- case '%x':
- case '%D': return pad(d.getMonth() + 1)
- + "/" + pad(d.getDate())
- + "/" + pad(d.getFullYear() % 100);
- case '%e': return pad(d.getDate(), " ");
- case '%H': return pad(d.getHours());
- case '%I': {
- var h = d.getHours() % 12;
- return h ? pad(h) : 12;
- }
- // TODO %j: day of year as a decimal number [001,366]
- case '%m': return pad(d.getMonth() + 1);
- case '%M': return pad(d.getMinutes());
- case '%n': return "\n";
- case '%p': return d.getHours() < 12 ? "AM" : "PM";
- case '%T':
- case '%X':
- case '%r': {
- var h = d.getHours() % 12;
- return (h ? pad(h) : 12)
- + ":" + pad(d.getMinutes())
- + ":" + pad(d.getSeconds())
- + " " + (d.getHours() < 12 ? "AM" : "PM");
- }
- case '%R': return pad(d.getHours()) + ":" + pad(d.getMinutes());
- case '%S': return pad(d.getSeconds());
- case '%t': return "\t";
- case '%u': {
- var w = d.getDay();
- return w ? w : 1;
- }
- // TODO %U: week number (sunday first day) [00,53]
- // TODO %V: week number (monday first day) [01,53] ... with weirdness
- case '%w': return d.getDay();
- // TODO %W: week number (monday first day) [00,53] ... with weirdness
- case '%y': return pad(d.getFullYear() % 100);
- case '%Y': return d.getFullYear();
- // TODO %Z: timezone name or abbreviation
- case '%%': return "%";
- }
- return s;
- });
- };
-}
-var pv = function() {/**
- * The top-level Protovis namespace. All public methods and fields should be
- * registered on this object. Note that core Protovis source is surrounded by an
- * anonymous function, so any other declared globals will not be visible outside
- * of core methods. This also allows multiple versions of Protovis to coexist,
- * since each version will see their own <tt>pv</tt> namespace.
- *
- * @namespace The top-level Protovis namespace, <tt>pv</tt>.
- */
-var pv = {};
-
-/**
- * @private Returns a prototype object suitable for extending the given class
- * <tt>f</tt>. Rather than constructing a new instance of <tt>f</tt> to serve as
- * the prototype (which unnecessarily runs the constructor on the created
- * prototype object, potentially polluting it), an anonymous function is
- * generated internally that shares the same prototype:
- *
- * <pre>function g() {}
- * g.prototype = f.prototype;
- * return new g();</pre>
- *
- * For more details, see Douglas Crockford's essay on prototypal inheritance.
- *
- * @param {function} f a constructor.
- * @returns a suitable prototype object.
- * @see Douglas Crockford's essay on <a
- * href="http://javascript.crockford.com/prototypal.html">prototypal
- * inheritance</a>.
- */
-pv.extend = function(f) {
- function g() {}
- g.prototype = f.prototype || f;
- return new g();
-};
-
-try {
- eval("pv.parse = function(x) x;"); // native support
-} catch (e) {
-
-/**
- * @private Parses a Protovis specification, which may use JavaScript 1.8
- * function expresses, replacing those function expressions with proper
- * functions such that the code can be run by a JavaScript 1.6 interpreter. This
- * hack only supports function expressions (using clumsy regular expressions, no
- * less), and not other JavaScript 1.8 features such as let expressions.
- *
- * @param {string} s a Protovis specification (i.e., a string of JavaScript 1.8
- * source code).
- * @returns {string} a conformant JavaScript 1.6 source code.
- */
- pv.parse = function(js) { // hacky regex support
- var re = new RegExp("function(\\s+\\w+)?\\([^)]*\\)\\s*", "mg"), m, d, i = 0, s = "";
- while (m = re.exec(js)) {
- var j = m.index + m[0].length;
- if (js.charAt(j--) != '{') {
- s += js.substring(i, j) + "{return ";
- i = j;
- for (var p = 0; p >= 0 && j < js.length; j++) {
- var c = js.charAt(j);
- switch (c) {
- case '"': case '\'': {
- while (++j < js.length && (d = js.charAt(j)) != c) {
- if (d == '\\') j++;
- }
- break;
- }
- case '[': case '(': p++; break;
- case ']': case ')': p--; break;
- case ';':
- case ',': if (p == 0) p--; break;
- }
- }
- s += pv.parse(js.substring(i, --j)) + ";}";
- i = j;
- }
- re.lastIndex = j;
- }
- s += js.substring(i);
- return s;
- };
-}
-
-/**
- * Returns the passed-in argument, <tt>x</tt>; the identity function. This method
- * is provided for convenience since it is used as the default behavior for a
- * number of property functions.
- *
- * @param x a value.
- * @returns the value <tt>x</tt>.
- */
-pv.identity = function(x) { return x; };
-
-/**
- * Returns <tt>this.index</tt>. This method is provided for convenience for use
- * with scales. For example, to color bars by their index, say:
- *
- * <pre>.fillStyle(pv.Colors.category10().by(pv.index))</pre>
- *
- * This method is equivalent to <tt>function() this.index</tt>, but more
- * succinct. Note that the <tt>index</tt> property is also supported for
- * accessor functions with {@link pv.max}, {@link pv.min} and other array
- * utility methods.
- *
- * @see pv.Scale
- * @see pv.Mark#index
- */
-pv.index = function() { return this.index; };
-
-/**
- * Returns <tt>this.childIndex</tt>. This method is provided for convenience for
- * use with scales. For example, to color bars by their child index, say:
- *
- * <pre>.fillStyle(pv.Colors.category10().by(pv.child))</pre>
- *
- * This method is equivalent to <tt>function() this.childIndex</tt>, but more
- * succinct.
- *
- * @see pv.Scale
- * @see pv.Mark#childIndex
- */
-pv.child = function() { return this.childIndex; };
-
-/**
- * Returns <tt>this.parent.index</tt>. This method is provided for convenience
- * for use with scales. This method is provided for convenience for use with
- * scales. For example, to color bars by their parent index, say:
- *
- * <pre>.fillStyle(pv.Colors.category10().by(pv.parent))</pre>
- *
- * Tthis method is equivalent to <tt>function() this.parent.index</tt>, but more
- * succinct.
- *
- * @see pv.Scale
- * @see pv.Mark#index
- */
-pv.parent = function() { return this.parent.index; };
-
-/**
- * Returns an array of numbers, starting at <tt>start</tt>, incrementing by
- * <tt>step</tt>, until <tt>stop</tt> is reached. The stop value is exclusive. If
- * only a single argument is specified, this value is interpeted as the
- * <i>stop</i> value, with the <i>start</i> value as zero. If only two arguments
- * are specified, the step value is implied to be one.
- *
- * <p>The method is modeled after the built-in <tt>range</tt> method from
- * Python. See the Python documentation for more details.
- *
- * @see <a href="http://docs.python.org/library/functions.html#range">Python range</a>
- * @param {number} [start] the start value.
- * @param {number} stop the stop value.
- * @param {number} [step] the step value.
- * @returns {number[]} an array of numbers.
- */
-pv.range = function(start, stop, step) {
- if (arguments.length == 1) {
- stop = start;
- start = 0;
- }
- if (step == undefined) step = 1;
- else if (!step) throw new Error("step must be non-zero");
- var array = [], i = 0, j;
- if (step < 0) {
- while ((j = start + step * i++) > stop) {
- array.push(j);
- }
- } else {
- while ((j = start + step * i++) < stop) {
- array.push(j);
- }
- }
- return array;
-};
-
-/**
- * Returns a random number in the range [<tt>min</tt>, <tt>max</tt>) that is a
- * multiple of <tt>step</tt>. More specifically, the returned number is of the
- * form <tt>min</tt> + <i>n</i> * <tt>step</tt>, where <i>n</i> is a nonnegative
- * integer. If <tt>step</tt> is not specified, it defaults to 1, returning a
- * random integer if <tt>min</tt> is also an integer.
- *
- * @param min {number} minimum value.
- * @param [max] {number} maximum value.
- * @param [step] {numbeR} step value.
- */
-pv.random = function(min, max, step) {
- if (arguments.length == 1) {
- max = min;
- min = 0;
- }
- if (step == undefined) {
- step = 1;
- }
- return step
- ? (Math.floor(Math.random() * (max - min) / step) * step + min)
- : (Math.random() * (max - min) + min);
-};
-
-/**
- * Concatenates the specified array with itself <i>n</i> times. For example,
- * <tt>pv.repeat([1, 2])</tt> returns [1, 2, 1, 2].
- *
- * @param {array} a an array.
- * @param {number} [n] the number of times to repeat; defaults to two.
- * @returns {array} an array that repeats the specified array.
- */
-pv.repeat = function(array, n) {
- if (arguments.length == 1) n = 2;
- return pv.blend(pv.range(n).map(function() { return array; }));
-};
-
-/**
- * Given two arrays <tt>a</tt> and <tt>b</tt>, <style
- * type="text/css">sub{line-height:0}</style> returns an array of all possible
- * pairs of elements [a<sub>i</sub>, b<sub>j</sub>]. The outer loop is on array
- * <i>a</i>, while the inner loop is on <i>b</i>, such that the order of
- * returned elements is [a<sub>0</sub>, b<sub>0</sub>], [a<sub>0</sub>,
- * b<sub>1</sub>], ... [a<sub>0</sub>, b<sub>m</sub>], [a<sub>1</sub>,
- * b<sub>0</sub>], [a<sub>1</sub>, b<sub>1</sub>], ... [a<sub>1</sub>,
- * b<sub>m</sub>], ... [a<sub>n</sub>, b<sub>m</sub>]. If either array is empty,
- * an empty array is returned.
- *
- * @param {array} a an array.
- * @param {array} b an array.
- * @returns {array} an array of pairs of elements in <tt>a</tt> and <tt>b</tt>.
- */
-pv.cross = function(a, b) {
- var array = [];
- for (var i = 0, n = a.length, m = b.length; i < n; i++) {
- for (var j = 0, x = a[i]; j < m; j++) {
- array.push([x, b[j]]);
- }
- }
- return array;
-};
-
-/**
- * Given the specified array of arrays, concatenates the arrays into a single
- * array. If the individual arrays are explicitly known, an alternative to blend
- * is to use JavaScript's <tt>concat</tt> method directly. These two equivalent
- * expressions:<ul>
- *
- * <li><tt>pv.blend([[1, 2, 3], ["a", "b", "c"]])</tt>
- * <li><tt>[1, 2, 3].concat(["a", "b", "c"])</tt>
- *
- * </ul>return [1, 2, 3, "a", "b", "c"].
- *
- * @param {array[]} arrays an array of arrays.
- * @returns {array} an array containing all the elements of each array in
- * <tt>arrays</tt>.
- */
-pv.blend = function(arrays) {
- return Array.prototype.concat.apply([], arrays);
-};
-
-/**
- * Given the specified array of arrays, <style
- * type="text/css">sub{line-height:0}</style> transposes each element
- * array<sub>ij</sub> with array<sub>ji</sub>. If the array has dimensions
- * <i>n</i>&times;<i>m</i>, it will have dimensions <i>m</i>&times;<i>n</i>
- * after this method returns. This method transposes the elements of the array
- * in place, mutating the array, and returning a reference to the array.
- *
- * @param {array[]} arrays an array of arrays.
- * @returns {array[]} the passed-in array, after transposing the elements.
- */
-pv.transpose = function(arrays) {
- var n = arrays.length, m = pv.max(arrays, function(d) { return d.length; });
-
- if (m > n) {
- arrays.length = m;
- for (var i = n; i < m; i++) {
- arrays[i] = new Array(n);
- }
- for (var i = 0; i < n; i++) {
- for (var j = i + 1; j < m; j++) {
- var t = arrays[i][j];
- arrays[i][j] = arrays[j][i];
- arrays[j][i] = t;
- }
- }
- } else {
- for (var i = 0; i < m; i++) {
- arrays[i].length = n;
- }
- for (var i = 0; i < n; i++) {
- for (var j = 0; j < i; j++) {
- var t = arrays[i][j];
- arrays[i][j] = arrays[j][i];
- arrays[j][i] = t;
- }
- }
- }
-
- arrays.length = m;
- for (var i = 0; i < m; i++) {
- arrays[i].length = n;
- }
-
- return arrays;
-};
-
-/**
- * Returns all of the property names (keys) of the specified object (a map). The
- * order of the returned array is not defined.
- *
- * @param map an object.
- * @returns {string[]} an array of strings corresponding to the keys.
- * @see #entries
- */
-pv.keys = function(map) {
- var array = [];
- for (var key in map) {
- array.push(key);
- }
- return array;
-};
-
-/**
- * Returns all of the entries (key-value pairs) of the specified object (a
- * map). The order of the returned array is not defined. Each key-value pair is
- * represented as an object with <tt>key</tt> and <tt>value</tt> attributes,
- * e.g., <tt>{key: "foo", value: 42}</tt>.
- *
- * @param map an object.
- * @returns {array} an array of key-value pairs corresponding to the keys.
- */
-pv.entries = function(map) {
- var array = [];
- for (var key in map) {
- array.push({ key: key, value: map[key] });
- }
- return array;
-};
-
-/**
- * Returns all of the values (attribute values) of the specified object (a
- * map). The order of the returned array is not defined.
- *
- * @param map an object.
- * @returns {array} an array of objects corresponding to the values.
- * @see #entries
- */
-pv.values = function(map) {
- var array = [];
- for (var key in map) {
- array.push(map[key]);
- }
- return array;
-};
-
-/**
- * @private A private variant of Array.prototype.map that supports the index
- * property.
- */
-function map(array, f) {
- var o = {};
- return f
- ? array.map(function(d, i) { o.index = i; return f.call(o, d); })
- : array.slice();
-};
-
-/**
- * Returns a normalized copy of the specified array, such that the sum of the
- * returned elements sum to one. If the specified array is not an array of
- * numbers, an optional accessor function <tt>f</tt> can be specified to map the
- * elements to numbers. For example, if <tt>array</tt> is an array of objects,
- * and each object has a numeric property "foo", the expression
- *
- * <pre>pv.normalize(array, function(d) d.foo)</pre>
- *
- * returns a normalized array on the "foo" property. If an accessor function is
- * not specified, the identity function is used. Accessor functions can refer to
- * <tt>this.index</tt>.
- *
- * @param {array} array an array of objects, or numbers.
- * @param {function} [f] an optional accessor function.
- * @returns {number[]} an array of numbers that sums to one.
- */
-pv.normalize = function(array, f) {
- var norm = map(array, f), sum = pv.sum(norm);
- for (var i = 0; i < norm.length; i++) norm[i] /= sum;
- return norm;
-};
-
-/**
- * Returns the sum of the specified array. If the specified array is not an
- * array of numbers, an optional accessor function <tt>f</tt> can be specified
- * to map the elements to numbers. See {@link #normalize} for an example.
- * Accessor functions can refer to <tt>this.index</tt>.
- *
- * @param {array} array an array of objects, or numbers.
- * @param {function} [f] an optional accessor function.
- * @returns {number} the sum of the specified array.
- */
-pv.sum = function(array, f) {
- var o = {};
- return array.reduce(f
- ? function(p, d, i) { o.index = i; return p + f.call(o, d); }
- : function(p, d) { return p + d; }, 0);
-};
-
-/**
- * Returns the maximum value of the specified array. If the specified array is
- * not an array of numbers, an optional accessor function <tt>f</tt> can be
- * specified to map the elements to numbers. See {@link #normalize} for an
- * example. Accessor functions can refer to <tt>this.index</tt>.
- *
- * @param {array} array an array of objects, or numbers.
- * @param {function} [f] an optional accessor function.
- * @returns {number} the maximum value of the specified array.
- */
-pv.max = function(array, f) {
- if (f == pv.index) return array.length - 1;
- return Math.max.apply(null, f ? map(array, f) : array);
-};
-
-/**
- * Returns the index of the maximum value of the specified array. If the
- * specified array is not an array of numbers, an optional accessor function
- * <tt>f</tt> can be specified to map the elements to numbers. See
- * {@link #normalize} for an example. Accessor functions can refer to
- * <tt>this.index</tt>.
- *
- * @param {array} array an array of objects, or numbers.
- * @param {function} [f] an optional accessor function.
- * @returns {number} the index of the maximum value of the specified array.
- */
-pv.max.index = function(array, f) {
- if (f == pv.index) return array.length - 1;
- if (!f) f = pv.identity;
- var maxi = -1, maxx = -Infinity, o = {};
- for (var i = 0; i < array.length; i++) {
- o.index = i;
- var x = f.call(o, array[i]);
- if (x > maxx) {
- maxx = x;
- maxi = i;
- }
- }
- return maxi;
-}
-
-/**
- * Returns the minimum value of the specified array of numbers. If the specified
- * array is not an array of numbers, an optional accessor function <tt>f</tt>
- * can be specified to map the elements to numbers. See {@link #normalize} for
- * an example. Accessor functions can refer to <tt>this.index</tt>.
- *
- * @param {array} array an array of objects, or numbers.
- * @param {function} [f] an optional accessor function.
- * @returns {number} the minimum value of the specified array.
- */
-pv.min = function(array, f) {
- if (f == pv.index) return 0;
- return Math.min.apply(null, f ? map(array, f) : array);
-};
-
-/**
- * Returns the index of the minimum value of the specified array. If the
- * specified array is not an array of numbers, an optional accessor function
- * <tt>f</tt> can be specified to map the elements to numbers. See
- * {@link #normalize} for an example. Accessor functions can refer to
- * <tt>this.index</tt>.
- *
- * @param {array} array an array of objects, or numbers.
- * @param {function} [f] an optional accessor function.
- * @returns {number} the index of the minimum value of the specified array.
- */
-pv.min.index = function(array, f) {
- if (f == pv.index) return 0;
- if (!f) f = pv.identity;
- var mini = -1, minx = Infinity, o = {};
- for (var i = 0; i < array.length; i++) {
- o.index = i;
- var x = f.call(o, array[i]);
- if (x < minx) {
- minx = x;
- mini = i;
- }
- }
- return mini;
-}
-
-/**
- * Returns the arithmetic mean, or average, of the specified array. If the
- * specified array is not an array of numbers, an optional accessor function
- * <tt>f</tt> can be specified to map the elements to numbers. See
- * {@link #normalize} for an example. Accessor functions can refer to
- * <tt>this.index</tt>.
- *
- * @param {array} array an array of objects, or numbers.
- * @param {function} [f] an optional accessor function.
- * @returns {number} the mean of the specified array.
- */
-pv.mean = function(array, f) {
- return pv.sum(array, f) / array.length;
-};
-
-/**
- * Returns the median of the specified array. If the specified array is not an
- * array of numbers, an optional accessor function <tt>f</tt> can be specified
- * to map the elements to numbers. See {@link #normalize} for an example.
- * Accessor functions can refer to <tt>this.index</tt>.
- *
- * @param {array} array an array of objects, or numbers.
- * @param {function} [f] an optional accessor function.
- * @returns {number} the median of the specified array.
- */
-pv.median = function(array, f) {
- if (f == pv.index) return (array.length - 1) / 2;
- array = map(array, f).sort(pv.naturalOrder);
- if (array.length % 2) return array[Math.floor(array.length / 2)];
- var i = array.length / 2;
- return (array[i - 1] + array[i]) / 2;
-};
-
-/**
- * Returns a map constructed from the specified <tt>keys</tt>, using the
- * function <tt>f</tt> to compute the value for each key. The single argument to
- * the value function is the key. The callback is invoked only for indexes of
- * the array which have assigned values; it is not invoked for indexes which
- * have been deleted or which have never been assigned values.
- *
- * <p>For example, this expression creates a map from strings to string length:
- *
- * <pre>pv.dict(["one", "three", "seventeen"], function(s) s.length)</pre>
- *
- * The returned value is <tt>{one: 3, three: 5, seventeen: 9}</tt>. Accessor
- * functions can refer to <tt>this.index</tt>.
- *
- * @param {array} keys an array.
- * @param {function} f a value function.
- * @returns a map from keys to values.
- */
-pv.dict = function(keys, f) {
- var m = {}, o = {};
- for (var i = 0; i < keys.length; i++) {
- if (i in keys) {
- var k = keys[i];
- o.index = i;
- m[k] = f.call(o, k);
- }
- }
- return m;
-};
-
-/**
- * Returns a permutation of the specified array, using the specified array of
- * indexes. The returned array contains the corresponding element in
- * <tt>array</tt> for each index in <tt>indexes</tt>, in order. For example,
- *
- * <pre>pv.permute(["a", "b", "c"], [1, 2, 0])</pre>
- *
- * returns <tt>["b", "c", "a"]</tt>. It is acceptable for the array of indexes
- * to be a different length from the array of elements, and for indexes to be
- * duplicated or omitted. The optional accessor function <tt>f</tt> can be used
- * to perform a simultaneous mapping of the array elements. Accessor functions
- * can refer to <tt>this.index</tt>.
- *
- * @param {array} array an array.
- * @param {number[]} indexes an array of indexes into <tt>array</tt>.
- * @param {function} [f] an optional accessor function.
- * @returns {array} an array of elements from <tt>array</tt>; a permutation.
- */
-pv.permute = function(array, indexes, f) {
- if (!f) f = pv.identity;
- var p = new Array(indexes.length), o = {};
- indexes.forEach(function(j, i) { o.index = j; p[i] = f.call(o, array[j]); });
- return p;
-};
-
-/**
- * Returns a map from key to index for the specified <tt>keys</tt> array. For
- * example,
- *
- * <pre>pv.numerate(["a", "b", "c"])</pre>
- *
- * returns <tt>{a: 0, b: 1, c: 2}</tt>. Note that since JavaScript maps only
- * support string keys, <tt>keys</tt> must contain strings, or other values that
- * naturally map to distinct string values. Alternatively, an optional accessor
- * function <tt>f</tt> can be specified to compute the string key for the given
- * element. Accessor functions can refer to <tt>this.index</tt>.
- *
- * @param {array} keys an array, usually of string keys.
- * @param {function} [f] an optional key function.
- * @returns a map from key to index.
- */
-pv.numerate = function(keys, f) {
- if (!f) f = pv.identity;
- var map = {}, o = {};
- keys.forEach(function(x, i) { o.index = i; map[f.call(o, x)] = i; });
- return map;
-};
-
-/**
- * The comparator function for natural order. This can be used in conjunction with
- * the built-in array <tt>sort</tt> method to sort elements by their natural
- * order, ascending. Note that if no comparator function is specified to the
- * built-in <tt>sort</tt> method, the default order is lexicographic, <i>not</i>
- * natural!
- *
- * @see <a
- * href="http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/sort">Array.sort</a>.
- * @param a an element to compare.
- * @param b an element to compare.
- * @returns {number} negative if a &lt; b; positive if a &gt; b; otherwise 0.
- */
-pv.naturalOrder = function(a, b) {
- return (a < b) ? -1 : ((a > b) ? 1 : 0);
-};
-
-/**
- * The comparator function for reverse natural order. This can be used in
- * conjunction with the built-in array <tt>sort</tt> method to sort elements by
- * their natural order, descending. Note that if no comparator function is
- * specified to the built-in <tt>sort</tt> method, the default order is
- * lexicographic, <i>not</i> natural!
- *
- * @see #naturalOrder
- * @param a an element to compare.
- * @param b an element to compare.
- * @returns {number} negative if a &lt; b; positive if a &gt; b; otherwise 0.
- */
-pv.reverseOrder = function(b, a) {
- return (a < b) ? -1 : ((a > b) ? 1 : 0);
-};
-
-/**
- * @private Computes the value of the specified CSS property <tt>p</tt> on the
- * specified element <tt>e</tt>.
- *
- * @param {string} p the name of the CSS property.
- * @param e the element on which to compute the CSS property.
- */
-pv.css = function(e, p) {
- return window.getComputedStyle
- ? window.getComputedStyle(e, null).getPropertyValue(p)
- : e.currentStyle[p];
-};
-
-/**
- * Namespace constants for SVG, XMLNS, and XLINK.
- *
- * @namespace Namespace constants for SVG, XMLNS, and XLINK.
- */
-pv.ns = {
- /**
- * The SVG namespace, "http://www.w3.org/2000/svg".
- *
- * @type string
- * @constant
- */
- svg: "http://www.w3.org/2000/svg",
-
- /**
- * The XMLNS namespace, "http://www.w3.org/2000/xmlns".
- *
- * @type string
- * @constant
- */
- xmlns: "http://www.w3.org/2000/xmlns",
-
- /**
- * The XLINK namespace, "http://www.w3.org/1999/xlink".
- *
- * @type string
- * @constant
- */
- xlink: "http://www.w3.org/1999/xlink"
-};
-
-/**
- * Protovis major and minor version numbers.
- *
- * @namespace Protovis major and minor version numbers.
- */
-pv.version = {
- /**
- * The major version number.
- *
- * @type number
- * @constant
- */
- major: 3,
-
- /**
- * The minor version number.
- *
- * @type number
- * @constant
- */
- minor: 1
-};
-
-/**
- * @private Reports the specified error to the JavaScript console. Mozilla only
- * allows logging to the console for privileged code; if the console is
- * unavailable, the alert dialog box is used instead.
- *
- * @param e the exception that triggered the error.
- */
-pv.error = function(e) {
- (typeof console == "undefined") ? alert(e) : console.error(e);
-};
-
-/**
- * @private Registers the specified listener for events of the specified type on
- * the specified target. For standards-compliant browsers, this method uses
- * <tt>addEventListener</tt>; for Internet Explorer, <tt>attachEvent</tt>.
- *
- * @param target a DOM element.
- * @param {string} type the type of event, such as "click".
- * @param {function} the listener callback function.
- */
-pv.listen = function(target, type, listener) {
- return target.addEventListener
- ? target.addEventListener(type, listener, false)
- : target.attachEvent("on" + type, listener);
-};
-
-/**
- * Returns the logarithm with a given base value.
- *
- * @param {number} x the number for which to compute the logarithm.
- * @param {number} b the base of the logarithm.
- * @returns {number} the logarithm value.
- */
-pv.log = function(x, b) {
- return Math.log(x) / Math.log(b);
-};
-
-/**
- * Computes a zero-symmetric logarithm. Computes the logarithm of the absolute
- * value of the input, and determines the sign of the output according to the
- * sign of the input value.
- *
- * @param {number} x the number for which to compute the logarithm.
- * @param {number} b the base of the logarithm.
- * @returns {number} the symmetric log value.
- */
-pv.logSymmetric = function(x, b) {
- return (x == 0) ? 0 : ((x < 0) ? -pv.log(-x, b) : pv.log(x, b));
-};
-
-/**
- * Computes a zero-symmetric logarithm, with adjustment to values between zero
- * and the logarithm base. This adjustment introduces distortion for values less
- * than the base number, but enables simultaneous plotting of log-transformed
- * data involving both positive and negative numbers.
- *
- * @param {number} x the number for which to compute the logarithm.
- * @param {number} b the base of the logarithm.
- * @returns {number} the adjusted, symmetric log value.
- */
-pv.logAdjusted = function(x, b) {
- var negative = x < 0;
- if (x < b) x += (b - x) / b;
- return negative ? -pv.log(x, b) : pv.log(x, b);
-};
-
-/**
- * Rounds an input value down according to its logarithm. The method takes the
- * floor of the logarithm of the value and then uses the resulting value as an
- * exponent for the base value.
- *
- * @param {number} x the number for which to compute the logarithm floor.
- * @param {number} b the base of the logarithm.
- * @return {number} the rounded-by-logarithm value.
- */
-pv.logFloor = function(x, b) {
- return (x > 0)
- ? Math.pow(b, Math.floor(pv.log(x, b)))
- : -Math.pow(b, -Math.floor(-pv.log(-x, b)));
-};
-
-/**
- * Rounds an input value up according to its logarithm. The method takes the
- * ceiling of the logarithm of the value and then uses the resulting value as an
- * exponent for the base value.
- *
- * @param {number} x the number for which to compute the logarithm ceiling.
- * @param {number} b the base of the logarithm.
- * @return {number} the rounded-by-logarithm value.
- */
-pv.logCeil = function(x, b) {
- return (x > 0)
- ? Math.pow(b, Math.ceil(pv.log(x, b)))
- : -Math.pow(b, -Math.ceil(-pv.log(-x, b)));
-};
-
-/**
- * Searches the specified array of numbers for the specified value using the
- * binary search algorithm. The array must be sorted (as by the <tt>sort</tt>
- * method) prior to making this call. If it is not sorted, the results are
- * undefined. If the array contains multiple elements with the specified value,
- * there is no guarantee which one will be found.
- *
- * <p>The <i>insertion point</i> is defined as the point at which the value
- * would be inserted into the array: the index of the first element greater than
- * the value, or <tt>array.length</tt>, if all elements in the array are less
- * than the specified value. Note that this guarantees that the return value
- * will be nonnegative if and only if the value is found.
- *
- * @param {number[]} array the array to be searched.
- * @param {number} value the value to be searched for.
- * @returns the index of the search value, if it is contained in the array;
- * otherwise, (-(<i>insertion point</i>) - 1).
- * @param {function} [f] an optional key function.
- */
-pv.search = function(array, value, f) {
- if (!f) f = pv.identity;
- var low = 0, high = array.length - 1;
- while (low <= high) {
- var mid = (low + high) >> 1, midValue = f(array[mid]);
- if (midValue < value) low = mid + 1;
- else if (midValue > value) high = mid - 1;
- else return mid;
- }
- return -low - 1;
-};
-
-pv.search.index = function(array, value, f) {
- var i = pv.search(array, value, f);
- return (i < 0) ? (-i - 1) : i;
-};
-/**
- * Returns a {@link pv.Tree} operator for the specified array. This is a
- * convenience factory method, equivalent to <tt>new pv.Tree(array)</tt>.
- *
- * @see pv.Tree
- * @param {array} array an array from which to construct a tree.
- * @returns {pv.Tree} a tree operator for the specified array.
- */
-pv.tree = function(array) {
- return new pv.Tree(array);
-};
-
-/**
- * Constructs a tree operator for the specified array. This constructor should
- * not be invoked directly; use {@link pv.tree} instead.
- *
- * @class Represents a tree operator for the specified array. The tree operator
- * allows a hierarchical map to be constructed from an array; it is similar to
- * the {@link pv.Nest} operator, except the hierarchy is derived dynamically
- * from the array elements.
- *
- * <p>For example, given an array of size information for ActionScript classes:
- *
- * <pre>{ name: "flare.flex.FlareVis", size: 4116 },
- * { name: "flare.physics.DragForce", size: 1082 },
- * { name: "flare.physics.GravityForce", size: 1336 }, ...</pre>
- *
- * To facilitate visualization, it may be useful to nest the elements by their
- * package hierarchy:
- *
- * <pre>var tree = pv.tree(classes)
- * .keys(function(d) d.name.split("."))
- * .map();</pre>
- *
- * The resulting tree is:
- *
- * <pre>{ flare: {
- * flex: {
- * FlareVis: {
- * name: "flare.flex.FlareVis",
- * size: 4116 } },
- * physics: {
- * DragForce: {
- * name: "flare.physics.DragForce",
- * size: 1082 },
- * GravityForce: {
- * name: "flare.physics.GravityForce",
- * size: 1336 } },
- * ... } }</pre>
- *
- * By specifying a value function,
- *
- * <pre>var tree = pv.tree(classes)
- * .keys(function(d) d.name.split("."))
- * .value(function(d) d.size)
- * .map();</pre>
- *
- * we can further eliminate redundant data:
- *
- * <pre>{ flare: {
- * flex: {
- * FlareVis: 4116 },
- * physics: {
- * DragForce: 1082,
- * GravityForce: 1336 },
- * ... } }</pre>
- *
- * For visualizations with large data sets, performance improvements may be seen
- * by storing the data in a tree format, and then flattening it into an array at
- * runtime with {@link pv.Flatten}.
- *
- * @param {array} array an array from which to construct a tree.
- */
-pv.Tree = function(array) {
- this.array = array;
-};
-
-/**
- * Assigns a <i>keys</i> function to this operator; required. The keys function
- * returns an array of <tt>string</tt>s for each element in the associated
- * array; these keys determine how the elements are nested in the tree. The
- * returned keys should be unique for each element in the array; otherwise, the
- * behavior of this operator is undefined.
- *
- * @param {function} k the keys function.
- * @returns {pv.Tree} this.
- */
-pv.Tree.prototype.keys = function(k) {
- this.k = k;
- return this;
-};
-
-/**
- * Assigns a <i>value</i> function to this operator; optional. The value
- * function specifies an optional transformation of the element in the array
- * before it is inserted into the map. If no value function is specified, it is
- * equivalent to using the identity function.
- *
- * @param {function} k the value function.
- * @returns {pv.Tree} this.
- */
-pv.Tree.prototype.value = function(v) {
- this.v = v;
- return this;
-};
-
-/**
- * Returns a hierarchical map of values. The hierarchy is determined by the keys
- * function; the values in the map are determined by the value function.
- *
- * @returns a hierarchical map of values.
- */
-pv.Tree.prototype.map = function() {
- var map = {}, o = {};
- for (var i = 0; i < this.array.length; i++) {
- o.index = i;
- var value = this.array[i], keys = this.k.call(o, value), node = map;
- for (var j = 0; j < keys.length - 1; j++) {
- node = node[keys[j]] || (node[keys[j]] = {});
- }
- node[keys[j]] = this.v ? this.v.call(o, value) : value;
- }
- return map;
-};
-/**
- * Returns a {@link pv.Nest} operator for the specified array. This is a
- * convenience factory method, equivalent to <tt>new pv.Nest(array)</tt>.
- *
- * @see pv.Nest
- * @param {array} array an array of elements to nest.
- * @returns {pv.Nest} a nest operator for the specified array.
- */
-pv.nest = function(array) {
- return new pv.Nest(array);
-};
-
-/**
- * Constructs a nest operator for the specified array. This constructor should
- * not be invoked directly; use {@link pv.nest} instead.
- *
- * @class Represents a {@link Nest} operator for the specified array. Nesting
- * allows elements in an array to be grouped into a hierarchical tree
- * structure. The levels in the tree are specified by <i>key</i> functions. The
- * leaf nodes of the tree can be sorted by value, while the internal nodes can
- * be sorted by key. Finally, the tree can be returned either has a
- * multidimensional array via {@link #entries}, or as a hierarchical map via
- * {@link #map}. The {@link #rollup} routine similarly returns a map, collapsing
- * the elements in each leaf node using a summary function.
- *
- * <p>For example, consider the following tabular data structure of Barley
- * yields, from various sites in Minnesota during 1931-2:
- *
- * <pre>{ yield: 27.00, variety: "Manchuria", year: 1931, site: "University Farm" },
- * { yield: 48.87, variety: "Manchuria", year: 1931, site: "Waseca" },
- * { yield: 27.43, variety: "Manchuria", year: 1931, site: "Morris" }, ...</pre>
- *
- * To facilitate visualization, it may be useful to nest the elements first by
- * year, and then by variety, as follows:
- *
- * <pre>var nest = pv.nest(yields)
- * .key(function(d) d.year)
- * .key(function(d) d.variety)
- * .entries();</pre>
- *
- * This returns a nested array. Each element of the outer array is a key-values
- * pair, listing the values for each distinct key:
- *
- * <pre>{ key: 1931, values: [
- * { key: "Manchuria", values: [
- * { yield: 27.00, variety: "Manchuria", year: 1931, site: "University Farm" },
- * { yield: 48.87, variety: "Manchuria", year: 1931, site: "Waseca" },
- * { yield: 27.43, variety: "Manchuria", year: 1931, site: "Morris" },
- * ...
- * ] },
- * { key: "Glabron", values: [
- * { yield: 43.07, variety: "Glabron", year: 1931, site: "University Farm" },
- * { yield: 55.20, variety: "Glabron", year: 1931, site: "Waseca" },
- * ...
- * ] },
- * ] },
- * { key: 1932, values: ... }</pre>
- *
- * Further details, including sorting and rollup, is provided below on the
- * corresponding methods.
- *
- * @param {array} array an array of elements to nest.
- */
-pv.Nest = function(array) {
- this.array = array;
- this.keys = [];
-};
-
-/**
- * Nests using the specified key function. Multiple keys may be added to the
- * nest; the array elements will be nested in the order keys are specified.
- *
- * @param {function} key a key function; must return a string or suitable map
- * key.
- * @return {pv.Nest} this.
- */
-pv.Nest.prototype.key = function(key) {
- this.keys.push(key);
- return this;
-};
-
-/**
- * Sorts the previously-added keys. The natural sort order is used by default
- * (see {@link pv.naturalOrder}); if an alternative order is desired,
- * <tt>order</tt> should be a comparator function. If this method is not called
- * (i.e., keys are <i>unsorted</i>), keys will appear in the order they appear
- * in the underlying elements array. For example,
- *
- * <pre>pv.nest(yields)
- * .key(function(d) d.year)
- * .key(function(d) d.variety)
- * .sortKeys()
- * .entries()</pre>
- *
- * groups yield data by year, then variety, and sorts the variety groups
- * lexicographically (since the variety attribute is a string).
- *
- * <p>Key sort order is only used in conjunction with {@link #entries}, which
- * returns an array of key-values pairs. If the nest is used to construct a
- * {@link #map} instead, keys are unsorted.
- *
- * @param {function} [order] an optional comparator function.
- * @returns {pv.Nest} this.
- */
-pv.Nest.prototype.sortKeys = function(order) {
- this.keys[this.keys.length - 1].order = order || pv.naturalOrder;
- return this;
-};
-
-/**
- * Sorts the leaf values. The natural sort order is used by default (see
- * {@link pv.naturalOrder}); if an alternative order is desired, <tt>order</tt>
- * should be a comparator function. If this method is not called (i.e., values
- * are <i>unsorted</i>), values will appear in the order they appear in the
- * underlying elements array. For example,
- *
- * <pre>pv.nest(yields)
- * .key(function(d) d.year)
- * .key(function(d) d.variety)
- * .sortValues(function(a, b) a.yield - b.yield)
- * .entries()</pre>
- *
- * groups yield data by year, then variety, and sorts the values for each
- * variety group by yield.
- *
- * <p>Value sort order, unlike keys, applies to both {@link #entries} and
- * {@link #map}. It has no effect on {@link #rollup}.
- *
- * @param {function} [order] an optional comparator function.
- * @return {pv.Nest} this.
- */
-pv.Nest.prototype.sortValues = function(order) {
- this.order = order || pv.naturalOrder;
- return this;
-};
-
-/**
- * Returns a hierarchical map of values. Each key adds one level to the
- * hierarchy. With only a single key, the returned map will have a key for each
- * distinct value of the key function; the correspond value with be an array of
- * elements with that key value. If a second key is added, this will be a nested
- * map. For example:
- *
- * <pre>pv.nest(yields)
- * .key(function(d) d.variety)
- * .key(function(d) d.site)
- * .map()</pre>
- *
- * returns a map <tt>m</tt> such that <tt>m[variety][site]</tt> is an array, a subset of
- * <tt>yields</tt>, with each element having the given variety and site.
- *
- * @returns a hierarchical map of values.
- */
-pv.Nest.prototype.map = function() {
- var map = {}, values = [];
-
- /* Build the map. */
- for (var i, j = 0; j < this.array.length; j++) {
- var x = this.array[j];
- var m = map;
- for (i = 0; i < this.keys.length - 1; i++) {
- var k = this.keys[i](x);
- if (!m[k]) m[k] = {};
- m = m[k];
- }
- k = this.keys[i](x);
- if (!m[k]) {
- var a = [];
- values.push(a);
- m[k] = a;
- }
- m[k].push(x);
- }
-
- /* Sort each leaf array. */
- if (this.order) {
- for (var i = 0; i < values.length; i++) {
- values[i].sort(this.order);
- }
- }
-
- return map;
-};
-
-/**
- * Returns a hierarchical nested array. This method is similar to
- * {@link pv.entries}, but works recursively on the entire hierarchy. Rather
- * than returning a map like {@link #map}, this method returns a nested
- * array. Each element of the array has a <tt>key</tt> and <tt>values</tt>
- * field. For leaf nodes, the <tt>values</tt> array will be a subset of the
- * underlying elements array; for non-leaf nodes, the <tt>values</tt> array will
- * contain more key-values pairs.
- *
- * <p>For an example usage, see the {@link Nest} constructor.
- *
- * @returns a hierarchical nested array.
- */
-pv.Nest.prototype.entries = function() {
-
- /** Recursively extracts the entries for the given map. */
- function entries(map) {
- var array = [];
- for (var k in map) {
- var v = map[k];
- array.push({ key: k, values: (v instanceof Array) ? v : entries(v) });
- };
- return array;
- }
-
- /** Recursively sorts the values for the given key-values array. */
- function sort(array, i) {
- var o = this.keys[i].order;
- if (o) array.sort(function(a, b) { return o(a.key, b.key); });
- if (++i < this.keys.length) {
- for (var j = 0; j < array.length; j++) {
- sort.call(this, array[j].values, i);
- }
- }
- return array;
- }
-
- return sort.call(this, entries(this.map()), 0);
-};
-
-/**
- * Returns a rollup map. The behavior of this method is the same as
- * {@link #map}, except that the leaf values are replaced with the return value
- * of the specified rollup function <tt>f</tt>. For example,
- *
- * <pre>pv.nest(yields)
- * .key(function(d) d.site)
- * .rollup(function(v) pv.median(v, function(d) d.yield))</pre>
- *
- * first groups yield data by site, and then returns a map from site to median
- * yield for the given site.
- *
- * @see #map
- * @param {function} f a rollup function.
- * @returns a hierarchical map, with the leaf values computed by <tt>f</tt>.
- */
-pv.Nest.prototype.rollup = function(f) {
-
- /** Recursively descends to the leaf nodes (arrays) and does rollup. */
- function rollup(map) {
- for (var key in map) {
- var value = map[key];
- if (value instanceof Array) {
- map[key] = f(value);
- } else {
- rollup(value);
- }
- }
- return map;
- }
-
- return rollup(this.map());
-};
-/**
- * Returns a {@link pv.Flatten} operator for the specified map. This is a
- * convenience factory method, equivalent to <tt>new pv.Flatten(map)</tt>.
- *
- * @see pv.Flatten
- * @param map a map to flatten.
- * @returns {pv.Flatten} a flatten operator for the specified map.
- */
-pv.flatten = function(map) {
- return new pv.Flatten(map);
-};
-
-/**
- * Constructs a flatten operator for the specified map. This constructor should
- * not be invoked directly; use {@link pv.flatten} instead.
- *
- * @class Represents a flatten operator for the specified array. Flattening
- * allows hierarchical maps to be flattened into an array. The levels in the
- * input tree are specified by <i>key</i> functions.
- *
- * <p>For example, consider the following hierarchical data structure of Barley
- * yields, from various sites in Minnesota during 1931-2:
- *
- * <pre>{ 1931: {
- * Manchuria: {
- * "University Farm": 27.00,
- * "Waseca": 48.87,
- * "Morris": 27.43,
- * ... },
- * Glabron: {
- * "University Farm": 43.07,
- * "Waseca": 55.20,
- * ... } },
- * 1932: {
- * ... } }</pre>
- *
- * To facilitate visualization, it may be useful to flatten the tree into a
- * tabular array:
- *
- * <pre>var array = pv.flatten(yields)
- * .key("year")
- * .key("variety")
- * .key("site")
- * .key("yield")
- * .array();</pre>
- *
- * This returns an array of object elements. Each element in the array has
- * attributes corresponding to this flatten operator's keys:
- *
- * <pre>{ site: "University Farm", variety: "Manchuria", year: 1931, yield: 27 },
- * { site: "Waseca", variety: "Manchuria", year: 1931, yield: 48.87 },
- * { site: "Morris", variety: "Manchuria", year: 1931, yield: 27.43 },
- * { site: "University Farm", variety: "Glabron", year: 1931, yield: 43.07 },
- * { site: "Waseca", variety: "Glabron", year: 1931, yield: 55.2 }, ...</pre>
- *
- * <p>The flatten operator is roughly the inverse of the {@link pv.Nest} and
- * {@link pv.Tree} operators.
- *
- * @param map a map to flatten.
- */
-pv.Flatten = function(map) {
- this.map = map;
- this.keys = [];
-};
-
-/**
- * Flattens using the specified key function. Multiple keys may be added to the
- * flatten; the tiers of the underlying tree must correspond to the specified
- * keys, in order. The order of the returned array is undefined; however, you
- * can easily sort it.
- *
- * @param {string} key the key name.
- * @param {function} [f] an optional value map function.
- * @return {pv.Nest} this.
- */
-pv.Flatten.prototype.key = function(key, f) {
- this.keys.push({name: key, value: f});
- return this;
-};
-
-/**
- * Returns the flattened array. Each entry in the array is an object; each
- * object has attributes corresponding to this flatten operator's keys.
- *
- * @returns an array of elements from the flattened map.
- */
-pv.Flatten.prototype.array = function() {
- var entries = [], stack = [], keys = this.keys;
-
- /* Recursively visits the specified value. */
- function visit(value, i) {
- if (i < keys.length - 1) {
- for (var key in value) {
- stack.push(key);
- visit(value[key], i + 1);
- stack.pop();
- }
- } else {
- entries.push(stack.concat(value));
- }
- }
-
- visit(this.map, 0);
- return entries.map(function(stack) {
- var m = {};
- for (var i = 0; i < keys.length; i++) {
- var k = keys[i], v = stack[i];
- m[k.name] = k.value ? k.value.call(null, v) : v;
- }
- return m;
- });
-};
-/**
- * Returns a {@link pv.Vector} for the specified <i>x</i> and <i>y</i>
- * coordinate. This is a convenience factory method, equivalent to <tt>new
- * pv.Vector(x, y)</tt>.
- *
- * @see pv.Vector
- * @param {number} x the <i>x</i> coordinate.
- * @param {number} y the <i>y</i> coordinate.
- * @returns {pv.Vector} a vector for the specified coordinates.
- */
-pv.vector = function(x, y) {
- return new pv.Vector(x, y);
-};
-
-/**
- * Constructs a {@link pv.Vector} for the specified <i>x</i> and <i>y</i>
- * coordinate. This constructor should not be invoked directly; use
- * {@link pv.vector} instead.
- *
- * @class Represents a two-dimensional vector; a 2-tuple <i>&#x27e8;x,
- * y&#x27e9;</i>.
- *
- * @param {number} x the <i>x</i> coordinate.
- * @param {number} y the <i>y</i> coordinate.
- */
-pv.Vector = function(x, y) {
- this.x = x;
- this.y = y;
-};
-
-/**
- * Returns a vector perpendicular to this vector: <i>&#x27e8;-y, x&#x27e9;</i>.
- *
- * @returns {pv.Vector} a perpendicular vector.
- */
-pv.Vector.prototype.perp = function() {
- return new pv.Vector(-this.y, this.x);
-};
-
-/**
- * Returns a normalized copy of this vector: a vector with the same direction,
- * but unit length. If this vector has zero length this method returns a copy of
- * this vector.
- *
- * @returns {pv.Vector} a unit vector.
- */
-pv.Vector.prototype.norm = function() {
- var l = this.length();
- return this.times(l ? (1 / l) : 1);
-};
-
-/**
- * Returns the magnitude of this vector, defined as <i>sqrt(x * x + y * y)</i>.
- *
- * @returns {number} a length.
- */
-pv.Vector.prototype.length = function() {
- return Math.sqrt(this.x * this.x + this.y * this.y);
-};
-
-/**
- * Returns a scaled copy of this vector: <i>&#x27e8;x * k, y * k&#x27e9;</i>.
- * To perform the equivalent divide operation, use <i>1 / k</i>.
- *
- * @param {number} k the scale factor.
- * @returns {pv.Vector} a scaled vector.
- */
-pv.Vector.prototype.times = function(k) {
- return new pv.Vector(this.x * k, this.y * k);
-};
-
-/**
- * Returns this vector plus the vector <i>v</i>: <i>&#x27e8;x + v.x, y +
- * v.y&#x27e9;</i>. If only one argument is specified, it is interpreted as the
- * vector <i>v</i>.
- *
- * @param {number} x the <i>x</i> coordinate to add.
- * @param {number} y the <i>y</i> coordinate to add.
- * @returns {pv.Vector} a new vector.
- */
-pv.Vector.prototype.plus = function(x, y) {
- return (arguments.length == 1)
- ? new pv.Vector(this.x + x.x, this.y + x.y)
- : new pv.Vector(this.x + x, this.y + y);
-};
-
-/**
- * Returns this vector minus the vector <i>v</i>: <i>&#x27e8;x - v.x, y -
- * v.y&#x27e9;</i>. If only one argument is specified, it is interpreted as the
- * vector <i>v</i>.
- *
- * @param {number} x the <i>x</i> coordinate to subtract.
- * @param {number} y the <i>y</i> coordinate to subtract.
- * @returns {pv.Vector} a new vector.
- */
-pv.Vector.prototype.minus = function(x, y) {
- return (arguments.length == 1)
- ? new pv.Vector(this.x - x.x, this.y - x.y)
- : new pv.Vector(this.x - x, this.y - y);
-};
-
-/**
- * Returns the dot product of this vector and the vector <i>v</i>: <i>x * v.x +
- * y * v.y</i>. If only one argument is specified, it is interpreted as the
- * vector <i>v</i>.
- *
- * @param {number} x the <i>x</i> coordinate to dot.
- * @param {number} y the <i>y</i> coordinate to dot.
- * @returns {number} a dot product.
- */
-pv.Vector.prototype.dot = function(x, y) {
- return (arguments.length == 1)
- ? this.x * x.x + this.y * x.y
- : this.x * x + this.y * y;
-};
-// TODO code-sharing between scales
-
-/**
- * @ignore
- * @class
- */
-pv.Scale = function() {};
-
-/**
- * @private Returns a function that interpolators from the start value to the
- * end value, given a parameter <i>t</i> in [0, 1].
- *
- * @param start the start value.
- * @param end the end value.
- */
-pv.Scale.interpolator = function(start, end) {
- if (typeof start == "number") {
- return function(t) {
- return t * (end - start) + start;
- };
- }
-
- /* For now, assume color. */
- start = pv.color(start).rgb();
- end = pv.color(end).rgb();
- return function(t) {
- var a = start.a * (1 - t) + end.a * t;
- if (a < 1e-5) a = 0; // avoid scientific notation
- return (start.a == 0) ? pv.rgb(end.r, end.g, end.b, a)
- : ((end.a == 0) ? pv.rgb(start.r, start.g, start.b, a)
- : pv.rgb(
- Math.round(start.r * (1 - t) + end.r * t),
- Math.round(start.g * (1 - t) + end.g * t),
- Math.round(start.b * (1 - t) + end.b * t), a));
- };
-};
-/**
- * Returns a linear scale for the specified domain. The arguments to this
- * constructor are optional, and equivalent to calling {@link #domain}.
- *
- * @class Represents a linear scale. <style
- * type="text/css">sub{line-height:0}</style> <img src="../linear.png"
- * width="180" height="175" align="right"> Most commonly, a linear scale
- * represents a 1-dimensional linear transformation from a numeric domain of
- * input data [<i>d<sub>0</sub></i>, <i>d<sub>1</sub></i>] to a numeric range of
- * pixels [<i>r<sub>0</sub></i>, <i>r<sub>1</sub></i>]. The equation for such a
- * scale is:
- *
- * <blockquote><i>f(x) = (x - d<sub>0</sub>) / (d<sub>1</sub> - d<sub>0</sub>) *
- * (r<sub>1</sub> - r<sub>0</sub>) + r<sub>0</sub></i></blockquote>
- *
- * For example, a linear scale from the domain [0, 100] to range [0, 640]:
- *
- * <blockquote><i>f(x) = (x - 0) / (100 - 0) * (640 - 0) + 0</i><br>
- * <i>f(x) = x / 100 * 640</i><br>
- * <i>f(x) = x * 6.4</i><br>
- * </blockquote>
- *
- * Thus, saying
- *
- * <pre>.height(function(d) d * 6.4)</pre>
- *
- * is identical to
- *
- * <pre>.height(pv.Scale.linear(0, 100).range(0, 640))</pre>
- *
- * As you can see, scales do not always make code smaller, but they should make
- * code more explicit and easier to maintain. In addition to readability, scales
- * offer several useful features:
- *
- * <p>1. The range can be expressed in colors, rather than pixels. Changing the
- * example above to
- *
- * <pre>.fillStyle(pv.Scale.linear(0, 100).range("red", "green"))</pre>
- *
- * will cause it to fill the marks "red" on an input value of 0, "green" on an
- * input value of 100, and some color in-between for intermediate values.
- *
- * <p>2. The domain and range can be subdivided for a "poly-linear"
- * transformation. For example, you may want a diverging color scale that is
- * increasingly red for negative values, and increasingly green for positive
- * values:
- *
- * <pre>.fillStyle(pv.Scale.linear(-1, 0, 1).range("red", "white", "green"))</pre>
- *
- * The domain can be specified as a series of <i>n</i> monotonically-increasing
- * values; the range must also be specified as <i>n</i> values, resulting in
- * <i>n - 1</i> contiguous linear scales.
- *
- * <p>3. Linear scales can be inverted for interaction. The {@link #invert}
- * method takes a value in the output range, and returns the corresponding value
- * in the input domain. This is frequently used to convert the mouse location
- * (see {@link pv.Mark#mouse}) to a value in the input domain. Note that
- * inversion is only supported for numeric ranges, and not colors.
- *
- * <p>4. A scale can be queried for reasonable "tick" values. The {@link #ticks}
- * method provides a convenient way to get a series of evenly-spaced rounded
- * values in the input domain. Frequently these are used in conjunction with
- * {@link pv.Rule} to display tick marks or grid lines.
- *
- * <p>5. A scale can be "niced" to extend the domain to suitable rounded
- * numbers. If the minimum and maximum of the domain are messy because they are
- * derived from data, you can use {@link #nice} to round these values down and
- * up to even numbers.
- *
- * @param {number...} domain... domain values.
- * @returns {pv.Scale.linear} a linear scale.
- */
-pv.Scale.linear = function() {
- var d = [0, 1], r = [0, 1], i = [pv.identity], precision = 0;
-
- /** @private */
- function scale(x) {
- var j = pv.search(d, x);
- if (j < 0) j = -j - 2;
- j = Math.max(0, Math.min(i.length - 1, j));
- return i[j]((x - d[j]) / (d[j + 1] - d[j]));
- }
-
- /**
- * Sets or gets the input domain. This method can be invoked several ways:
- *
- * <p>1. <tt>domain(min, ..., max)</tt>
- *
- * <p>Specifying the domain as a series of numbers is the most explicit and
- * recommended approach. Most commonly, two numbers are specified: the minimum
- * and maximum value. However, for a diverging scale, or other subdivided
- * poly-linear scales, multiple values can be specified. Values can be derived
- * from data using {@link pv.min} and {@link pv.max}. For example:
- *
- * <pre>.domain(0, pv.max(array))</pre>
- *
- * An alternative method for deriving minimum and maximum values from data
- * follows.
- *
- * <p>2. <tt>domain(array, minf, maxf)</tt>
- *
- * <p>When both the minimum and maximum value are derived from data, the
- * arguments to the <tt>domain</tt> method can be specified as the array of
- * data, followed by zero, one or two accessor functions. For example, if the
- * array of data is just an array of numbers:
- *
- * <pre>.domain(array)</pre>
- *
- * On the other hand, if the array elements are objects representing stock
- * values per day, and the domain should consider the stock's daily low and
- * daily high:
- *
- * <pre>.domain(array, function(d) d.low, function(d) d.high)</pre>
- *
- * The first method of setting the domain is preferred because it is more
- * explicit; setting the domain using this second method should be used only
- * if brevity is required.
- *
- * <p>3. <tt>domain()</tt>
- *
- * <p>Invoking the <tt>domain</tt> method with no arguments returns the
- * current domain as an array of numbers.
- *
- * @function
- * @name pv.Scale.linear.prototype.domain
- * @param {number...} domain... domain values.
- * @returns {pv.Scale.linear} <tt>this</tt>, or the current domain.
- */
- scale.domain = function(array, min, max) {
- if (arguments.length) {
- if (array instanceof Array) {
- if (arguments.length < 2) min = pv.identity;
- if (arguments.length < 3) max = min;
- d = [pv.min(array, min), pv.max(array, max)];
- } else {
- d = Array.prototype.slice.call(arguments);
- }
- return this;
- }
- return d;
- };
-
- /**
- * Sets or gets the output range. This method can be invoked several ways:
- *
- * <p>1. <tt>range(min, ..., max)</tt>
- *
- * <p>The range may be specified as a series of numbers or colors. Most
- * commonly, two numbers are specified: the minimum and maximum pixel values.
- * For a color scale, values may be specified as {@link pv.Color}s or
- * equivalent strings. For a diverging scale, or other subdivided poly-linear
- * scales, multiple values can be specified. For example:
- *
- * <pre>.range("red", "white", "green")</pre>
- *
- * <p>Currently, only numbers and colors are supported as range values. The
- * number of range values must exactly match the number of domain values, or
- * the behavior of the scale is undefined.
- *
- * <p>2. <tt>range()</tt>
- *
- * <p>Invoking the <tt>range</tt> method with no arguments returns the current
- * range as an array of numbers or colors.
- *
- * @function
- * @name pv.Scale.linear.prototype.range
- * @param {...} range... range values.
- * @returns {pv.Scale.linear} <tt>this</tt>, or the current range.
- */
- scale.range = function() {
- if (arguments.length) {
- r = Array.prototype.slice.call(arguments);
- i = [];
- for (var j = 0; j < r.length - 1; j++) {
- i.push(pv.Scale.interpolator(r[j], r[j + 1]));
- }
- return this;
- }
- return r;
- };
-
- /**
- * Inverts the specified value in the output range, returning the
- * corresponding value in the input domain. This is frequently used to convert
- * the mouse location (see {@link pv.Mark#mouse}) to a value in the input
- * domain. Inversion is only supported for numeric ranges, and not colors.
- *
- * <p>Note that this method does not do any rounding or bounds checking. If
- * the input domain is discrete (e.g., an array index), the returned value
- * should be rounded. If the specified <tt>y</tt> value is outside the range,
- * the returned value may be equivalently outside the input domain.
- *
- * @function
- * @name pv.Scale.linear.prototype.invert
- * @param {number} y a value in the output range (a pixel location).
- * @returns {number} a value in the input domain.
- */
- scale.invert = function(y) {
- var j = pv.search(r, y);
- if (j < 0) j = -j - 2;
- j = Math.max(0, Math.min(i.length - 1, j));
- return (y - r[j]) / (r[j + 1] - r[j]) * (d[j + 1] - d[j]) + d[j];
- };
-
- /**
- * Returns an array of evenly-spaced, suitably-rounded values in the input
- * domain. This method attempts to return between 5 and 10 tick values. These
- * values are frequently used in conjunction with {@link pv.Rule} to display
- * tick marks or grid lines.
- *
- * @function
- * @name pv.Scale.linear.prototype.ticks
- * @returns {number[]} an array input domain values to use as ticks.
- */
- scale.ticks = function() {
- var min = d[0],
- max = d[d.length - 1],
- span = max - min,
- step = pv.logCeil(span / 10, 10);
- if (span / step < 2) step /= 5;
- else if (span / step < 5) step /= 2;
- var start = Math.ceil(min / step) * step,
- end = Math.floor(max / step) * step;
- precision = Math.max(0, -Math.floor(pv.log(step, 10) + .01));
- return pv.range(start, end + step, step);
- };
-
- /**
- * Formats the specified tick value using the appropriate precision, based on
- * the step interval between tick marks.
- *
- * @function
- * @name pv.Scale.linear.prototype.tickFormat
- * @param {number} t a tick value.
- * @return {string} a formatted tick value.
- */
- scale.tickFormat = function(t) {
- return t.toFixed(precision);
- };
-
- /**
- * "Nices" this scale, extending the bounds of the input domain to
- * evenly-rounded values. Nicing is useful if the domain is computed
- * dynamically from data, and may be irregular. For example, given a domain of
- * [0.20147987687960267, 0.996679553296417], a call to <tt>nice()</tt> might
- * extend the domain to [0.2, 1].
- *
- * <p>This method must be invoked each time after setting the domain.
- *
- * @function
- * @name pv.Scale.linear.prototype.nice
- * @returns {pv.Scale.linear} <tt>this</tt>.
- */
- scale.nice = function() {
- var min = d[0],
- max = d[d.length - 1],
- step = Math.pow(10, Math.round(Math.log(max - min) / Math.log(10)) - 1);
- d = [Math.floor(min / step) * step, Math.ceil(max / step) * step];
- return this;
- };
-
- /**
- * Returns a view of this scale by the specified accessor function <tt>f</tt>.
- * Given a scale <tt>y</tt>, <tt>y.by(function(d) d.foo)</tt> is equivalent to
- * <tt>function(d) y(d.foo)</tt>.
- *
- * <p>This method is provided for convenience, such that scales can be
- * succinctly defined inline. For example, given an array of data elements
- * that have a <tt>score</tt> attribute with the domain [0, 1], the height
- * property could be specified as:
- *
- * <pre>.height(pv.Scale.linear().range(0, 480).by(function(d) d.score))</pre>
- *
- * This is equivalent to:
- *
- * <pre>.height(function(d) d.score * 480)</pre>
- *
- * This method should be used judiciously; it is typically more clear to
- * invoke the scale directly, passing in the value to be scaled.
- *
- * @function
- * @name pv.Scale.linear.prototype.by
- * @param {function} f an accessor function.
- * @returns {pv.Scale.linear} a view of this scale by the specified accessor
- * function.
- */
- scale.by = function(f) {
- function by() { return scale(f.apply(this, arguments)); }
- for (var method in scale) by[method] = scale[method];
- return by;
- };
-
- scale.domain.apply(scale, arguments);
- return scale;
-};
-/**
- * Returns a log scale for the specified domain. The arguments to this
- * constructor are optional, and equivalent to calling {@link #domain}.
- *
- * @class Represents a log scale. <style
- * type="text/css">sub{line-height:0}</style> <img src="../log.png"
- * width="190" height="175" align="right"> Most commonly, a log scale represents
- * a 1-dimensional log transformation from a numeric domain of input data
- * [<i>d<sub>0</sub></i>, <i>d<sub>1</sub></i>] to a numeric range of pixels
- * [<i>r<sub>0</sub></i>, <i>r<sub>1</sub></i>]. The equation for such a scale
- * is:
- *
- * <blockquote><i>f(x) = (log(x) - log(d<sub>0</sub>)) / (log(d<sub>1</sub>) -
- * log(d<sub>0</sub>)) * (r<sub>1</sub> - r<sub>0</sub>) +
- * r<sub>0</sub></i></blockquote>
- *
- * where <i>log(x)</i> represents the zero-symmetric logarthim of <i>x</i> using
- * the scale's associated base (default: 10, see {@link pv.logSymmetric}). For
- * example, a log scale from the domain [1, 100] to range [0, 640]:
- *
- * <blockquote><i>f(x) = (log(x) - log(1)) / (log(100) - log(1)) * (640 - 0) + 0</i><br>
- * <i>f(x) = log(x) / 2 * 640</i><br>
- * <i>f(x) = log(x) * 320</i><br>
- * </blockquote>
- *
- * Thus, saying
- *
- * <pre>.height(function(d) Math.log(d) * 138.974)</pre>
- *
- * is equivalent to
- *
- * <pre>.height(pv.Scale.log(1, 100).range(0, 640))</pre>
- *
- * As you can see, scales do not always make code smaller, but they should make
- * code more explicit and easier to maintain. In addition to readability, scales
- * offer several useful features:
- *
- * <p>1. The range can be expressed in colors, rather than pixels. Changing the
- * example above to
- *
- * <pre>.fillStyle(pv.Scale.log(1, 100).range("red", "green"))</pre>
- *
- * will cause it to fill the marks "red" on an input value of 1, "green" on an
- * input value of 100, and some color in-between for intermediate values.
- *
- * <p>2. The domain and range can be subdivided for a "poly-log"
- * transformation. For example, you may want a diverging color scale that is
- * increasingly red for small values, and increasingly green for large values:
- *
- * <pre>.fillStyle(pv.Scale.log(1, 10, 100).range("red", "white", "green"))</pre>
- *
- * The domain can be specified as a series of <i>n</i> monotonically-increasing
- * values; the range must also be specified as <i>n</i> values, resulting in
- * <i>n - 1</i> contiguous log scales.
- *
- * <p>3. Log scales can be inverted for interaction. The {@link #invert} method
- * takes a value in the output range, and returns the corresponding value in the
- * input domain. This is frequently used to convert the mouse location (see
- * {@link pv.Mark#mouse}) to a value in the input domain. Note that inversion is
- * only supported for numeric ranges, and not colors.
- *
- * <p>4. A scale can be queried for reasonable "tick" values. The {@link #ticks}
- * method provides a convenient way to get a series of evenly-spaced rounded
- * values in the input domain. Frequently these are used in conjunction with
- * {@link pv.Rule} to display tick marks or grid lines.
- *
- * <p>5. A scale can be "niced" to extend the domain to suitable rounded
- * numbers. If the minimum and maximum of the domain are messy because they are
- * derived from data, you can use {@link #nice} to round these values down and
- * up to even numbers.
- *
- * @param {number...} domain... domain values.
- * @returns {pv.Scale.log} a log scale.
- */
-pv.Scale.log = function() {
- var d = [1, 10], l = [0, 1], b = 10, r = [0, 1], i = [pv.identity];
-
- /** @private */
- function scale(x) {
- var j = pv.search(d, x);
- if (j < 0) j = -j - 2;
- j = Math.max(0, Math.min(i.length - 1, j));
- return i[j]((log(x) - l[j]) / (l[j + 1] - l[j]));
- }
-
- /** @private */
- function log(x) {
- return pv.logSymmetric(x, b);
- }
-
- /**
- * Sets or gets the input domain. This method can be invoked several ways:
- *
- * <p>1. <tt>domain(min, ..., max)</tt>
- *
- * <p>Specifying the domain as a series of numbers is the most explicit and
- * recommended approach. Most commonly, two numbers are specified: the minimum
- * and maximum value. However, for a diverging scale, or other subdivided
- * poly-log scales, multiple values can be specified. Values can be derived
- * from data using {@link pv.min} and {@link pv.max}. For example:
- *
- * <pre>.domain(1, pv.max(array))</pre>
- *
- * An alternative method for deriving minimum and maximum values from data
- * follows.
- *
- * <p>2. <tt>domain(array, minf, maxf)</tt>
- *
- * <p>When both the minimum and maximum value are derived from data, the
- * arguments to the <tt>domain</tt> method can be specified as the array of
- * data, followed by zero, one or two accessor functions. For example, if the
- * array of data is just an array of numbers:
- *
- * <pre>.domain(array)</pre>
- *
- * On the other hand, if the array elements are objects representing stock
- * values per day, and the domain should consider the stock's daily low and
- * daily high:
- *
- * <pre>.domain(array, function(d) d.low, function(d) d.high)</pre>
- *
- * The first method of setting the domain is preferred because it is more
- * explicit; setting the domain using this second method should be used only
- * if brevity is required.
- *
- * <p>3. <tt>domain()</tt>
- *
- * <p>Invoking the <tt>domain</tt> method with no arguments returns the
- * current domain as an array of numbers.
- *
- * @function
- * @name pv.Scale.log.prototype.domain
- * @param {number...} domain... domain values.
- * @returns {pv.Scale.log} <tt>this</tt>, or the current domain.
- */
- scale.domain = function(array, min, max) {
- if (arguments.length) {
- if (array instanceof Array) {
- if (arguments.length < 2) min = pv.identity;
- if (arguments.length < 3) max = min;
- d = [pv.min(array, min), pv.max(array, max)];
- } else {
- d = Array.prototype.slice.call(arguments);
- }
- l = d.map(log);
- return this;
- }
- return d;
- };
-
- /**
- * @function
- * @name pv.Scale.log.prototype.range
- * @param {...} range... range values.
- * @returns {pv.Scale.log} <tt>this</tt>.
- */
- scale.range = function() {
- if (arguments.length) {
- r = Array.prototype.slice.call(arguments);
- i = [];
- for (var j = 0; j < r.length - 1; j++) {
- i.push(pv.Scale.interpolator(r[j], r[j + 1]));
- }
- return this;
- }
- return r;
- };
-
- /**
- * Sets or gets the output range. This method can be invoked several ways:
- *
- * <p>1. <tt>range(min, ..., max)</tt>
- *
- * <p>The range may be specified as a series of numbers or colors. Most
- * commonly, two numbers are specified: the minimum and maximum pixel values.
- * For a color scale, values may be specified as {@link pv.Color}s or
- * equivalent strings. For a diverging scale, or other subdivided poly-log
- * scales, multiple values can be specified. For example:
- *
- * <pre>.range("red", "white", "green")</pre>
- *
- * <p>Currently, only numbers and colors are supported as range values. The
- * number of range values must exactly match the number of domain values, or
- * the behavior of the scale is undefined.
- *
- * <p>2. <tt>range()</tt>
- *
- * <p>Invoking the <tt>range</tt> method with no arguments returns the current
- * range as an array of numbers or colors.
- *
- * @function
- * @name pv.Scale.log.prototype.invert
- * @param {...} range... range values.
- * @returns {pv.Scale.log} <tt>this</tt>, or the current range.
- */
- scale.invert = function(y) {
- var j = pv.search(r, y);
- if (j < 0) j = -j - 2;
- j = Math.max(0, Math.min(i.length - 1, j));
- var t = l[j] + (y - r[j]) / (r[j + 1] - r[j]) * (l[j + 1] - l[j]);
- return (d[j] < 0) ? -Math.pow(b, -t) : Math.pow(b, t);
- };
-
- /**
- * Returns an array of evenly-spaced, suitably-rounded values in the input
- * domain. These values are frequently used in conjunction with {@link
- * pv.Rule} to display tick marks or grid lines.
- *
- * @function
- * @name pv.Scale.log.prototype.ticks
- * @returns {number[]} an array input domain values to use as ticks.
- */
- scale.ticks = function() {
- // TODO: support multiple domains
- var start = Math.floor(l[0]),
- end = Math.ceil(l[1]),
- ticks = [];
- for (var i = start; i < end; i++) {
- var x = Math.pow(b, i);
- if (d[0] < 0) x = -x;
- for (var j = 1; j < b; j++) {
- ticks.push(x * j);
- }
- }
- ticks.push(Math.pow(b, end));
- if (ticks[0] < d[0]) ticks.shift();
- if (ticks[ticks.length - 1] > d[1]) ticks.pop();
- return ticks;
- };
-
- /**
- * Formats the specified tick value using the appropriate precision, assuming
- * base 10.
- *
- * @function
- * @name pv.Scale.log.prototype.tickFormat
- * @param {number} t a tick value.
- * @return {string} a formatted tick value.
- */
- scale.tickFormat = function(t) {
- return t.toPrecision(1);
- };
-
- /**
- * "Nices" this scale, extending the bounds of the input domain to
- * evenly-rounded values. This method uses {@link pv.logFloor} and {@link
- * pv.logCeil}. Nicing is useful if the domain is computed dynamically from
- * data, and may be irregular. For example, given a domain of
- * [0.20147987687960267, 0.996679553296417], a call to <tt>nice()</tt> might
- * extend the domain to [0.1, 1].
- *
- * <p>This method must be invoked each time after setting the domain (and
- * base).
- *
- * @function
- * @name pv.Scale.log.prototype.nice
- * @returns {pv.Scale.log} <tt>this</tt>.
- */
- scale.nice = function() {
- // TODO: support multiple domains
- d = [pv.logFloor(d[0], b), pv.logCeil(d[1], b)];
- l = d.map(log);
- return this;
- };
-
- /**
- * Sets or gets the logarithm base. Defaults to 10.
- *
- * @function
- * @name pv.Scale.log.prototype.base
- * @param {number} [v] the new base.
- * @returns {pv.Scale.log} <tt>this</tt>, or the current base.
- */
- scale.base = function(v) {
- if (arguments.length) {
- b = v;
- l = d.map(log);
- return this;
- }
- return b;
- };
-
- /**
- * Returns a view of this scale by the specified accessor function <tt>f</tt>.
- * Given a scale <tt>y</tt>, <tt>y.by(function(d) d.foo)</tt> is equivalent to
- * <tt>function(d) y(d.foo)</tt>.
- *
- * <p>This method is provided for convenience, such that scales can be
- * succinctly defined inline. For example, given an array of data elements
- * that have a <tt>score</tt> attribute with the domain [0, 1], the height
- * property could be specified as:
- *
- * <pre>.height(pv.Scale.log().range(0, 480).by(function(d) d.score))</pre>
- *
- * This is equivalent to:
- *
- * <pre>.height(function(d) d.score * 480)</pre>
- *
- * This method should be used judiciously; it is typically more clear to
- * invoke the scale directly, passing in the value to be scaled.
- *
- * @function
- * @name pv.Scale.log.prototype.by
- * @param {function} f an accessor function.
- * @returns {pv.Scale.log} a view of this scale by the specified accessor
- * function.
- */
- scale.by = function(f) {
- function by() { return scale(f.apply(this, arguments)); }
- for (var method in scale) by[method] = scale[method];
- return by;
- };
-
- scale.domain.apply(scale, arguments);
- return scale;
-};
-/**
- * Returns an ordinal scale for the specified domain. The arguments to this
- * constructor are optional, and equivalent to calling {@link #domain}.
- *
- * @class Represents an ordinal scale. <style
- * type="text/css">sub{line-height:0}</style> An ordinal scale represents a
- * pairwise mapping from <i>n</i> discrete values in the input domain to
- * <i>n</i> discrete values in the output range. For example, an ordinal scale
- * might map a domain of species ["setosa", "versicolor", "virginica"] to colors
- * ["red", "green", "blue"]. Thus, saying
- *
- * <pre>.fillStyle(function(d) {
- * switch (d.species) {
- * case "setosa": return "red";
- * case "versicolor": return "green";
- * case "virginica": return "blue";
- * }
- * })</pre>
- *
- * is equivalent to
- *
- * <pre>.fillStyle(pv.Scale.ordinal("setosa", "versicolor", "virginica")
- * .range("red", "green", "blue")
- * .by(function(d) d.species))</pre>
- *
- * If the mapping from species to color does not need to be specified
- * explicitly, the domain can be omitted. In this case it will be inferred
- * lazily from the data:
- *
- * <pre>.fillStyle(pv.colors("red", "green", "blue")
- * .by(function(d) d.species))</pre>
- *
- * When the domain is inferred, the first time the scale is invoked, the first
- * element from the range will be returned. Subsequent calls with unique values
- * will return subsequent elements from the range. If the inferred domain grows
- * larger than the range, range values will be reused. However, it is strongly
- * recommended that the domain and the range contain the same number of
- * elements.
- *
- * <p>A range can be discretized from a continuous interval (e.g., for pixel
- * positioning) by using {@link #split}, {@link #splitFlush} or
- * {@link #splitBanded} after the domain has been set. For example, if
- * <tt>states</tt> is an array of the fifty U.S. state names, the state name can
- * be encoded in the left position:
- *
- * <pre>.left(pv.Scale.ordinal(states)
- * .split(0, 640)
- * .by(function(d) d.state))</pre>
- *
- * <p>N.B.: ordinal scales are not invertible (at least not yet), since the
- * domain and range and discontinuous. A workaround is to use a linear scale.
- *
- * @param {...} domain... domain values.
- * @returns {pv.Scale.ordinal} an ordinal scale.
- * @see pv.colors
- */
-pv.Scale.ordinal = function() {
- var d = [], i = {}, r = [], band = 0;
-
- /** @private */
- function scale(x) {
- if (!(x in i)) i[x] = d.push(x) - 1;
- return r[i[x] % r.length];
- }
-
- /**
- * Sets or gets the input domain. This method can be invoked several ways:
- *
- * <p>1. <tt>domain(values...)</tt>
- *
- * <p>Specifying the domain as a series of values is the most explicit and
- * recommended approach. However, if the domain values are derived from data,
- * you may find the second method more appropriate.
- *
- * <p>2. <tt>domain(array, f)</tt>
- *
- * <p>Rather than enumerating the domain values as explicit arguments to this
- * method, you can specify a single argument of an array. In addition, you can
- * specify an optional accessor function to extract the domain values from the
- * array.
- *
- * <p>3. <tt>domain()</tt>
- *
- * <p>Invoking the <tt>domain</tt> method with no arguments returns the
- * current domain as an array.
- *
- * @function
- * @name pv.Scale.ordinal.prototype.domain
- * @param {...} domain... domain values.
- * @returns {pv.Scale.ordinal} <tt>this</tt>, or the current domain.
- */
- scale.domain = function(array, f) {
- if (arguments.length) {
- array = (array instanceof Array)
- ? ((arguments.length > 1) ? map(array, f) : array)
- : Array.prototype.slice.call(arguments);
-
- /* Filter the specified ordinals to their unique values. */
- d = [];
- var seen = {};
- for (var j = 0; j < array.length; j++) {
- var o = array[j];
- if (!(o in seen)) {
- seen[o] = true;
- d.push(o);
- }
- }
-
- i = pv.numerate(d);
- return this;
- }
- return d;
- };
-
- /**
- * Sets or gets the output range. This method can be invoked several ways:
- *
- * <p>1. <tt>range(values...)</tt>
- *
- * <p>Specifying the range as a series of values is the most explicit and
- * recommended approach. However, if the range values are derived from data,
- * you may find the second method more appropriate.
- *
- * <p>2. <tt>range(array, f)</tt>
- *
- * <p>Rather than enumerating the range values as explicit arguments to this
- * method, you can specify a single argument of an array. In addition, you can
- * specify an optional accessor function to extract the range values from the
- * array.
- *
- * <p>3. <tt>range()</tt>
- *
- * <p>Invoking the <tt>range</tt> method with no arguments returns the
- * current range as an array.
- *
- * @function
- * @name pv.Scale.ordinal.prototype.range
- * @param {...} range... range values.
- * @returns {pv.Scale.ordinal} <tt>this</tt>, or the current range.
- */
- scale.range = function(array, f) {
- if (arguments.length) {
- r = (array instanceof Array)
- ? ((arguments.length > 1) ? map(array, f) : array)
- : Array.prototype.slice.call(arguments);
- if (typeof r[0] == "string") r = r.map(pv.color);
- return this;
- }
- return r;
- };
-
- /**
- * Sets the range from the given continuous interval. The interval
- * [<i>min</i>, <i>max</i>] is subdivided into <i>n</i> equispaced points,
- * where <i>n</i> is the number of (unique) values in the domain. The first
- * and last point are offset from the edge of the range by half the distance
- * between points.
- *
- * <p>This method must be called <i>after</i> the domain is set.
- *
- * @function
- * @name pv.Scale.ordinal.prototype.split
- * @param {number} min minimum value of the output range.
- * @param {number} max maximum value of the output range.
- * @returns {pv.Scale.ordinal} <tt>this</tt>.
- * @see #splitFlush
- * @see #splitBanded
- */
- scale.split = function(min, max) {
- var step = (max - min) / this.domain().length;
- r = pv.range(min + step / 2, max, step);
- return this;
- };
-
- /**
- * Sets the range from the given continuous interval. The interval
- * [<i>min</i>, <i>max</i>] is subdivided into <i>n</i> equispaced points,
- * where <i>n</i> is the number of (unique) values in the domain. The first
- * and last point are exactly on the edge of the range.
- *
- * <p>This method must be called <i>after</i> the domain is set.
- *
- * @function
- * @name pv.Scale.ordinal.prototype.splitFlush
- * @param {number} min minimum value of the output range.
- * @param {number} max maximum value of the output range.
- * @returns {pv.Scale.ordinal} <tt>this</tt>.
- * @see #split
- */
- scale.splitFlush = function(min, max) {
- var n = this.domain().length, step = (max - min) / (n - 1);
- r = (n == 1) ? [(min + max) / 2]
- : pv.range(min, max + step / 2, step);
- return this;
- };
-
- /**
- * Sets the range from the given continuous interval. The interval
- * [<i>min</i>, <i>max</i>] is subdivided into <i>n</i> equispaced bands,
- * where <i>n</i> is the number of (unique) values in the domain. The first
- * and last band are offset from the edge of the range by the distance between
- * bands.
- *
- * <p>The band width argument, <tt>band</tt>, is typically in the range [0, 1]
- * and defaults to 1. This fraction corresponds to the amount of space in the
- * range to allocate to the bands, as opposed to padding. A value of 0.5 means
- * that the band width will be equal to the padding width. The computed
- * absolute band width can be retrieved from the range as
- * <tt>scale.range().band</tt>.
- *
- * <p>If the band width argument is negative, this method will allocate bands
- * of a <i>fixed</i> width <tt>-band</tt>, rather than a relative fraction of
- * the available space.
- *
- * <p>Tip: to inset the bands by a fixed amount <tt>p</tt>, specify a minimum
- * value of <tt>min + p</tt> (or simply <tt>p</tt>, if <tt>min</tt> is
- * 0). Then set the mark width to <tt>scale.range().band - p</tt>.
- *
- * <p>This method must be called <i>after</i> the domain is set.
- *
- * @function
- * @name pv.Scale.ordinal.prototype.splitBanded
- * @param {number} min minimum value of the output range.
- * @param {number} max maximum value of the output range.
- * @param {number} [band] the fractional band width in [0, 1]; defaults to 1.
- * @returns {pv.Scale.ordinal} <tt>this</tt>.
- * @see #split
- */
- scale.splitBanded = function(min, max, band) {
- if (arguments.length < 3) band = 1;
- if (band < 0) {
- var n = this.domain().length,
- total = -band * n,
- remaining = max - min - total,
- padding = remaining / (n + 1);
- r = pv.range(min + padding, max, padding - band);
- r.band = -band;
- } else {
- var step = (max - min) / (this.domain().length + (1 - band));
- r = pv.range(min + step * (1 - band), max, step);
- r.band = step * band;
- }
- return this;
- };
-
- /**
- * Returns a view of this scale by the specified accessor function <tt>f</tt>.
- * Given a scale <tt>y</tt>, <tt>y.by(function(d) d.foo)</tt> is equivalent to
- * <tt>function(d) y(d.foo)</tt>. This method should be used judiciously; it
- * is typically more clear to invoke the scale directly, passing in the value
- * to be scaled.
- *
- * @function
- * @name pv.Scale.ordinal.prototype.by
- * @param {function} f an accessor function.
- * @returns {pv.Scale.ordinal} a view of this scale by the specified accessor
- * function.
- */
- scale.by = function(f) {
- function by() { return scale(f.apply(this, arguments)); }
- for (var method in scale) by[method] = scale[method];
- return by;
- };
-
- scale.domain.apply(scale, arguments);
- return scale;
-};
-/**
- * Returns the {@link pv.Color} for the specified color format string. Colors
- * may have an associated opacity, or alpha channel. Color formats are specified
- * by CSS Color Modular Level 3, using either in RGB or HSL color space. For
- * example:<ul>
- *
- * <li>#f00 // #rgb
- * <li>#ff0000 // #rrggbb
- * <li>rgb(255, 0, 0)
- * <li>rgb(100%, 0%, 0%)
- * <li>hsl(0, 100%, 50%)
- * <li>rgba(0, 0, 255, 0.5)
- * <li>hsla(120, 100%, 50%, 1)
- *
- * </ul>The SVG 1.0 color keywords names are also supported, such as "aliceblue"
- * and "yellowgreen". The "transparent" keyword is supported for a
- * fully-transparent color.
- *
- * <p>If the <tt>format</tt> argument is already an instance of <tt>Color</tt>,
- * the argument is returned with no further processing.
- *
- * @param {string} format the color specification string, such as "#f00".
- * @returns {pv.Color} the corresponding <tt>Color</tt>.
- * @see <a href="http://www.w3.org/TR/SVG/types.html#ColorKeywords">SVG color
- * keywords</a>
- * @see <a href="http://www.w3.org/TR/css3-color/">CSS3 color module</a>
- */
-pv.color = function(format) {
- if (!format || (format == "transparent")) {
- return pv.rgb(0, 0, 0, 0);
- }
- if (format instanceof pv.Color) {
- return format;
- }
-
- /* Handle hsl, rgb. */
- var m1 = /([a-z]+)\((.*)\)/i.exec(format);
- if (m1) {
- var m2 = m1[2].split(","), a = 1;
- switch (m1[1]) {
- case "hsla":
- case "rgba": {
- a = parseFloat(m2[3]);
- break;
- }
- }
- switch (m1[1]) {
- case "hsla":
- case "hsl": {
- var h = parseFloat(m2[0]), // degrees
- s = parseFloat(m2[1]) / 100, // percentage
- l = parseFloat(m2[2]) / 100; // percentage
- return (new pv.Color.Hsl(h, s, l, a)).rgb();
- }
- case "rgba":
- case "rgb": {
- function parse(c) { // either integer or percentage
- var f = parseFloat(c);
- return (c[c.length - 1] == '%') ? Math.round(f * 2.55) : f;
- }
- var r = parse(m2[0]), g = parse(m2[1]), b = parse(m2[2]);
- return pv.rgb(r, g, b, a);
- }
- }
- }
-
- /* Named colors. */
- format = pv.Color.names[format] || format;
-
- /* Hexadecimal colors: #rgb and #rrggbb. */
- if (format.charAt(0) == "#") {
- var r, g, b;
- if (format.length == 4) {
- r = format.charAt(1); r += r;
- g = format.charAt(2); g += g;
- b = format.charAt(3); b += b;
- } else if (format.length == 7) {
- r = format.substring(1, 3);
- g = format.substring(3, 5);
- b = format.substring(5, 7);
- }
- return pv.rgb(parseInt(r, 16), parseInt(g, 16), parseInt(b, 16), 1);
- }
-
- /* Otherwise, assume named colors. TODO allow lazy conversion to RGB. */
- return new pv.Color(format, 1);
-};
-
-/**
- * Constructs a color with the specified color format string and opacity. This
- * constructor should not be invoked directly; use {@link pv.color} instead.
- *
- * @class Represents an abstract (possibly translucent) color. The color is
- * divided into two parts: the <tt>color</tt> attribute, an opaque color format
- * string, and the <tt>opacity</tt> attribute, a float in [0, 1]. The color
- * space is dependent on the implementing class; all colors support the
- * {@link #rgb} method to convert to RGB color space for interpolation.
- *
- * <p>See also the <a href="../../api/Color.html">Color guide</a>.
- *
- * @param {string} color an opaque color format string, such as "#f00".
- * @param {number} opacity the opacity, in [0,1].
- * @see pv.color
- */
-pv.Color = function(color, opacity) {
- /**
- * An opaque color format string, such as "#f00".
- *
- * @type string
- * @see <a href="http://www.w3.org/TR/SVG/types.html#ColorKeywords">SVG color
- * keywords</a>
- * @see <a href="http://www.w3.org/TR/css3-color/">CSS3 color module</a>
- */
- this.color = color;
-
- /**
- * The opacity, a float in [0, 1].
- *
- * @type number
- */
- this.opacity = opacity;
-};
-
-/**
- * Returns a new color that is a brighter version of this color. The behavior of
- * this method may vary slightly depending on the underlying color space.
- * Although brighter and darker are inverse operations, the results of a series
- * of invocations of these two methods might be inconsistent because of rounding
- * errors.
- *
- * @param [k] {number} an optional scale factor; defaults to 1.
- * @see #darker
- * @returns {pv.Color} a brighter color.
- */
-pv.Color.prototype.brighter = function(k) {
- return this.rgb().brighter(k);
-};
-
-/**
- * Returns a new color that is a brighter version of this color. The behavior of
- * this method may vary slightly depending on the underlying color space.
- * Although brighter and darker are inverse operations, the results of a series
- * of invocations of these two methods might be inconsistent because of rounding
- * errors.
- *
- * @param [k] {number} an optional scale factor; defaults to 1.
- * @see #brighter
- * @returns {pv.Color} a darker color.
- */
-pv.Color.prototype.darker = function(k) {
- return this.rgb().darker(k);
-};
-
-/**
- * Constructs a new RGB color with the specified channel values.
- *
- * @param {number} r the red channel, an integer in [0,255].
- * @param {number} g the green channel, an integer in [0,255].
- * @param {number} b the blue channel, an integer in [0,255].
- * @param {number} [a] the alpha channel, a float in [0,1].
- * @returns pv.Color.Rgb
- */
-pv.rgb = function(r, g, b, a) {
- return new pv.Color.Rgb(r, g, b, (arguments.length == 4) ? a : 1);
-};
-
-/**
- * Constructs a new RGB color with the specified channel values.
- *
- * @class Represents a color in RGB space.
- *
- * @param {number} r the red channel, an integer in [0,255].
- * @param {number} g the green channel, an integer in [0,255].
- * @param {number} b the blue channel, an integer in [0,255].
- * @param {number} a the alpha channel, a float in [0,1].
- * @extends pv.Color
- */
-pv.Color.Rgb = function(r, g, b, a) {
- pv.Color.call(this, a ? ("rgb(" + r + "," + g + "," + b + ")") : "none", a);
-
- /**
- * The red channel, an integer in [0, 255].
- *
- * @type number
- */
- this.r = r;
-
- /**
- * The green channel, an integer in [0, 255].
- *
- * @type number
- */
- this.g = g;
-
- /**
- * The blue channel, an integer in [0, 255].
- *
- * @type number
- */
- this.b = b;
-
- /**
- * The alpha channel, a float in [0, 1].
- *
- * @type number
- */
- this.a = a;
-};
-pv.Color.Rgb.prototype = pv.extend(pv.Color);
-
-/**
- * Constructs a new RGB color with the same green, blue and alpha channels as
- * this color, with the specified red channel.
- *
- * @param {number} r the red channel, an integer in [0,255].
- */
-pv.Color.Rgb.prototype.red = function(r) {
- return pv.rgb(r, this.g, this.b, this.a);
-};
-
-/**
- * Constructs a new RGB color with the same red, blue and alpha channels as this
- * color, with the specified green channel.
- *
- * @param {number} g the green channel, an integer in [0,255].
- */
-pv.Color.Rgb.prototype.green = function(g) {
- return pv.rgb(this.r, g, this.b, this.a);
-};
-
-/**
- * Constructs a new RGB color with the same red, green and alpha channels as
- * this color, with the specified blue channel.
- *
- * @param {number} b the blue channel, an integer in [0,255].
- */
-pv.Color.Rgb.prototype.blue = function(b) {
- return pv.rgb(this.r, this.g, b, this.a);
-};
-
-/**
- * Constructs a new RGB color with the same red, green and blue channels as this
- * color, with the specified alpha channel.
- *
- * @param {number} a the alpha channel, a float in [0,1].
- */
-pv.Color.Rgb.prototype.alpha = function(a) {
- return pv.rgb(this.r, this.g, this.b, a);
-};
-
-/**
- * Returns the RGB color equivalent to this color. This method is abstract and
- * must be implemented by subclasses.
- *
- * @returns {pv.Color.Rgb} an RGB color.
- * @function
- * @name pv.Color.prototype.rgb
- */
-
-/**
- * Returns this.
- *
- * @returns {pv.Color.Rgb} this.
- */
-pv.Color.Rgb.prototype.rgb = function() { return this; };
-
-/**
- * Returns a new color that is a brighter version of this color. This method
- * applies an arbitrary scale factor to each of the three RGB components of this
- * color to create a brighter version of this color. Although brighter and
- * darker are inverse operations, the results of a series of invocations of
- * these two methods might be inconsistent because of rounding errors.
- *
- * @param [k] {number} an optional scale factor; defaults to 1.
- * @see #darker
- * @returns {pv.Color.Rgb} a brighter color.
- */
-pv.Color.Rgb.prototype.brighter = function(k) {
- k = Math.pow(0.7, arguments.length ? k : 1);
- var r = this.r, g = this.g, b = this.b, i = 30;
- if (!r && !g && !b) return pv.rgb(i, i, i, this.a);
- if (r && (r < i)) r = i;
- if (g && (g < i)) g = i;
- if (b && (b < i)) b = i;
- return pv.rgb(
- Math.min(255, Math.floor(r / k)),
- Math.min(255, Math.floor(g / k)),
- Math.min(255, Math.floor(b / k)),
- this.a);
-};
-
-/**
- * Returns a new color that is a darker version of this color. This method
- * applies an arbitrary scale factor to each of the three RGB components of this
- * color to create a darker version of this color. Although brighter and darker
- * are inverse operations, the results of a series of invocations of these two
- * methods might be inconsistent because of rounding errors.
- *
- * @param [k] {number} an optional scale factor; defaults to 1.
- * @see #brighter
- * @returns {pv.Color.Rgb} a darker color.
- */
-pv.Color.Rgb.prototype.darker = function(k) {
- k = Math.pow(0.7, arguments.length ? k : 1);
- return pv.rgb(
- Math.max(0, Math.floor(k * this.r)),
- Math.max(0, Math.floor(k * this.g)),
- Math.max(0, Math.floor(k * this.b)),
- this.a);
-};
-
-/**
- * Constructs a new HSL color with the specified values.
- *
- * @param {number} h the hue, an integer in [0, 360].
- * @param {number} s the saturation, a float in [0, 1].
- * @param {number} l the lightness, a float in [0, 1].
- * @param {number} [a] the opacity, a float in [0, 1].
- * @returns pv.Color.Hsl
- */
-pv.hsl = function(h, s, l, a) {
- return new pv.Color.Hsl(h, s, l, (arguments.length == 4) ? a : 1);
-};
-
-/**
- * Constructs a new HSL color with the specified values.
- *
- * @class Represents a color in HSL space.
- *
- * @param {number} h the hue, an integer in [0, 360].
- * @param {number} s the saturation, a float in [0, 1].
- * @param {number} l the lightness, a float in [0, 1].
- * @param {number} a the opacity, a float in [0, 1].
- * @extends pv.Color
- */
-pv.Color.Hsl = function(h, s, l, a) {
- pv.Color.call(this, "hsl(" + h + "," + (s * 100) + "%," + (l * 100) + "%)", a);
-
- /**
- * The hue, an integer in [0, 360].
- *
- * @type number
- */
- this.h = h;
-
- /**
- * The saturation, a float in [0, 1].
- *
- * @type number
- */
- this.s = s;
-
- /**
- * The lightness, a float in [0, 1].
- *
- * @type number
- */
- this.l = l;
-
- /**
- * The opacity, a float in [0, 1].
- *
- * @type number
- */
- this.a = a;
-};
-pv.Color.Hsl.prototype = pv.extend(pv.Color);
-
-/**
- * Constructs a new HSL color with the same saturation, lightness and alpha as
- * this color, and the specified hue.
- *
- * @param {number} h the hue, an integer in [0, 360].
- */
-pv.Color.Hsl.prototype.hue = function(h) {
- return pv.hsl(h, this.s, this.l, this.a);
-};
-
-/**
- * Constructs a new HSL color with the same hue, lightness and alpha as this
- * color, and the specified saturation.
- *
- * @param {number} s the saturation, a float in [0, 1].
- */
-pv.Color.Hsl.prototype.saturation = function(s) {
- return pv.hsl(this.h, s, this.l, this.a);
-};
-
-/**
- * Constructs a new HSL color with the same hue, saturation and alpha as this
- * color, and the specified lightness.
- *
- * @param {number} l the lightness, a float in [0, 1].
- */
-pv.Color.Hsl.prototype.lightness = function(l) {
- return pv.hsl(this.h, this.s, l, this.a);
-};
-
-/**
- * Constructs a new HSL color with the same hue, saturation and lightness as
- * this color, and the specified alpha.
- *
- * @param {number} a the opacity, a float in [0, 1].
- */
-pv.Color.Hsl.prototype.alpha = function(a) {
- return pv.hsl(this.h, this.s, this.l, a);
-};
-
-/**
- * Returns the RGB color equivalent to this HSL color.
- *
- * @returns {pv.Color.Rgb} an RGB color.
- */
-pv.Color.Hsl.prototype.rgb = function() {
- var h = this.h, s = this.s, l = this.l;
-
- /* Some simple corrections for h, s and l. */
- h = h % 360; if (h < 0) h += 360;
- s = Math.max(0, Math.min(s, 1));
- l = Math.max(0, Math.min(l, 1));
-
- /* From FvD 13.37, CSS Color Module Level 3 */
- var m2 = (l <= .5) ? (l * (1 + s)) : (l + s - l * s);
- var m1 = 2 * l - m2;
- function v(h) {
- if (h > 360) h -= 360;
- else if (h < 0) h += 360;
- if (h < 60) return m1 + (m2 - m1) * h / 60;
- if (h < 180) return m2;
- if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60;
- return m1;
- }
- function vv(h) {
- return Math.round(v(h) * 255);
- }
-
- return pv.rgb(vv(h + 120), vv(h), vv(h - 120), this.a);
-};
-
-/**
- * @private SVG color keywords, per CSS Color Module Level 3.
- *
- * @see <a href="http://www.w3.org/TR/SVG/types.html#ColorKeywords">SVG color
- * keywords</a>
- */
-pv.Color.names = {
- aliceblue: "#f0f8ff",
- antiquewhite: "#faebd7",
- aqua: "#00ffff",
- aquamarine: "#7fffd4",
- azure: "#f0ffff",
- beige: "#f5f5dc",
- bisque: "#ffe4c4",
- black: "#000000",
- blanchedalmond: "#ffebcd",
- blue: "#0000ff",
- blueviolet: "#8a2be2",
- brown: "#a52a2a",
- burlywood: "#deb887",
- cadetblue: "#5f9ea0",
- chartreuse: "#7fff00",
- chocolate: "#d2691e",
- coral: "#ff7f50",
- cornflowerblue: "#6495ed",
- cornsilk: "#fff8dc",
- crimson: "#dc143c",
- cyan: "#00ffff",
- darkblue: "#00008b",
- darkcyan: "#008b8b",
- darkgoldenrod: "#b8860b",
- darkgray: "#a9a9a9",
- darkgreen: "#006400",
- darkgrey: "#a9a9a9",
- darkkhaki: "#bdb76b",
- darkmagenta: "#8b008b",
- darkolivegreen: "#556b2f",
- darkorange: "#ff8c00",
- darkorchid: "#9932cc",
- darkred: "#8b0000",
- darksalmon: "#e9967a",
- darkseagreen: "#8fbc8f",
- darkslateblue: "#483d8b",
- darkslategray: "#2f4f4f",
- darkslategrey: "#2f4f4f",
- darkturquoise: "#00ced1",
- darkviolet: "#9400d3",
- deeppink: "#ff1493",
- deepskyblue: "#00bfff",
- dimgray: "#696969",
- dimgrey: "#696969",
- dodgerblue: "#1e90ff",
- firebrick: "#b22222",
- floralwhite: "#fffaf0",
- forestgreen: "#228b22",
- fuchsia: "#ff00ff",
- gainsboro: "#dcdcdc",
- ghostwhite: "#f8f8ff",
- gold: "#ffd700",
- goldenrod: "#daa520",
- gray: "#808080",
- green: "#008000",
- greenyellow: "#adff2f",
- grey: "#808080",
- honeydew: "#f0fff0",
- hotpink: "#ff69b4",
- indianred: "#cd5c5c",
- indigo: "#4b0082",
- ivory: "#fffff0",
- khaki: "#f0e68c",
- lavender: "#e6e6fa",
- lavenderblush: "#fff0f5",
- lawngreen: "#7cfc00",
- lemonchiffon: "#fffacd",
- lightblue: "#add8e6",
- lightcoral: "#f08080",
- lightcyan: "#e0ffff",
- lightgoldenrodyellow: "#fafad2",
- lightgray: "#d3d3d3",
- lightgreen: "#90ee90",
- lightgrey: "#d3d3d3",
- lightpink: "#ffb6c1",
- lightsalmon: "#ffa07a",
- lightseagreen: "#20b2aa",
- lightskyblue: "#87cefa",
- lightslategray: "#778899",
- lightslategrey: "#778899",
- lightsteelblue: "#b0c4de",
- lightyellow: "#ffffe0",
- lime: "#00ff00",
- limegreen: "#32cd32",
- linen: "#faf0e6",
- magenta: "#ff00ff",
- maroon: "#800000",
- mediumaquamarine: "#66cdaa",
- mediumblue: "#0000cd",
- mediumorchid: "#ba55d3",
- mediumpurple: "#9370db",
- mediumseagreen: "#3cb371",
- mediumslateblue: "#7b68ee",
- mediumspringgreen: "#00fa9a",
- mediumturquoise: "#48d1cc",
- mediumvioletred: "#c71585",
- midnightblue: "#191970",
- mintcream: "#f5fffa",
- mistyrose: "#ffe4e1",
- moccasin: "#ffe4b5",
- navajowhite: "#ffdead",
- navy: "#000080",
- oldlace: "#fdf5e6",
- olive: "#808000",
- olivedrab: "#6b8e23",
- orange: "#ffa500",
- orangered: "#ff4500",
- orchid: "#da70d6",
- palegoldenrod: "#eee8aa",
- palegreen: "#98fb98",
- paleturquoise: "#afeeee",
- palevioletred: "#db7093",
- papayawhip: "#ffefd5",
- peachpuff: "#ffdab9",
- peru: "#cd853f",
- pink: "#ffc0cb",
- plum: "#dda0dd",
- powderblue: "#b0e0e6",
- purple: "#800080",
- red: "#ff0000",
- rosybrown: "#bc8f8f",
- royalblue: "#4169e1",
- saddlebrown: "#8b4513",
- salmon: "#fa8072",
- sandybrown: "#f4a460",
- seagreen: "#2e8b57",
- seashell: "#fff5ee",
- sienna: "#a0522d",
- silver: "#c0c0c0",
- skyblue: "#87ceeb",
- slateblue: "#6a5acd",
- slategray: "#708090",
- slategrey: "#708090",
- snow: "#fffafa",
- springgreen: "#00ff7f",
- steelblue: "#4682b4",
- tan: "#d2b48c",
- teal: "#008080",
- thistle: "#d8bfd8",
- tomato: "#ff6347",
- turquoise: "#40e0d0",
- violet: "#ee82ee",
- wheat: "#f5deb3",
- white: "#ffffff",
- whitesmoke: "#f5f5f5",
- yellow: "#ffff00",
- yellowgreen: "#9acd32"
-};
-/**
- * Returns a new categorical color encoding using the specified colors. The
- * arguments to this method are an array of colors; see {@link pv.color}. For
- * example, to create a categorical color encoding using the <tt>species</tt>
- * attribute:
- *
- * <pre>pv.colors("red", "green", "blue").by(function(d) d.species)</pre>
- *
- * The result of this expression can be used as a fill- or stroke-style
- * property. This assumes that the data's <tt>species</tt> attribute is a
- * string.
- *
- * @param {string} colors... categorical colors.
- * @see pv.Scale.ordinal
- * @returns {pv.Scale.ordinal} an ordinal color scale.
- */
-pv.colors = function() {
- var scale = pv.Scale.ordinal();
- scale.range.apply(scale, arguments);
- return scale;
-};
-
-/**
- * A collection of standard color palettes for categorical encoding.
- *
- * @namespace A collection of standard color palettes for categorical encoding.
- */
-pv.Colors = {};
-
-/**
- * Returns a new 10-color scheme. The arguments to this constructor are
- * optional, and equivalent to calling {@link pv.Scale.OrdinalScale#domain}. The
- * following colors are used:
- *
- * <div style="background:#1f77b4;">#1f77b4</div>
- * <div style="background:#ff7f0e;">#ff7f0e</div>
- * <div style="background:#2ca02c;">#2ca02c</div>
- * <div style="background:#d62728;">#d62728</div>
- * <div style="background:#9467bd;">#9467bd</div>
- * <div style="background:#8c564b;">#8c564b</div>
- * <div style="background:#e377c2;">#e377c2</div>
- * <div style="background:#7f7f7f;">#7f7f7f</div>
- * <div style="background:#bcbd22;">#bcbd22</div>
- * <div style="background:#17becf;">#17becf</div>
- *
- * @param {number...} domain... domain values.
- * @returns {pv.Scale.ordinal} a new ordinal color scale.
- * @see pv.color
- */
-pv.Colors.category10 = function() {
- var scale = pv.colors(
- "#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd",
- "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf");
- scale.domain.apply(scale, arguments);
- return scale;
-};
-
-/**
- * Returns a new 20-color scheme. The arguments to this constructor are
- * optional, and equivalent to calling {@link pv.Scale.OrdinalScale#domain}. The
- * following colors are used:
- *
- * <div style="background:#1f77b4;">#1f77b4</div>
- * <div style="background:#aec7e8;">#aec7e8</div>
- * <div style="background:#ff7f0e;">#ff7f0e</div>
- * <div style="background:#ffbb78;">#ffbb78</div>
- * <div style="background:#2ca02c;">#2ca02c</div>
- * <div style="background:#98df8a;">#98df8a</div>
- * <div style="background:#d62728;">#d62728</div>
- * <div style="background:#ff9896;">#ff9896</div>
- * <div style="background:#9467bd;">#9467bd</div>
- * <div style="background:#c5b0d5;">#c5b0d5</div>
- * <div style="background:#8c564b;">#8c564b</div>
- * <div style="background:#c49c94;">#c49c94</div>
- * <div style="background:#e377c2;">#e377c2</div>
- * <div style="background:#f7b6d2;">#f7b6d2</div>
- * <div style="background:#7f7f7f;">#7f7f7f</div>
- * <div style="background:#c7c7c7;">#c7c7c7</div>
- * <div style="background:#bcbd22;">#bcbd22</div>
- * <div style="background:#dbdb8d;">#dbdb8d</div>
- * <div style="background:#17becf;">#17becf</div>
- * <div style="background:#9edae5;">#9edae5</div>
- *
- * @param {number...} domain... domain values.
- * @returns {pv.Scale.ordinal} a new ordinal color scale.
- * @see pv.color
-*/
-pv.Colors.category20 = function() {
- var scale = pv.colors(
- "#1f77b4", "#aec7e8", "#ff7f0e", "#ffbb78", "#2ca02c",
- "#98df8a", "#d62728", "#ff9896", "#9467bd", "#c5b0d5",
- "#8c564b", "#c49c94", "#e377c2", "#f7b6d2", "#7f7f7f",
- "#c7c7c7", "#bcbd22", "#dbdb8d", "#17becf", "#9edae5");
- scale.domain.apply(scale, arguments);
- return scale;
-};
-
-/**
- * Returns a new alternative 19-color scheme. The arguments to this constructor
- * are optional, and equivalent to calling
- * {@link pv.Scale.OrdinalScale#domain}. The following colors are used:
- *
- * <div style="background:#9c9ede;">#9c9ede</div>
- * <div style="background:#7375b5;">#7375b5</div>
- * <div style="background:#4a5584;">#4a5584</div>
- * <div style="background:#cedb9c;">#cedb9c</div>
- * <div style="background:#b5cf6b;">#b5cf6b</div>
- * <div style="background:#8ca252;">#8ca252</div>
- * <div style="background:#637939;">#637939</div>
- * <div style="background:#e7cb94;">#e7cb94</div>
- * <div style="background:#e7ba52;">#e7ba52</div>
- * <div style="background:#bd9e39;">#bd9e39</div>
- * <div style="background:#8c6d31;">#8c6d31</div>
- * <div style="background:#e7969c;">#e7969c</div>
- * <div style="background:#d6616b;">#d6616b</div>
- * <div style="background:#ad494a;">#ad494a</div>
- * <div style="background:#843c39;">#843c39</div>
- * <div style="background:#de9ed6;">#de9ed6</div>
- * <div style="background:#ce6dbd;">#ce6dbd</div>
- * <div style="background:#a55194;">#a55194</div>
- * <div style="background:#7b4173;">#7b4173</div>
- *
- * @param {number...} domain... domain values.
- * @returns {pv.Scale.ordinal} a new ordinal color scale.
- * @see pv.color
- */
-pv.Colors.category19 = function() {
- var scale = pv.colors(
- "#9c9ede", "#7375b5", "#4a5584", "#cedb9c", "#b5cf6b",
- "#8ca252", "#637939", "#e7cb94", "#e7ba52", "#bd9e39",
- "#8c6d31", "#e7969c", "#d6616b", "#ad494a", "#843c39",
- "#de9ed6", "#ce6dbd", "#a55194", "#7b4173");
- scale.domain.apply(scale, arguments);
- return scale;
-};
-/**
- * Returns a linear color ramp from the specified <tt>start</tt> color to the
- * specified <tt>end</tt> color. The color arguments may be specified either as
- * <tt>string</tt>s or as {@link pv.Color}s.
- *
- * @param {string} start the start color; may be a <tt>pv.Color</tt>.
- * @param {string} end the end color; may be a <tt>pv.Color</tt>.
- * @returns {Function} a color ramp from <tt>start</tt> to <tt>end</tt>.
- * @see pv.Scale.linear
- */
-pv.ramp = function(start, end) {
- var scale = pv.Scale.linear();
- scale.range.apply(scale, arguments);
- return scale;
-};
-// TODO don't populate default attributes?
-
-/**
- * @private
- * @namespace
- */
-pv.Scene = pv.SvgScene = {};
-
-/**
- * Updates the display for the specified array of scene nodes.
- *
- * @param scenes {array} an array of scene nodes.
- */
-pv.SvgScene.updateAll = function(scenes) {
- if (!scenes.length) return;
- if ((scenes[0].reverse)
- && (scenes.type != "line")
- && (scenes.type != "area")) {
- var reversed = pv.extend(scenes);
- for (var i = 0, j = scenes.length - 1; j >= 0; i++, j--) {
- reversed[i] = scenes[j];
- }
- scenes = reversed;
- }
- this.removeSiblings(this[scenes.type](scenes));
-};
-
-/**
- * Creates a new SVG element of the specified type.
- *
- * @param type {string} an SVG element type, such as "rect".
- * @return a new SVG element.
- */
-pv.SvgScene.create = function(type) {
- return document.createElementNS(pv.ns.svg, type);
-};
-
-/**
- * Expects the element <i>e</i> to be the specified type. If the element does
- * not exist, a new one is created. If the element does exist but is the wrong
- * type, it is replaced with the specified element.
- *
- * @param type {string} an SVG element type, such as "rect".
- * @return a new SVG element.
- */
-pv.SvgScene.expect = function(type, e) {
- if (!e) return this.create(type);
- if (e.tagName == "a") e = e.firstChild;
- if (e.tagName == type) return e;
- var n = this.create(type);
- e.parentNode.replaceChild(n, e);
- return n;
-};
-
-/** TODO */
-pv.SvgScene.append = function(e, scenes, index) {
- e.$scene = {scenes:scenes, index:index};
- e = this.title(e, scenes[index]);
- if (!e.parentNode) scenes.$g.appendChild(e);
- return e.nextSibling;
-};
-
-/**
- * Applies a title tooltip to the specified element <tt>e</tt>, using the
- * <tt>title</tt> property of the specified scene node <tt>s</tt>. Note that
- * this implementation does not create an SVG <tt>title</tt> element as a child
- * of <tt>e</tt>; although this is the recommended standard, it is only
- * supported in Opera. Instead, an anchor element is created around the element
- * <tt>e</tt>, and the <tt>xlink:title</tt> attribute is set accordingly.
- *
- * @param e an SVG element.
- * @param s a scene node.
- */
-pv.SvgScene.title = function(e, s) {
- var a = e.parentNode, t = String(s.title);
- if (a && (a.tagName != "a")) a = null;
- if (t) {
- if (!a) {
- a = this.create("a");
- if (e.parentNode) e.parentNode.replaceChild(a, e);
- a.appendChild(e);
- }
- a.setAttributeNS(pv.ns.xlink, "title", t);
- return a;
- }
- if (a) a.parentNode.replaceChild(e, a);
- return e;
-};
-
-/** TODO */
-pv.SvgScene.dispatch = function(e) {
- var t = e.target.$scene;
- if (t) {
- t.scenes.mark.dispatch(e.type, t.scenes, t.index);
- e.preventDefault();
- }
-};
-
-/** TODO */
-pv.SvgScene.removeSiblings = function(e) {
- while (e) {
- var n = e.nextSibling;
- e.parentNode.removeChild(e);
- e = n;
- }
-};
-// TODO strokeStyle for areaSegment?
-
-pv.SvgScene.area = function(scenes) {
- var e = scenes.$g.firstChild;
- if (!scenes.length) return e;
- var s = scenes[0];
-
- /* segmented */
- if (s.segmented) return this.areaSegment(scenes);
-
- /* visible */
- if (!s.visible) return e;
- var fill = pv.color(s.fillStyle), stroke = pv.color(s.strokeStyle);
- if (!fill.opacity && !stroke.opacity) return e;
-
- /* points */
- var p1 = "", p2 = "";
- for (var i = 0, j = scenes.length - 1; j >= 0; i++, j--) {
- var si = scenes[i], sj = scenes[j];
- p1 += si.left + "," + si.top + " ";
- p2 += (sj.left + sj.width) + "," + (sj.top + sj.height) + " ";
-
- /* interpolate (assume linear by default) */
- if (i < scenes.length - 1) {
- var sk = scenes[i + 1], sl = scenes[j - 1];
- switch (s.interpolate) {
- case "step-before": {
- p1 += si.left + "," + sk.top + " ";
- p2 += (sl.left + sl.width) + "," + (sj.top + sj.height) + " ";
- break;
- }
- case "step-after": {
- p1 += sk.left + "," + si.top + " ";
- p2 += (sj.left + sj.width) + "," + (sl.top + sl.height) + " ";
- break;
- }
- }
- }
- }
-
- e = this.expect("polygon", e);
- e.setAttribute("cursor", s.cursor);
- e.setAttribute("points", p1 + p2);
- var fill = pv.color(s.fillStyle);
- e.setAttribute("fill", fill.color);
- e.setAttribute("fill-opacity", fill.opacity);
- var stroke = pv.color(s.strokeStyle);
- e.setAttribute("stroke", stroke.color);
- e.setAttribute("stroke-opacity", stroke.opacity);
- e.setAttribute("stroke-width", s.lineWidth);
- return this.append(e, scenes, 0);
-};
-
-pv.SvgScene.areaSegment = function(scenes) {
- var e = scenes.$g.firstChild;
- for (var i = 0, n = scenes.length - 1; i < n; i++) {
- var s1 = scenes[i], s2 = scenes[i + 1];
-
- /* visible */
- if (!s1.visible || !s2.visible) continue;
- var fill = pv.color(s1.fillStyle), stroke = pv.color(s1.strokeStyle);
- if (!fill.opacity && !stroke.opacity) continue;
-
- /* points */
- var p = s1.left + "," + s1.top + " "
- + s2.left + "," + s2.top + " "
- + (s2.left + s2.width) + "," + (s2.top + s2.height) + " "
- + (s1.left + s1.width) + "," + (s1.top + s1.height);
-
- e = this.expect("polygon", e);
- e.setAttribute("cursor", s1.cursor);
- e.setAttribute("points", p);
- e.setAttribute("fill", fill.color);
- e.setAttribute("fill-opacity", fill.opacity);
- e.setAttribute("stroke", stroke.color);
- e.setAttribute("stroke-opacity", stroke.opacity);
- e.setAttribute("stroke-width", s1.lineWidth);
- e = this.append(e, scenes, i);
- }
- return e;
-};
-pv.SvgScene.bar = function(scenes) {
- var e = scenes.$g.firstChild;
- for (var i = 0; i < scenes.length; i++) {
- var s = scenes[i];
-
- /* visible */
- if (!s.visible) continue;
- var fill = pv.color(s.fillStyle), stroke = pv.color(s.strokeStyle);
- if (!fill.opacity && !stroke.opacity) continue;
-
- e = this.expect("rect", e);
- e.setAttribute("cursor", s.cursor);
- e.setAttribute("x", s.left);
- e.setAttribute("y", s.top);
- e.setAttribute("width", Math.max(1E-10, s.width));
- e.setAttribute("height", Math.max(1E-10, s.height));
- e.setAttribute("fill", fill.color);
- e.setAttribute("fill-opacity", fill.opacity);
- e.setAttribute("stroke", stroke.color);
- e.setAttribute("stroke-opacity", stroke.opacity);
- e.setAttribute("stroke-width", s.lineWidth);
- e = this.append(e, scenes, i);
- }
- return e;
-};
-pv.SvgScene.dot = function(scenes) {
- var e = scenes.$g.firstChild;
- for (var i = 0; i < scenes.length; i++) {
- var s = scenes[i];
-
- /* visible */
- if (!s.visible) continue;
- var fill = pv.color(s.fillStyle), stroke = pv.color(s.strokeStyle);
- if (!fill.opacity && !stroke.opacity) continue;
-
- /* points */
- var radius = Math.sqrt(s.size), fillPath = "", strokePath = "";
- switch (s.shape) {
- case "cross": {
- fillPath = "M" + -radius + "," + -radius
- + "L" + radius + "," + radius
- + "M" + radius + "," + -radius
- + "L" + -radius + "," + radius;
- break;
- }
- case "triangle": {
- var h = radius, w = radius * 2 / Math.sqrt(3);
- fillPath = "M0," + h
- + "L" + w +"," + -h
- + " " + -w + "," + -h
- + "Z";
- break;
- }
- case "diamond": {
- radius *= Math.sqrt(2);
- fillPath = "M0," + -radius
- + "L" + radius + ",0"
- + " 0," + radius
- + " " + -radius + ",0"
- + "Z";
- break;
- }
- case "square": {
- fillPath = "M" + -radius + "," + -radius
- + "L" + radius + "," + -radius
- + " " + radius + "," + radius
- + " " + -radius + "," + radius
- + "Z";
- break;
- }
- case "tick": {
- fillPath = "M0,0L0," + -s.size;
- break;
- }
- default: {
- function circle(r) {
- return "M0," + r
- + "A" + r + "," + r + " 0 1,1 0," + (-r)
- + "A" + r + "," + r + " 0 1,1 0," + r
- + "Z";
- }
- if (s.lineWidth / 2 > radius) strokePath = circle(s.lineWidth);
- fillPath = circle(radius);
- break;
- }
- }
-
- /* transform */
- var transform = "translate(" + s.left + "," + s.top + ")"
- + (s.angle ? " rotate(" + 180 * s.angle / Math.PI + ")" : "");
-
- /* The normal fill path. */
- e = this.expect("path", e);
- e.setAttribute("d", fillPath);
- e.setAttribute("transform", transform);
- e.setAttribute("fill", fill.color);
- e.setAttribute("fill-opacity", fill.opacity);
- e.setAttribute("cursor", s.cursor);
- if (strokePath) {
- e.setAttribute("stroke", "none");
- } else {
- e.setAttribute("stroke", stroke.color);
- e.setAttribute("stroke-opacity", stroke.opacity);
- e.setAttribute("stroke-width", s.lineWidth);
- }
- e = this.append(e, scenes, i);
-
- /* The special-case stroke path. */
- if (strokePath) {
- e = this.expect("path", e);
- e.setAttribute("d", strokePath);
- e.setAttribute("transform", transform);
- e.setAttribute("fill", stroke.color);
- e.setAttribute("fill-opacity", stroke.opacity);
- e.setAttribute("cursor", s.cursor);
- e = this.append(e, scenes, i);
- }
- }
- return e;
-};
-pv.SvgScene.image = function(scenes) {
- var e = scenes.$g.firstChild;
- for (var i = 0; i < scenes.length; i++) {
- var s = scenes[i];
-
- /* visible */
- if (!s.visible) continue;
-
- /* fill */
- e = this.fill(e, scenes, i);
-
- /* image */
- e = this.expect("image", e);
- e.setAttribute("preserveAspectRatio", "none");
- e.setAttribute("x", s.left);
- e.setAttribute("y", s.top);
- e.setAttribute("width", s.width);
- e.setAttribute("height", s.height);
- e.setAttribute("cursor", s.cursor);
- e.setAttributeNS(pv.ns.xlink, "href", s.url);
- e = this.append(e, scenes, i);
-
- /* stroke */
- e = this.stroke(e, scenes, i);
- }
- return e;
-};
-pv.SvgScene.label = function(scenes) {
- var e = scenes.$g.firstChild;
- for (var i = 0; i < scenes.length; i++) {
- var s = scenes[i];
-
- /* visible */
- if (!s.visible) continue;
- var fill = pv.color(s.textStyle);
- if (!fill.opacity) continue;
-
- /* text-baseline, text-align */
- var x = 0, y = 0, dy = 0, anchor = "start";
- switch (s.textBaseline) {
- case "middle": dy = ".35em"; break;
- case "top": dy = ".71em"; y = s.textMargin; break;
- case "bottom": y = "-" + s.textMargin; break;
- }
- switch (s.textAlign) {
- case "right": anchor = "end"; x = "-" + s.textMargin; break;
- case "center": anchor = "middle"; break;
- case "left": x = s.textMargin; break;
- }
-
- e = this.expect("text", e);
- e.setAttribute("pointer-events", "none");
- e.setAttribute("x", x);
- e.setAttribute("y", y);
- e.setAttribute("dy", dy);
- e.setAttribute("text-anchor", anchor);
- e.setAttribute("transform",
- "translate(" + s.left + "," + s.top + ")"
- + (s.textAngle ? " rotate(" + 180 * s.textAngle / Math.PI + ")" : ""));
- e.setAttribute("fill", fill.color);
- e.setAttribute("fill-opacity", fill.opacity);
- e.style.font = s.font;
- e.style.textShadow = s.textShadow;
- if (e.firstChild) e.firstChild.nodeValue = s.text;
- else e.appendChild(document.createTextNode(s.text));
- e = this.append(e, scenes, i);
- }
- return e;
-};
-// TODO fillStyle for lineSegment?
-// TODO lineOffset for flow maps?
-
-pv.SvgScene.line = function(scenes) {
- var e = scenes.$g.firstChild;
- if (scenes.length < 2) return e;
- var s = scenes[0];
-
- /* segmented */
- if (s.segmented) return this.lineSegment(scenes);
-
- /* visible */
- if (!s.visible) return e;
- var fill = pv.color(s.fillStyle), stroke = pv.color(s.strokeStyle);
- if (!fill.opacity && !stroke.opacity) return e;
-
- /* points */
- var p = "";
- for (var i = 0; i < scenes.length; i++) {
- var si = scenes[i];
- p += si.left + "," + si.top + " ";
-
- /* interpolate (assume linear by default) */
- if (i < scenes.length - 1) {
- var sj = scenes[i + 1];
- switch (s.interpolate) {
- case "step-before": {
- p += si.left + "," + sj.top + " ";
- break;
- }
- case "step-after": {
- p += sj.left + "," + si.top + " ";
- break;
- }
- }
- }
- }
-
-
- e = this.expect("polyline", e);
- e.setAttribute("cursor", s.cursor);
- e.setAttribute("points", p);
- e.setAttribute("fill", fill.color);
- e.setAttribute("fill-opacity", fill.opacity);
- e.setAttribute("stroke", stroke.color);
- e.setAttribute("stroke-opacity", stroke.opacity);
- e.setAttribute("stroke-width", s.lineWidth);
- return this.append(e, scenes, 0);
-};
-
-pv.SvgScene.lineSegment = function(scenes) {
- var e = scenes.$g.firstChild;
- for (var i = 0, n = scenes.length - 1; i < n; i++) {
- var s1 = scenes[i], s2 = scenes[i + 1];
-
- /* visible */
- if (!s1.visible || !s2.visible) continue;
- var stroke = pv.color(s1.strokeStyle);
- if (!stroke.opacity) continue;
-
- /* Line-line intersection, per Akenine-Moller 16.16.1. */
- function intersect(o1, d1, o2, d2) {
- return o1.plus(d1.times(o2.minus(o1).dot(d2.perp()) / d1.dot(d2.perp())));
- }
-
- /*
- * P1-P2 is the current line segment. V is a vector that is perpendicular to
- * the line segment, and has length lineWidth / 2. ABCD forms the initial
- * bounding box of the line segment (i.e., the line segment if we were to do
- * no joins).
- */
- var p1 = pv.vector(s1.left, s1.top),
- p2 = pv.vector(s2.left, s2.top),
- p = p2.minus(p1),
- v = p.perp().norm(),
- w = v.times(s1.lineWidth / 2),
- a = p1.plus(w),
- b = p2.plus(w),
- c = p2.minus(w),
- d = p1.minus(w);
-
- /*
- * Start join. P0 is the previous line segment's start point. We define the
- * cutting plane as the average of the vector perpendicular to P0-P1, and
- * the vector perpendicular to P1-P2. This insures that the cross-section of
- * the line on the cutting plane is equal if the line-width is unchanged.
- * Note that we don't implement miter limits, so these can get wild.
- */
- if (i > 0) {
- var s0 = scenes[i - 1];
- if (s0.visible) {
- var v1 = p1.minus(s0.left, s0.top).perp().norm().plus(v);
- d = intersect(p1, v1, d, p);
- a = intersect(p1, v1, a, p);
- }
- }
-
- /* Similarly, for end join. */
- if (i < (n - 1)) {
- var s3 = scenes[i + 2];
- if (s3.visible) {
- var v2 = pv.vector(s3.left, s3.top).minus(p2).perp().norm().plus(v);
- c = intersect(p2, v2, c, p);
- b = intersect(p2, v2, b, p);
- }
- }
-
- /* points */
- var p = a.x + "," + a.y + " "
- + b.x + "," + b.y + " "
- + c.x + "," + c.y + " "
- + d.x + "," + d.y;
-
- e = this.expect("polygon", e);
- e.setAttribute("cursor", s1.cursor);
- e.setAttribute("points", p);
- e.setAttribute("fill", stroke.color);
- e.setAttribute("fill-opacity", stroke.opacity);
- e = this.append(e, scenes, i);
- }
- return e;
-};
-var guid = 0;
-
-pv.SvgScene.panel = function(scenes) {
- var g = scenes.$g, e = g && g.firstChild;
- for (var i = 0; i < scenes.length; i++) {
- var s = scenes[i];
-
- /* visible */
- if (!s.visible) continue;
-
- /* svg */
- if (!scenes.parent) {
- s.canvas.style.display = "inline-block";
- g = s.canvas.firstChild;
- if (!g) {
- g = s.canvas.appendChild(this.create("svg"));
- g.onclick
- = g.onmousedown
- = g.onmouseup
- = g.onmousemove
- = g.onmouseout
- = g.onmouseover
- = pv.SvgScene.dispatch;
- }
- scenes.$g = g;
- g.setAttribute("width", s.width + s.left + s.right);
- g.setAttribute("height", s.height + s.top + s.bottom);
- if (typeof e == "undefined") e = g.firstChild;
- }
-
- /* clip (nest children) */
- if (s.overflow == "hidden") {
- var c = this.expect("g", e), id = (guid++).toString(36);
- c.setAttribute("clip-path", "url(#" + id + ")");
- if (!c.parentNode) g.appendChild(c);
- scenes.$g = g = c;
- e = c.firstChild;
-
- e = this.expect("clipPath", e);
- e.setAttribute("id", id);
- var r = e.firstChild || e.appendChild(this.create("rect"));
- r.setAttribute("x", s.left);
- r.setAttribute("y", s.top);
- r.setAttribute("width", s.width);
- r.setAttribute("height", s.height);
- if (!e.parentNode) g.appendChild(e);
- e = e.nextSibling;
- }
-
- /* fill */
- e = this.fill(e, scenes, i);
-
- /* children */
- for (var j = 0; j < s.children.length; j++) {
- s.children[j].$g = e = this.expect("g", e);
- e.setAttribute("transform", "translate(" + s.left + "," + s.top + ")");
- this.updateAll(s.children[j]);
- if (!e.parentNode) g.appendChild(e);
- e = e.nextSibling;
- }
-
- /* stroke */
- e = this.stroke(e, scenes, i);
-
- /* clip (restore group) */
- if (s.overflow == "hidden") {
- scenes.$g = g = c.parentNode;
- e = c.nextSibling;
- }
- }
- return e;
-};
-
-pv.SvgScene.fill = function(e, scenes, i) {
- var s = scenes[i], fill = pv.color(s.fillStyle);
- if (fill.opacity) {
- e = this.expect("rect", e);
- e.setAttribute("x", s.left);
- e.setAttribute("y", s.top);
- e.setAttribute("width", s.width);
- e.setAttribute("height", s.height);
- e.setAttribute("cursor", s.cursor);
- e.setAttribute("fill", fill.color);
- e.setAttribute("fill-opacity", fill.opacity);
- e = this.append(e, scenes, i);
- }
- return e;
-};
-
-pv.SvgScene.stroke = function(e, scenes, i) {
- var s = scenes[i], stroke = pv.color(s.strokeStyle);
- if (stroke.opacity) {
- e = this.expect("rect", e);
- e.setAttribute("x", s.left);
- e.setAttribute("y", s.top);
- e.setAttribute("width", Math.max(1E-10, s.width));
- e.setAttribute("height", Math.max(1E-10, s.height));
- e.setAttribute("cursor", s.cursor);
- e.setAttribute("fill", "none");
- e.setAttribute("stroke", stroke.color);
- e.setAttribute("stroke-opacity", stroke.opacity);
- e.setAttribute("stroke-width", s.lineWidth);
- e = this.append(e, scenes, i);
- }
- return e;
-};
-pv.SvgScene.rule = function(scenes) {
- var e = scenes.$g.firstChild;
- for (var i = 0; i < scenes.length; i++) {
- var s = scenes[i];
-
- /* visible */
- if (!s.visible) continue;
- var stroke = pv.color(s.strokeStyle);
- if (!stroke.opacity) continue;
-
- e = this.expect("line", e);
- e.setAttribute("cursor", s.cursor);
- e.setAttribute("x1", s.left);
- e.setAttribute("y1", s.top);
- e.setAttribute("x2", s.left + s.width);
- e.setAttribute("y2", s.top + s.height);
- e.setAttribute("stroke", stroke.color);
- e.setAttribute("stroke-opacity", stroke.opacity);
- e.setAttribute("stroke-width", s.lineWidth);
- e = this.append(e, scenes, i);
- }
- return e;
-};
-pv.SvgScene.wedge = function(scenes) {
- var e = scenes.$g.firstChild;
- for (var i = 0; i < scenes.length; i++) {
- var s = scenes[i];
-
- /* visible */
- if (!s.visible) continue;
- var fill = pv.color(s.fillStyle), stroke = pv.color(s.strokeStyle);
- if (!fill.opacity && !stroke.opacity) continue;
-
- /* points */
- var r1 = s.innerRadius, r2 = s.outerRadius, a = Math.abs(s.angle), p;
- if (a >= 2 * Math.PI) {
- if (r1) {
- p = "M0," + r2
- + "A" + r2 + "," + r2 + " 0 1,1 0," + (-r2)
- + "A" + r2 + "," + r2 + " 0 1,1 0," + r2
- + "M0," + r1
- + "A" + r1 + "," + r1 + " 0 1,1 0," + (-r1)
- + "A" + r1 + "," + r1 + " 0 1,1 0," + r1
- + "Z";
- } else {
- p = "M0," + r2
- + "A" + r2 + "," + r2 + " 0 1,1 0," + (-r2)
- + "A" + r2 + "," + r2 + " 0 1,1 0," + r2
- + "Z";
- }
- } else {
- var sa = Math.min(s.startAngle, s.endAngle),
- ea = Math.max(s.startAngle, s.endAngle),
- c1 = Math.cos(sa), c2 = Math.cos(ea),
- s1 = Math.sin(sa), s2 = Math.sin(ea);
- if (r1) {
- p = "M" + r2 * c1 + "," + r2 * s1
- + "A" + r2 + "," + r2 + " 0 "
- + ((a < Math.PI) ? "0" : "1") + ",1 "
- + r2 * c2 + "," + r2 * s2
- + "L" + r1 * c2 + "," + r1 * s2
- + "A" + r1 + "," + r1 + " 0 "
- + ((a < Math.PI) ? "0" : "1") + ",0 "
- + r1 * c1 + "," + r1 * s1 + "Z";
- } else {
- p = "M" + r2 * c1 + "," + r2 * s1
- + "A" + r2 + "," + r2 + " 0 "
- + ((a < Math.PI) ? "0" : "1") + ",1 "
- + r2 * c2 + "," + r2 * s2 + "L0,0Z";
- }
- }
-
- e = this.expect("path", e);
- e.setAttribute("fill-rule", "evenodd");
- e.setAttribute("cursor", s.cursor);
- e.setAttribute("transform", "translate(" + s.left + "," + s.top + ")");
- e.setAttribute("d", p);
- e.setAttribute("fill", fill.color);
- e.setAttribute("fill-opacity", fill.opacity);
- e.setAttribute("stroke", stroke.color);
- e.setAttribute("stroke-opacity", stroke.opacity);
- e.setAttribute("stroke-width", s.lineWidth);
- e = this.append(e, scenes, i);
- }
- return e;
-};
-/**
- * Constructs a new mark with default properties. Marks, with the exception of
- * the root panel, are not typically constructed directly; instead, they are
- * added to a panel or an existing mark via {@link pv.Mark#add}.
- *
- * @class Represents a data-driven graphical mark. The <tt>Mark</tt> class is
- * the base class for all graphical marks in Protovis; it does not provide any
- * specific rendering functionality, but together with {@link Panel} establishes
- * the core framework.
- *
- * <p>Concrete mark types include familiar visual elements such as bars, lines
- * and labels. Although a bar mark may be used to construct a bar chart, marks
- * know nothing about charts; it is only through their specification and
- * composition that charts are produced. These building blocks permit many
- * combinatorial possibilities.
- *
- * <p>Marks are associated with <b>data</b>: a mark is generated once per
- * associated datum, mapping the datum to visual <b>properties</b> such as
- * position and color. Thus, a single mark specification represents a set of
- * visual elements that share the same data and visual encoding. The type of
- * mark defines the names of properties and their meaning. A property may be
- * static, ignoring the associated datum and returning a constant; or, it may be
- * dynamic, derived from the associated datum or index. Such dynamic encodings
- * can be specified succinctly using anonymous functions. Special properties
- * called event handlers can be registered to add interactivity.
- *
- * <p>Protovis uses <b>inheritance</b> to simplify the specification of related
- * marks: a new mark can be derived from an existing mark, inheriting its
- * properties. The new mark can then override properties to specify new
- * behavior, potentially in terms of the old behavior. In this way, the old mark
- * serves as the <b>prototype</b> for the new mark. Most mark types share the
- * same basic properties for consistency and to facilitate inheritance.
- *
- * <p>The prioritization of redundant properties is as follows:<ol>
- *
- * <li>If the <tt>width</tt> property is not specified (i.e., null), its value
- * is the width of the parent panel, minus this mark's left and right margins;
- * the left and right margins are zero if not specified.
- *
- * <li>Otherwise, if the <tt>right</tt> margin is not specified, its value is
- * the width of the parent panel, minus this mark's width and left margin; the
- * left margin is zero if not specified.
- *
- * <li>Otherwise, if the <tt>left</tt> property is not specified, its value is
- * the width of the parent panel, minus this mark's width and the right margin.
- *
- * </ol>This prioritization is then duplicated for the <tt>height</tt>,
- * <tt>bottom</tt> and <tt>top</tt> properties, respectively.
- *
- * <p>While most properties are <i>variable</i>, some mark types, such as lines
- * and areas, generate a single visual element rather than a distinct visual
- * element per datum. With these marks, some properties may be <b>fixed</b>.
- * Fixed properties can vary per mark, but not <i>per datum</i>! These
- * properties are evaluated solely for the first (0-index) datum, and typically
- * are specified as a constant. However, it is valid to use a function if the
- * property varies between panels or is dynamically generated.
- *
- * <p>See also the <a href="../../api/">Protovis guide</a>.
- */
-pv.Mark = function() {
- /*
- * TYPE 0 constant defs
- * TYPE 1 function defs
- * TYPE 2 constant properties
- * TYPE 3 function properties
- * in order of evaluation!
- */
- this.$properties = [];
-};
-
-/** @private TOOD */
-pv.Mark.prototype.properties = {};
-
-/**
- * @private Defines and registers a property method for the property with the
- * given name. This method should be called on a mark class prototype to define
- * each exposed property. (Note this refers to the JavaScript
- * <tt>prototype</tt>, not the Protovis mark prototype, which is the {@link
- * #proto} field.)
- *
- * <p>The created property method supports several modes of invocation: <ol>
- *
- * <li>If invoked with a <tt>Function</tt> argument, this function is evaluated
- * for each associated datum. The return value of the function is used as the
- * computed property value. The context of the function (<tt>this</tt>) is this
- * mark. The arguments to the function are the associated data of this mark and
- * any enclosing panels. For example, a linear encoding of numerical data to
- * height is specified as
- *
- * <pre>m.height(function(d) d * 100);</pre>
- *
- * The expression <tt>d * 100</tt> will be evaluated for the height property of
- * each mark instance. The return value of the property method (e.g.,
- * <tt>m.height</tt>) is this mark (<tt>m</tt>)).<p>
- *
- * <li>If invoked with a non-function argument, the property is treated as a
- * constant. The return value of the property method (e.g., <tt>m.height</tt>)
- * is this mark.<p>
- *
- * <li>If invoked with no arguments, the computed property value for the current
- * mark instance in the scene graph is returned. This facilitates <i>property
- * chaining</i>, where one mark's properties are defined in terms of another's.
- * For example, to offset a mark's location from its prototype, you might say
- *
- * <pre>m.top(function() this.proto.top() + 10);</pre>
- *
- * Note that the index of the mark being evaluated (in the above example,
- * <tt>this.proto</tt>) is inherited from the <tt>Mark</tt> class and set by
- * this mark. So, if the fifth element's top property is being evaluated, the
- * fifth instance of <tt>this.proto</tt> will similarly be queried for the value
- * of its top property. If the mark being evaluated has a different number of
- * instances, or its data is unrelated, the behavior of this method is
- * undefined. In these cases it may be better to index the <tt>scene</tt>
- * explicitly to specify the exact instance.
- *
- * </ol><p>Property names should follow standard JavaScript method naming
- * conventions, using lowerCamel-style capitalization.
- *
- * <p>In addition to creating the property method, every property is registered
- * in the {@link #properties} map on the <tt>prototype</tt>. Although this is an
- * instance field, it is considered immutable and shared by all instances of a
- * given mark type. The <tt>properties</tt> map can be queried to see if a mark
- * type defines a particular property, such as width or height.
- *
- * @param {string} name the property name.
- */
-pv.Mark.prototype.property = function(name) {
- if (!this.hasOwnProperty("properties")) {
- this.properties = pv.extend(this.properties);
- }
- this.properties[name] = true;
-
- /*
- * Define the setter-getter globally, since the default behavior should be the
- * same for all properties, and since the Protovis inheritance chain is
- * independent of the JavaScript inheritance chain. For example, anchors
- * define a "name" property that is evaluated on derived marks, even though
- * those marks don't normally have a name.
- */
- pv.Mark.prototype[name] = function(v) {
- if (arguments.length) {
- this.$properties.push({
- name: name,
- type: (typeof v == "function") ? 3 : 2,
- value: v
- });
- return this;
- }
- return this.scene[this.index][name];
- };
-
- return this;
-};
-
-/* Define all global properties. */
-pv.Mark.prototype
- .property("data")
- .property("visible")
- .property("left")
- .property("right")
- .property("top")
- .property("bottom")
- .property("cursor")
- .property("title")
- .property("reverse");
-
-/**
- * The mark type; a lower camelCase name. The type name controls rendering
- * behavior, and unless the rendering engine is extended, must be one of the
- * built-in concrete mark types: area, bar, dot, image, label, line, panel,
- * rule, or wedge.
- *
- * @type string
- * @name pv.Mark.prototype.type
- */
-
-/**
- * The mark prototype, possibly undefined, from which to inherit property
- * functions. The mark prototype is not necessarily of the same type as this
- * mark. Any properties defined on this mark will override properties inherited
- * either from the prototype or from the type-specific defaults.
- *
- * @type pv.Mark
- * @name pv.Mark.prototype.proto
- */
-
-/**
- * The enclosing parent panel. The parent panel is generally undefined only for
- * the root panel; however, it is possible to create "offscreen" marks that are
- * used only for inheritance purposes.
- *
- * @type pv.Panel
- * @name pv.Mark.prototype.parent
- */
-
-/**
- * The child index. -1 if the enclosing parent panel is null; otherwise, the
- * zero-based index of this mark into the parent panel's <tt>children</tt> array.
- *
- * @type number
- */
-pv.Mark.prototype.childIndex = -1;
-
-/**
- * The mark index. The value of this field depends on which instance (i.e.,
- * which element of the data array) is currently being evaluated. During the
- * build phase, the index is incremented over each datum; when handling events,
- * the index is set to the instance that triggered the event.
- *
- * @type number
- */
-pv.Mark.prototype.index = -1;
-
-/**
- * The scene graph. The scene graph is an array of objects; each object (or
- * "node") corresponds to an instance of this mark and an element in the data
- * array. The scene graph can be traversed to lookup previously-evaluated
- * properties.
- *
- * <p>For instance, consider a stacked area chart. The bottom property of the
- * area can be defined using the <i>cousin</i> instance, which is the current
- * area instance in the previous instantiation of the parent panel. In this
- * sample code,
- *
- * <pre>new pv.Panel()
- * .width(150).height(150)
- * .add(pv.Panel)
- * .data([[1, 1.2, 1.7, 1.5, 1.7],
- * [.5, 1, .8, 1.1, 1.3],
- * [.2, .5, .8, .9, 1]])
- * .add(pv.Area)
- * .data(function(d) d)
- * .bottom(function() {
- * var c = this.cousin();
- * return c ? (c.bottom + c.height) : 0;
- * })
- * .height(function(d) d * 40)
- * .left(function() this.index * 35)
- * .root.render();</pre>
- *
- * the bottom property is computed based on the upper edge of the corresponding
- * datum in the previous series. The area's parent panel is instantiated once
- * per series, so the cousin refers to the previous (below) area mark. (Note
- * that the position of the upper edge is not the same as the top property,
- * which refers to the top margin: the distance from the top edge of the panel
- * to the top edge of the mark.)
- *
- * @see #first
- * @see #last
- * @see #sibling
- * @see #cousin
- * @name pv.Mark.prototype.scene
- */
-
-/**
- * The root parent panel. This may be undefined for "offscreen" marks that are
- * created for inheritance purposes only.
- *
- * @type pv.Panel
- * @name pv.Mark.prototype.root
- */
-
-/**
- * The data property; an array of objects. The size of the array determines the
- * number of marks that will be instantiated; each element in the array will be
- * passed to property functions to compute the property values. Typically, the
- * data property is specified as a constant array, such as
- *
- * <pre>m.data([1, 2, 3, 4, 5]);</pre>
- *
- * However, it is perfectly acceptable to define the data property as a
- * function. This function might compute the data dynamically, allowing
- * different data to be used per enclosing panel. For instance, in the stacked
- * area graph example (see {@link #scene}), the data function on the area mark
- * dereferences each series.
- *
- * @type array
- * @name pv.Mark.prototype.data
- */
-
-/**
- * The visible property; a boolean determining whether or not the mark instance
- * is visible. If a mark instance is not visible, its other properties will not
- * be evaluated. Similarly, for panels no child marks will be rendered.
- *
- * @type boolean
- * @name pv.Mark.prototype.visible
- */
-
-/**
- * The left margin; the distance, in pixels, between the left edge of the
- * enclosing panel and the left edge of this mark. Note that in some cases this
- * property may be redundant with the right property, or with the conjunction of
- * right and width.
- *
- * @type number
- * @name pv.Mark.prototype.left
- */
-
-/**
- * The right margin; the distance, in pixels, between the right edge of the
- * enclosing panel and the right edge of this mark. Note that in some cases this
- * property may be redundant with the left property, or with the conjunction of
- * left and width.
- *
- * @type number
- * @name pv.Mark.prototype.right
- */
-
-/**
- * The top margin; the distance, in pixels, between the top edge of the
- * enclosing panel and the top edge of this mark. Note that in some cases this
- * property may be redundant with the bottom property, or with the conjunction
- * of bottom and height.
- *
- * @type number
- * @name pv.Mark.prototype.top
- */
-
-/**
- * The bottom margin; the distance, in pixels, between the bottom edge of the
- * enclosing panel and the bottom edge of this mark. Note that in some cases
- * this property may be redundant with the top property, or with the conjunction
- * of top and height.
- *
- * @type number
- * @name pv.Mark.prototype.bottom
- */
-
-/**
- * The cursor property; corresponds to the CSS cursor property. This is
- * typically used in conjunction with event handlers to indicate interactivity.
- *
- * @type string
- * @name pv.Mark.prototype.cursor
- * @see <a href="http://www.w3.org/TR/CSS2/ui.html#propdef-cursor">CSS2 cursor</a>
- */
-
-/**
- * The title property; corresponds to the HTML/SVG title property, allowing the
- * general of simple plain text tooltips.
- *
- * @type string
- * @name pv.Mark.prototype.title
- */
-
-/**
- * The reverse property; a boolean determining whether marks are ordered from
- * front-to-back or back-to-front. SVG does not support explicit z-ordering;
- * shapes are rendered in the order they appear. Thus, by default, marks are
- * rendered in data order. Setting the reverse property to false reverses the
- * order in which they are rendered; however, the properties are still evaluated
- * (i.e., built) in forward order.
- *
- * @type boolean
- * @name pv.Mark.prototype.reverse
- */
-
-/**
- * Default properties for all mark types. By default, the data array is the
- * parent data as a single-element array; if the data property is not specified,
- * this causes each mark to be instantiated as a singleton with the parents
- * datum. The visible property is true by default, and the reverse property is
- * false.
- *
- * @type pv.Mark
- */
-pv.Mark.prototype.defaults = new pv.Mark()
- .data(function(d) { return [d]; })
- .visible(true)
- .reverse(false)
- .cursor("")
- .title("");
-
-/* Private categorical colors for default fill & stroke styles. */
-var defaultFillStyle = pv.Colors.category20().by(pv.parent),
- defaultStrokeStyle = pv.Colors.category10().by(pv.parent);
-
-/**
- * Sets the prototype of this mark to the specified mark. Any properties not
- * defined on this mark may be inherited from the specified prototype mark, or
- * its prototype, and so on. The prototype mark need not be the same type of
- * mark as this mark. (Note that for inheritance to be useful, properties with
- * the same name on different mark types should have equivalent meaning.)
- *
- * @param {pv.Mark} proto the new prototype.
- * @return {pv.Mark} this mark.
- * @see #add
- */
-pv.Mark.prototype.extend = function(proto) {
- this.proto = proto;
- return this;
-};
-
-/**
- * Adds a new mark of the specified type to the enclosing parent panel, whilst
- * simultaneously setting the prototype of the new mark to be this mark.
- *
- * @param {function} type the type of mark to add; a constructor, such as
- * <tt>pv.Bar</tt>.
- * @return {pv.Mark} the new mark.
- * @see #extend
- */
-pv.Mark.prototype.add = function(type) {
- return this.parent.add(type).extend(this);
-};
-
-/**
- * Defines a local variable on this mark. Local variables are initialized once
- * per mark (i.e., per parent panel instance), and can be used to store local
- * state for the mark. Here are a few reasons you might want to use
- * <tt>def</tt>:
- *
- * <p>1. To store local state. For example, say you were visualizing employment
- * statistics, and your root panel had an array of occupations. In a child
- * panel, you might want to initialize a local scale, and reference it from a
- * property function:
- *
- * <pre>.def("y", function(d) pv.Scale.linear(0, pv.max(d.values)).range(0, h))
- * .height(function(d) this.y()(d))</pre>
- *
- * In this example, <tt>this.y()</tt> returns the defined local scale. We then
- * invoke the scale function, passing in the datum, to compute the height. Note
- * that defs are similar to fixed properties: they are only evaluated once per
- * parent panel, and <tt>this.y()</tt> returns a function, rather than
- * automatically evaluating this function as a property.
- *
- * <p>2. To store temporary state for interaction. Say you have an array of
- * bars, and you want to color the bar differently if the mouse is over it. Use
- * <tt>def</tt> to define a local variable, and event handlers to override this
- * variable interactively:
- *
- * <pre>.def("i", -1)
- * .event("mouseover", function() this.i(this.index))
- * .event("mouseout", function() this.i(-1))
- * .fillStyle(function() this.i() == this.index ? "red" : "blue")</pre>
- *
- * Notice that <tt>this.i()</tt> can be used both to set the value of <i>i</i>
- * (when an argument is specified), and to get the value of <i>i</i> (when no
- * arguments are specified). In this way, it's like other property methods.
- *
- * <p>3. To specify fixed properties efficiently. Sometimes, the value of a
- * property may be locally a constant, but dependent on parent panel data which
- * is variable. In this scenario, you can use <tt>def</tt> to define a property;
- * it will only get computed once per mark, rather than once per datum.
- *
- * @param {string} name the name of the local variable.
- * @param {function} [value] an optional initializer; may be a constant or a
- * function.
- */
-pv.Mark.prototype.def = function(name, value) {
- this.$properties.push({
- name: name,
- type: (typeof value == "function") ? 1 : 0,
- value: value
- });
- return this;
-};
-
-/**
- * Returns an anchor with the specified name. While anchor names are typically
- * constants, the anchor name is a true property, which means you can specify a
- * function to compute the anchor name dynamically. See the
- * {@link pv.Anchor#name} property for details.
- *
- * @param {string} name the anchor name; either a string or a property function.
- * @returns {pv.Anchor} the new anchor.
- */
-pv.Mark.prototype.anchor = function(name) {
- var anchor = new pv.Anchor().extend(this).name(name);
- anchor.parent = this.parent;
- return anchor;
-};
-
-/**
- * Returns the anchor target of this mark, if it is derived from an anchor;
- * otherwise returns null. For example, if a label is derived from a bar anchor,
- *
- * <pre>bar.anchor("top").add(pv.Label);</pre>
- *
- * then property functions on the label can refer to the bar via the
- * <tt>anchorTarget</tt> method. This method is also useful for mark types
- * defining properties on custom anchors.
- *
- * @returns {pv.Mark} the anchor target of this mark; possibly null.
- */
-pv.Mark.prototype.anchorTarget = function() {
- var target = this;
- while (!(target instanceof pv.Anchor)) {
- target = target.proto;
- if (!target) return null;
- }
- return target.proto;
-};
-
-/**
- * Returns the first instance of this mark in the scene graph. This method can
- * only be called when the mark is bound to the scene graph (for example, from
- * an event handler, or within a property function).
- *
- * @returns a node in the scene graph.
- */
-pv.Mark.prototype.first = function() {
- return this.scene[0];
-};
-
-/**
- * Returns the last instance of this mark in the scene graph. This method can
- * only be called when the mark is bound to the scene graph (for example, from
- * an event handler, or within a property function). In addition, note that mark
- * instances are built sequentially, so the last instance of this mark may not
- * yet be constructed.
- *
- * @returns a node in the scene graph.
- */
-pv.Mark.prototype.last = function() {
- return this.scene[this.scene.length - 1];
-};
-
-/**
- * Returns the previous instance of this mark in the scene graph, or null if
- * this is the first instance.
- *
- * @returns a node in the scene graph, or null.
- */
-pv.Mark.prototype.sibling = function() {
- return (this.index == 0) ? null : this.scene[this.index - 1];
-};
-
-/**
- * Returns the current instance in the scene graph of this mark, in the previous
- * instance of the enclosing parent panel. May return null if this instance
- * could not be found. See the {@link pv.Layout.stack} function for an example
- * property function using cousin.
- *
- * @see pv.Layout.stack
- * @returns a node in the scene graph, or null.
- */
-pv.Mark.prototype.cousin = function() {
- var p = this.parent, s = p && p.sibling();
- return (s && s.children) ? s.children[this.childIndex][this.index] : null;
-};
-
-/**
- * Renders this mark, including recursively rendering all child marks if this is
- * a panel.
- */
-pv.Mark.prototype.render = function() {
- /*
- * Rendering consists of three phases: bind, build and update. The update
- * phase is decoupled to allow different rendering engines.
- *
- * In the bind phase, inherited property definitions are cached so they do not
- * need to be queried during build. In the build phase, properties are
- * evaluated, and the scene graph is generated. In the update phase, the scene
- * is rendered by creating and updating elements and attributes in the SVG
- * image. No properties are evaluated during the update phase; instead the
- * values computed previously in the build phase are simply translated into
- * SVG.
- */
- this.bind();
- this.build();
- pv.Scene.updateAll(this.scene);
-};
-
-/** @private Computes the root data stack for the specified mark. */
-function argv(mark) {
- var stack = [];
- while (mark) {
- stack.push(mark.scene[mark.index].data);
- mark = mark.parent;
- }
- return stack;
-}
-
-/** @private TODO */
-pv.Mark.prototype.bind = function() {
- var seen = {}, types = [[], [], [], []], data, visible;
-
- /** TODO */
- function bind(mark) {
- do {
- var properties = mark.$properties;
- for (var i = properties.length - 1; i >= 0 ; i--) {
- var p = properties[i];
- if (!(p.name in seen)) {
- seen[p.name] = 1;
- switch (p.name) {
- case "data": data = p; break;
- case "visible": visible = p; break;
- default: types[p.type].push(p); break;
- }
- }
- }
- } while (mark = mark.proto);
- }
-
- /** TODO */
- function def(name) {
- return function(v) {
- var defs = this.scene.defs;
- if (arguments.length) {
- if (v == undefined) {
- delete defs.locked[name];
- } else {
- defs.locked[name] = true;
- }
- defs.values[name] = v;
- return this;
- } else {
- return defs.values[name];
- }
- };
- }
-
- /* Scan the proto chain for all defined properties. */
- bind(this);
- bind(this.defaults);
- types[1].reverse();
- types[3].reverse();
-
- /* Any undefined properties are null. */
- var mark = this;
- do for (var name in mark.properties) {
- if (!(name in seen)) {
- seen[name] = 1;
- types[2].push({name: name, type: 2, value: null});
- }
- } while (mark = mark.proto);
-
- /* Define setter-getter for inherited defs. */
- var defs = types[0].concat(types[1]);
- for (var i = 0; i < defs.length; i++) {
- var d = defs[i];
- this[d.name] = def(d.name);
- }
-
- /* Setup binds to evaluate constants before functions. */
- this.binds = {
- data: data,
- visible: visible,
- defs: defs,
- properties: pv.blend(types)
- };
-};
-
-/**
- * @private Evaluates properties and computes implied properties. Properties are
- * stored in the {@link #scene} array for each instance of this mark.
- *
- * <p>As marks are built recursively, the {@link #index} property is updated to
- * match the current index into the data array for each mark. Note that the
- * index property is only set for the mark currently being built and its
- * enclosing parent panels. The index property for other marks is unset, but is
- * inherited from the global <tt>Mark</tt> class prototype. This allows mark
- * properties to refer to properties on other marks <i>in the same panel</i>
- * conveniently; however, in general it is better to reference mark instances
- * specifically through the scene graph rather than depending on the magical
- * behavior of {@link #index}.
- *
- * <p>The root scene array has a special property, <tt>data</tt>, which stores
- * the current data stack. The first element in this stack is the current datum,
- * followed by the datum of the enclosing parent panel, and so on. The data
- * stack should not be accessed directly; instead, property functions are passed
- * the current data stack as arguments.
- *
- * <p>The evaluation of the <tt>data</tt> and <tt>visible</tt> properties is
- * special. The <tt>data</tt> property is evaluated first; unlike the other
- * properties, the data stack is from the parent panel, rather than the current
- * mark, since the data is not defined until the data property is evaluated.
- * The <tt>visisble</tt> property is subsequently evaluated for each instance;
- * only if true will the {@link #buildInstance} method be called, evaluating
- * other properties and recursively building the scene graph.
- *
- * <p>If this mark is being re-built, any old instances of this mark that no
- * longer exist (because the new data array contains fewer elements) will be
- * cleared using {@link #clearInstance}.
- *
- * @param parent the instance of the parent panel from the scene graph.
- */
-pv.Mark.prototype.build = function() {
- var scene = this.scene;
- if (!scene) {
- scene = this.scene = [];
- scene.mark = this;
- scene.type = this.type;
- scene.childIndex = this.childIndex;
- if (this.parent) {
- scene.parent = this.parent.scene;
- scene.parentIndex = this.parent.index;
- }
- }
-
- /* Set the data stack. */
- var stack = this.root.scene.data;
- if (!stack) this.root.scene.data = stack = argv(this.parent);
-
- /* Evaluate defs. */
- if (this.binds.defs.length) {
- var defs = scene.defs;
- if (!defs) scene.defs = defs = {values: {}, locked: {}};
- for (var i = 0; i < this.binds.defs.length; i++) {
- var d = this.binds.defs[i];
- if (!(d.name in defs.locked)) {
- var v = d.value;
- if (d.type == 1) {
- property = d.name;
- v = v.apply(this, stack);
- }
- defs.values[d.name] = v;
- }
- }
- }
-
- /* Evaluate special data property. */
- var data = this.binds.data;
- switch (data.type) {
- case 0: case 1: data = defs.values.data; break;
- case 2: data = data.value; break;
- case 3: {
- property = "data";
- data = data.value.apply(this, stack);
- break;
- }
- }
-
- /* Create, update and delete scene nodes. */
- stack.unshift(null);
- scene.length = data.length;
- for (var i = 0; i < data.length; i++) {
- pv.Mark.prototype.index = this.index = i;
- var s = scene[i];
- if (!s) scene[i] = s = {};
- s.data = stack[0] = data[i];
-
- /* Evaluate special visible property. */
- var visible = this.binds.visible;
- switch (visible.type) {
- case 0: case 1: visible = defs.values.visible; break;
- case 2: visible = visible.value; break;
- case 3: {
- property = "visible";
- visible = visible.value.apply(this, stack);
- break;
- }
- }
-
- if (s.visible = visible) this.buildInstance(s);
- }
- stack.shift();
- delete this.index;
- pv.Mark.prototype.index = -1;
- if (!this.parent) scene.data = null;
-
- return this;
-};
-
-/**
- * @private Evaluates the specified array of properties for the specified
- * instance <tt>s</tt> in the scene graph.
- *
- * @param s a node in the scene graph; the instance of the mark to build.
- * @param properties an array of properties.
- */
-pv.Mark.prototype.buildProperties = function(s, properties) {
- for (var i = 0, n = properties.length; i < n; i++) {
- var p = properties[i], v = p.value;
- switch (p.type) {
- case 0: case 1: v = this.scene.defs.values[p.name]; break;
- case 3: {
- property = p.name;
- v = v.apply(this, this.root.scene.data);
- break;
- }
- }
- s[p.name] = v;
- }
-};
-
-/**
- * @private Evaluates all of the properties for this mark for the specified
- * instance <tt>s</tt> in the scene graph. The set of properties to evaluate is
- * retrieved from the {@link #properties} array for this mark type (see {@link
- * #type}). After these properties are evaluated, any <b>implied</b> properties
- * may be computed by the mark and set on the scene graph; see
- * {@link #buildImplied}.
- *
- * <p>For panels, this method recursively builds the scene graph for all child
- * marks as well. In general, this method should not need to be overridden by
- * concrete mark types.
- *
- * @param s a node in the scene graph; the instance of the mark to build.
- */
-pv.Mark.prototype.buildInstance = function(s) {
- this.buildProperties(s, this.binds.properties);
- this.buildImplied(s);
-};
-
-/**
- * @private Computes the implied properties for this mark for the specified
- * instance <tt>s</tt> in the scene graph. Implied properties are those with
- * dependencies on multiple other properties; for example, the width property
- * may be implied if the left and right properties are set. This method can be
- * overridden by concrete mark types to define new implied properties, if
- * necessary.
- *
- * @param s a node in the scene graph; the instance of the mark to build.
- */
-pv.Mark.prototype.buildImplied = function(s) {
- var l = s.left;
- var r = s.right;
- var t = s.top;
- var b = s.bottom;
-
- /* Assume width and height are zero if not supported by this mark type. */
- var p = this.properties;
- var w = p.width ? s.width : 0;
- var h = p.height ? s.height : 0;
-
- /* Compute implied width, right and left. */
- var width = this.parent ? this.parent.width() : (w + l + r);
- if (w == null) {
- w = width - (r = r || 0) - (l = l || 0);
- } else if (r == null) {
- r = width - w - (l = l || 0);
- } else if (l == null) {
- l = width - w - (r = r || 0);
- }
-
- /* Compute implied height, bottom and top. */
- var height = this.parent ? this.parent.height() : (h + t + b);
- if (h == null) {
- h = height - (t = t || 0) - (b = b || 0);
- } else if (b == null) {
- b = height - h - (t = t || 0);
- } else if (t == null) {
- t = height - h - (b = b || 0);
- }
-
- s.left = l;
- s.right = r;
- s.top = t;
- s.bottom = b;
-
- /* Only set width and height if they are supported by this mark type. */
- if (p.width) s.width = w;
- if (p.height) s.height = h;
-};
-
-/**
- * @private The name of the property being evaluated, for so-called "smart"
- * functions that change behavior depending on which property is being
- * evaluated. This functionality is somewhat magical, so for now, this feature
- * is not exposed outside the library.
- *
- * @type string
- */
-var property;
-
-/** @private The current mouse location. */
-var pageX = 0, pageY = 0;
-pv.listen(window, "mousemove", function(e) { pageX = e.pageX; pageY = e.pageY; });
-
-/**
- * Returns the current location of the mouse (cursor) relative to this mark's
- * parent. The <i>x</i> coordinate corresponds to the left margin, while the
- * <i>y</i> coordinate corresponds to the top margin.
- *
- * @returns {pv.Vector} the mouse location.
- */
-pv.Mark.prototype.mouse = function() {
- var x = 0, y = 0, mark = (this instanceof pv.Panel) ? this : this.parent;
- do {
- x += mark.left();
- y += mark.top();
- } while (mark = mark.parent);
- var node = this.root.canvas();
- do {
- x += node.offsetLeft;
- y += node.offsetTop;
- } while (node = node.offsetParent);
- return pv.vector(pageX - x, pageY - y);
-};
-
-/**
- * Registers an event handler for the specified event type with this mark. When
- * an event of the specified type is triggered, the specified handler will be
- * invoked. The handler is invoked in a similar method to property functions:
- * the context is <tt>this</tt> mark instance, and the arguments are the full
- * data stack. Event handlers can use property methods to manipulate the display
- * properties of the mark:
- *
- * <pre>m.event("click", function() this.fillStyle("red"));</pre>
- *
- * Alternatively, the external data can be manipulated and the visualization
- * redrawn:
- *
- * <pre>m.event("click", function(d) {
- * data = all.filter(function(k) k.name == d);
- * vis.render();
- * });</pre>
- *
- * The return value of the event handler determines which mark gets re-rendered.
- * Use defs ({@link #def}) to set temporary state from event handlers.
- *
- * <p>The complete set of event types is defined by SVG; see the reference
- * below. The set of supported event types is:<ul>
- *
- * <li>click
- * <li>mousedown
- * <li>mouseup
- * <li>mouseover
- * <li>mousemove
- * <li>mouseout
- *
- * </ul>Since Protovis does not specify any concept of focus, it does not
- * support key events; these should be handled outside the visualization using
- * standard JavaScript. In the future, support for interaction may be extended
- * to support additional event types, particularly those most relevant to
- * interactive visualization, such as selection.
- *
- * <p>TODO In the current implementation, event handlers are not inherited from
- * prototype marks. They must be defined explicitly on each interactive mark. In
- * addition, only one event handler for a given event type can be defined; when
- * specifying multiple event handlers for the same type, only the last one will
- * be used.
- *
- * @see <a href="http://www.w3.org/TR/SVGTiny12/interact.html#SVGEvents">SVG events</a>
- * @param {string} type the event type.
- * @param {function} handler the event handler.
- * @returns {pv.Mark} this.
- */
-pv.Mark.prototype.event = function(type, handler) {
- if (!this.$handlers) this.$handlers = {};
- this.$handlers[type] = handler;
- return this;
-};
-
-/** @private TODO */
-pv.Mark.prototype.dispatch = function(type, scenes, index) {
- var l = this.$handlers && this.$handlers[type];
- if (!l) {
- if (this.parent) {
- this.parent.dispatch(type, scenes.parent, scenes.parentIndex);
- }
- return;
- }
- try {
-
- /* Setup the scene stack. */
- var mark = this;
- do {
- mark.index = index;
- mark.scene = scenes;
- index = scenes.parentIndex;
- scenes = scenes.parent;
- } while (mark = mark.parent);
-
- /* Execute the event listener. */
- try {
- mark = l.apply(this, this.root.scene.data = argv(this));
- } finally {
- this.root.scene.data = null;
- }
-
- /* Update the display. TODO dirtying. */
- if (mark instanceof pv.Mark) mark.render();
-
- } finally {
-
- /* Restore the scene stack. */
- var mark = this;
- do {
- if (mark.parent) delete mark.scene;
- delete mark.index;
- } while (mark = mark.parent);
- }
-};
-/**
- * Constructs a new mark anchor with default properties.
- *
- * @class Represents an anchor on a given mark. An anchor is itself a mark, but
- * without a visual representation. It serves only to provide useful default
- * properties that can be inherited by other marks. Each type of mark can define
- * any number of named anchors for convenience. If the concrete mark type does
- * not define an anchor implementation specifically, one will be inherited from
- * the mark's parent class.
- *
- * <p>For example, the bar mark provides anchors for its four sides: left,
- * right, top and bottom. Adding a label to the top anchor of a bar,
- *
- * <pre>bar.anchor("top").add(pv.Label);</pre>
- *
- * will render a text label on the top edge of the bar; the top anchor defines
- * the appropriate position properties (top and left), as well as text-rendering
- * properties for convenience (textAlign and textBaseline).
- *
- * @extends pv.Mark
- */
-pv.Anchor = function() {
- pv.Mark.call(this);
-};
-
-pv.Anchor.prototype = pv.extend(pv.Mark)
- .property("name");
-
-/**
- * The anchor name. The set of supported anchor names is dependent on the
- * concrete mark type; see the mark type for details. For example, bars support
- * left, right, top and bottom anchors.
- *
- * <p>While anchor names are typically constants, the anchor name is a true
- * property, which means you can specify a function to compute the anchor name
- * dynamically. For instance, if you wanted to alternate top and bottom anchors,
- * saying
- *
- * <pre>m.anchor(function() (this.index % 2) ? "top" : "bottom").add(pv.Dot);</pre>
- *
- * would have the desired effect.
- *
- * @type string
- * @name pv.Anchor.prototype.name
- */
-/**
- * Constructs a new area mark with default properties. Areas are not typically
- * constructed directly, but by adding to a panel or an existing mark via
- * {@link pv.Mark#add}.
- *
- * @class Represents an area mark: the solid area between two series of
- * connected line segments. Unsurprisingly, areas are used most frequently for
- * area charts.
- *
- * <p>Just as a line represents a polyline, the <tt>Area</tt> mark type
- * represents a <i>polygon</i>. However, an area is not an arbitrary polygon;
- * vertices are paired either horizontally or vertically into parallel
- * <i>spans</i>, and each span corresponds to an associated datum. Either the
- * width or the height must be specified, but not both; this determines whether
- * the area is horizontally-oriented or vertically-oriented. Like lines, areas
- * can be stroked and filled with arbitrary colors.
- *
- * <p>See also the <a href="../../api/Area.html">Area guide</a>.
- *
- * @extends pv.Mark
- */
-pv.Area = function() {
- pv.Mark.call(this);
-};
-
-pv.Area.prototype = pv.extend(pv.Mark)
- .property("width")
- .property("height")
- .property("lineWidth")
- .property("strokeStyle")
- .property("fillStyle")
- .property("segmented")
- .property("interpolate");
-
-pv.Area.prototype.type = "area";
-
-/**
- * The width of a given span, in pixels; used for horizontal spans. If the width
- * is specified, the height property should be 0 (the default). Either the top
- * or bottom property should be used to space the spans vertically, typically as
- * a multiple of the index.
- *
- * @type number
- * @name pv.Area.prototype.width
- */
-
-/**
- * The height of a given span, in pixels; used for vertical spans. If the height
- * is specified, the width property should be 0 (the default). Either the left
- * or right property should be used to space the spans horizontally, typically
- * as a multiple of the index.
- *
- * @type number
- * @name pv.Area.prototype.height
- */
-
-/**
- * The width of stroked lines, in pixels; used in conjunction with
- * <tt>strokeStyle</tt> to stroke the perimeter of the area. Unlike the
- * {@link Line} mark type, the entire perimeter is stroked, rather than just one
- * edge. The default value of this property is 1.5, but since the default stroke
- * style is null, area marks are not stroked by default.
- *
- * <p>This property is <i>fixed</i> for non-segmented areas. See
- * {@link pv.Mark}.
- *
- * @type number
- * @name pv.Area.prototype.lineWidth
- */
-
-/**
- * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
- * stroke the perimeter of the area. Unlike the {@link Line} mark type, the
- * entire perimeter is stroked, rather than just one edge. The default value of
- * this property is null, meaning areas are not stroked by default.
- *
- * <p>This property is <i>fixed</i> for non-segmented areas. See
- * {@link pv.Mark}.
- *
- * @type string
- * @name pv.Area.prototype.strokeStyle
- * @see pv.color
- */
-
-/**
- * The area fill style; if non-null, the interior of the polygon forming the
- * area is filled with the specified color. The default value of this property
- * is a categorical color.
- *
- * <p>This property is <i>fixed</i> for non-segmented areas. See
- * {@link pv.Mark}.
- *
- * @type string
- * @name pv.Area.prototype.fillStyle
- * @see pv.color
- */
-
-/**
- * Whether the area is segmented; whether variations in fill style, stroke
- * style, and the other properties are treated as fixed. Rendering segmented
- * areas is noticeably slower than non-segmented areas.
- *
- * <p>This property is <i>fixed</i>. See {@link pv.Mark}.
- *
- * @type boolean
- * @name pv.Area.prototype.segmented
- */
-
-/**
- * How to interpolate between values. Linear interpolation ("linear") is the
- * default, producing a straight line between points. For piecewise constant
- * functions (i.e., step functions), either "step-before" or "step-after" can be
- * specified.
- *
- * <p>Note: this property is currently supported only on non-segmented areas.
- *
- * <p>This property is <i>fixed</i>. See {@link pv.Mark}.
- *
- * @type string
- * @name pv.Area.prototype.interpolate
- */
-
-/**
- * Default properties for areas. By default, there is no stroke and the fill
- * style is a categorical color.
- *
- * @type pv.Area
- */
-pv.Area.prototype.defaults = new pv.Area()
- .extend(pv.Mark.prototype.defaults)
- .lineWidth(1.5)
- .fillStyle(defaultFillStyle)
- .interpolate("linear");
-
-/**
- * Constructs a new area anchor with default properties. Areas support five
- * different anchors:<ul>
- *
- * <li>top
- * <li>left
- * <li>center
- * <li>bottom
- * <li>right
- *
- * </ul>In addition to positioning properties (left, right, top bottom), the
- * anchors support text rendering properties (text-align, text-baseline). Text is
- * rendered to appear inside the area polygon.
- *
- * <p>To facilitate stacking of areas, the anchors are defined in terms of their
- * opposite edge. For example, the top anchor defines the bottom property, such
- * that the area grows upwards; the bottom anchor instead defines the top
- * property, such that the area grows downwards. Of course, in general it is
- * more robust to use panels and the cousin accessor to define stacked area
- * marks; see {@link pv.Mark#scene} for an example.
- *
- * @param {string} name the anchor name; either a string or a property function.
- * @returns {pv.Anchor}
- */
-pv.Area.prototype.anchor = function(name) {
- var area = this;
- return pv.Mark.prototype.anchor.call(this, name)
- .left(function() {
- switch (this.name()) {
- case "bottom":
- case "top":
- case "center": return area.left() + area.width() / 2;
- case "right": return area.left() + area.width();
- }
- return null;
- })
- .right(function() {
- switch (this.name()) {
- case "bottom":
- case "top":
- case "center": return area.right() + area.width() / 2;
- case "left": return area.right() + area.width();
- }
- return null;
- })
- .top(function() {
- switch (this.name()) {
- case "left":
- case "right":
- case "center": return area.top() + area.height() / 2;
- case "bottom": return area.top() + area.height();
- }
- return null;
- })
- .bottom(function() {
- switch (this.name()) {
- case "left":
- case "right":
- case "center": return area.bottom() + area.height() / 2;
- case "top": return area.bottom() + area.height();
- }
- return null;
- })
- .textAlign(function() {
- switch (this.name()) {
- case "bottom":
- case "top":
- case "center": return "center";
- case "right": return "right";
- }
- return "left";
- })
- .textBaseline(function() {
- switch (this.name()) {
- case "right":
- case "left":
- case "center": return "middle";
- case "top": return "top";
- }
- return "bottom";
- });
-};
-
-/**
- * @private Overrides the default behavior of {@link pv.Mark.buildImplied} such
- * that the width and height are set to zero if null.
- *
- * @param s a node in the scene graph; the instance of the mark to build.
- */
-pv.Area.prototype.buildImplied = function(s) {
- if (s.height == null) s.height = 0;
- if (s.width == null) s.width = 0;
- pv.Mark.prototype.buildImplied.call(this, s);
-};
-
-/** @private */
-var pv_Area_specials = {left:1, top:1, right:1, bottom:1, width:1, height:1, name:1};
-
-/** @private */
-pv.Area.prototype.bind = function() {
- pv.Mark.prototype.bind.call(this);
- var binds = this.binds,
- properties = binds.properties,
- specials = binds.specials = [];
- for (var i = 0, n = properties.length; i < n; i++) {
- var p = properties[i];
- if (p.name in pv_Area_specials) specials.push(p);
- }
-};
-
-/** @private */
-pv.Area.prototype.buildInstance = function(s) {
- if (this.index && !this.scene[0].segmented) {
- this.buildProperties(s, this.binds.specials);
- this.buildImplied(s);
- } else {
- pv.Mark.prototype.buildInstance.call(this, s);
- }
-};
-/**
- * Constructs a new bar mark with default properties. Bars are not typically
- * constructed directly, but by adding to a panel or an existing mark via
- * {@link pv.Mark#add}.
- *
- * @class Represents a bar: an axis-aligned rectangle that can be stroked and
- * filled. Bars are used for many chart types, including bar charts, histograms
- * and Gantt charts. Bars can also be used as decorations, for example to draw a
- * frame border around a panel; in fact, a panel is a special type (a subclass)
- * of bar.
- *
- * <p>Bars can be positioned in several ways. Most commonly, one of the four
- * corners is fixed using two margins, and then the width and height properties
- * determine the extent of the bar relative to this fixed location. For example,
- * using the bottom and left properties fixes the bottom-left corner; the width
- * then extends to the right, while the height extends to the top. As an
- * alternative to the four corners, a bar can be positioned exclusively using
- * margins; this is convenient as an inset from the containing panel, for
- * example. See {@link pv.Mark} for details on the prioritization of redundant
- * positioning properties.
- *
- * <p>See also the <a href="../../api/Bar.html">Bar guide</a>.
- *
- * @extends pv.Mark
- */
-pv.Bar = function() {
- pv.Mark.call(this);
-};
-
-pv.Bar.prototype = pv.extend(pv.Mark)
- .property("width")
- .property("height")
- .property("lineWidth")
- .property("strokeStyle")
- .property("fillStyle");
-
-pv.Bar.prototype.type = "bar";
-
-/**
- * The width of the bar, in pixels. If the left position is specified, the bar
- * extends rightward from the left edge; if the right position is specified, the
- * bar extends leftward from the right edge.
- *
- * @type number
- * @name pv.Bar.prototype.width
- */
-
-/**
- * The height of the bar, in pixels. If the bottom position is specified, the
- * bar extends upward from the bottom edge; if the top position is specified,
- * the bar extends downward from the top edge.
- *
- * @type number
- * @name pv.Bar.prototype.height
- */
-
-/**
- * The width of stroked lines, in pixels; used in conjunction with
- * <tt>strokeStyle</tt> to stroke the bar's border.
- *
- * @type number
- * @name pv.Bar.prototype.lineWidth
- */
-
-/**
- * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
- * stroke the bar's border. The default value of this property is null, meaning
- * bars are not stroked by default.
- *
- * @type string
- * @name pv.Bar.prototype.strokeStyle
- * @see pv.color
- */
-
-/**
- * The bar fill style; if non-null, the interior of the bar is filled with the
- * specified color. The default value of this property is a categorical color.
- *
- * @type string
- * @name pv.Bar.prototype.fillStyle
- * @see pv.color
- */
-
-/**
- * Default properties for bars. By default, there is no stroke and the fill
- * style is a categorical color.
- *
- * @type pv.Bar
- */
-pv.Bar.prototype.defaults = new pv.Bar()
- .extend(pv.Mark.prototype.defaults)
- .lineWidth(1.5)
- .fillStyle(defaultFillStyle);
-
-/**
- * Constructs a new bar anchor with default properties. Bars support five
- * different anchors:<ul>
- *
- * <li>top
- * <li>left
- * <li>center
- * <li>bottom
- * <li>right
- *
- * </ul>In addition to positioning properties (left, right, top bottom), the
- * anchors support text rendering properties (text-align, text-baseline). Text
- * is rendered to appear inside the bar.
- *
- * <p>To facilitate stacking of bars, the anchors are defined in terms of their
- * opposite edge. For example, the top anchor defines the bottom property, such
- * that the bar grows upwards; the bottom anchor instead defines the top
- * property, such that the bar grows downwards. Of course, in general it is more
- * robust to use panels and the cousin accessor to define stacked bars; see
- * {@link pv.Mark#scene} for an example.
- *
- * <p>Bar anchors also "smartly" specify position properties based on whether
- * the derived mark type supports the width and height properties. If the
- * derived mark type does not support these properties (e.g., dots), the
- * position will be centered on the corresponding edge. Otherwise (e.g., bars),
- * the position will be in the opposite side.
- *
- * @param {string} name the anchor name; either a string or a property function.
- * @returns {pv.Anchor}
- */
-pv.Bar.prototype.anchor = function(name) {
- var bar = this;
- return pv.Mark.prototype.anchor.call(this, name)
- .left(function() {
- switch (this.name()) {
- case "bottom":
- case "top":
- case "center": return bar.left() + (this.properties.width ? 0 : (bar.width() / 2));
- case "right": return bar.left() + bar.width();
- }
- return null;
- })
- .right(function() {
- switch (this.name()) {
- case "bottom":
- case "top":
- case "center": return bar.right() + (this.properties.width ? 0 : (bar.width() / 2));
- case "left": return bar.right() + bar.width();
- }
- return null;
- })
- .top(function() {
- switch (this.name()) {
- case "left":
- case "right":
- case "center": return bar.top() + (this.properties.height ? 0 : (bar.height() / 2));
- case "bottom": return bar.top() + bar.height();
- }
- return null;
- })
- .bottom(function() {
- switch (this.name()) {
- case "left":
- case "right":
- case "center": return bar.bottom() + (this.properties.height ? 0 : (bar.height() / 2));
- case "top": return bar.bottom() + bar.height();
- }
- return null;
- })
- .textAlign(function() {
- switch (this.name()) {
- case "bottom":
- case "top":
- case "center": return "center";
- case "right": return "right";
- }
- return "left";
- })
- .textBaseline(function() {
- switch (this.name()) {
- case "right":
- case "left":
- case "center": return "middle";
- case "top": return "top";
- }
- return "bottom";
- });
-};
-/**
- * Constructs a new dot mark with default properties. Dots are not typically
- * constructed directly, but by adding to a panel or an existing mark via
- * {@link pv.Mark#add}.
- *
- * @class Represents a dot; a dot is simply a sized glyph centered at a given
- * point that can also be stroked and filled. The <tt>size</tt> property is
- * proportional to the area of the rendered glyph to encourage meaningful visual
- * encodings. Dots can visually encode up to eight dimensions of data, though
- * this may be unwise due to integrality. See {@link pv.Mark} for details on the
- * prioritization of redundant positioning properties.
- *
- * <p>See also the <a href="../../api/Dot.html">Dot guide</a>.
- *
- * @extends pv.Mark
- */
-pv.Dot = function() {
- pv.Mark.call(this);
-};
-
-pv.Dot.prototype = pv.extend(pv.Mark)
- .property("size")
- .property("shape")
- .property("angle")
- .property("lineWidth")
- .property("strokeStyle")
- .property("fillStyle");
-
-pv.Dot.prototype.type = "dot";
-
-/**
- * The size of the dot, in square pixels. Square pixels are used such that the
- * area of the dot is linearly proportional to the value of the size property,
- * facilitating representative encodings.
- *
- * @see #radius
- * @type number
- * @name pv.Dot.prototype.size
- */
-
-/**
- * The shape name. Several shapes are supported:<ul>
- *
- * <li>cross
- * <li>triangle
- * <li>diamond
- * <li>square
- * <li>tick
- * <li>circle
- *
- * </ul>These shapes can be further changed using the {@link #angle} property;
- * for instance, a cross can be turned into a plus by rotating. Similarly, the
- * tick, which is vertical by default, can be rotated horizontally. Note that
- * some shapes (cross and tick) do not have interior areas, and thus do not
- * support fill style meaningfully.
- *
- * <p>Note: it may be more natural to use the {@link pv.Rule} mark for
- * horizontal and vertical ticks. The tick shape is only necessary if angled
- * ticks are needed.
- *
- * @type string
- * @name pv.Dot.prototype.shape
- */
-
-/**
- * The rotation angle, in radians. Used to rotate shapes, such as to turn a
- * cross into a plus.
- *
- * @type number
- * @name pv.Dot.prototype.angle
- */
-
-/**
- * The width of stroked lines, in pixels; used in conjunction with
- * <tt>strokeStyle</tt> to stroke the dot's shape.
- *
- * @type number
- * @name pv.Dot.prototype.lineWidth
- */
-
-/**
- * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
- * stroke the dot's shape. The default value of this property is a categorical
- * color.
- *
- * @type string
- * @name pv.Dot.prototype.strokeStyle
- * @see pv.color
- */
-
-/**
- * The fill style; if non-null, the interior of the dot is filled with the
- * specified color. The default value of this property is null, meaning dots are
- * not filled by default.
- *
- * @type string
- * @name pv.Dot.prototype.fillStyle
- * @see pv.color
- */
-
-/**
- * Default properties for dots. By default, there is no fill and the stroke
- * style is a categorical color. The default shape is "circle" with size 20.
- *
- * @type pv.Dot
- */
-pv.Dot.prototype.defaults = new pv.Dot()
- .extend(pv.Mark.prototype.defaults)
- .size(20)
- .shape("circle")
- .lineWidth(1.5)
- .strokeStyle(defaultStrokeStyle);
-
-/**
- * Constructs a new dot anchor with default properties. Dots support five
- * different anchors:<ul>
- *
- * <li>top
- * <li>left
- * <li>center
- * <li>bottom
- * <li>right
- *
- * </ul>In addition to positioning properties (left, right, top bottom), the
- * anchors support text rendering properties (text-align, text-baseline). Text is
- * rendered to appear outside the dot. Note that this behavior is different from
- * other mark anchors, which default to rendering text <i>inside</i> the mark.
- *
- * <p>For consistency with the other mark types, the anchor positions are
- * defined in terms of their opposite edge. For example, the top anchor defines
- * the bottom property, such that a bar added to the top anchor grows upward.
- *
- * @param {string} name the anchor name; either a string or a property function.
- * @returns {pv.Anchor}
- */
-pv.Dot.prototype.anchor = function(name) {
- var dot = this;
- return pv.Mark.prototype.anchor.call(this, name)
- .left(function(d) {
- switch (this.name()) {
- case "bottom":
- case "top":
- case "center": return dot.left();
- case "right": return dot.left() + dot.radius();
- }
- return null;
- })
- .right(function(d) {
- switch (this.name()) {
- case "bottom":
- case "top":
- case "center": return dot.right();
- case "left": return dot.right() + dot.radius();
- }
- return null;
- })
- .top(function(d) {
- switch (this.name()) {
- case "left":
- case "right":
- case "center": return dot.top();
- case "bottom": return dot.top() + dot.radius();
- }
- return null;
- })
- .bottom(function(d) {
- switch (this.name()) {
- case "left":
- case "right":
- case "center": return dot.bottom();
- case "top": return dot.bottom() + dot.radius();
- }
- return null;
- })
- .textAlign(function(d) {
- switch (this.name()) {
- case "left": return "right";
- case "bottom":
- case "top":
- case "center": return "center";
- }
- return "left";
- })
- .textBaseline(function(d) {
- switch (this.name()) {
- case "right":
- case "left":
- case "center": return "middle";
- case "bottom": return "top";
- }
- return "bottom";
- });
-};
-
-/**
- * Returns the radius of the dot, which is defined to be the square root of the
- * {@link #size} property.
- *
- * @returns {number} the radius.
- */
-pv.Dot.prototype.radius = function() {
- return Math.sqrt(this.size());
-};
-/**
- * Constructs a new label mark with default properties. Labels are not typically
- * constructed directly, but by adding to a panel or an existing mark via
- * {@link pv.Mark#add}.
- *
- * @class Represents a text label, allowing textual annotation of other marks or
- * arbitrary text within the visualization. The character data must be plain
- * text (unicode), though the text can be styled using the {@link #font}
- * property. If rich text is needed, external HTML elements can be overlaid on
- * the canvas by hand.
- *
- * <p>Labels are positioned using the box model, similarly to {@link Dot}. Thus,
- * a label has no width or height, but merely a text anchor location. The text
- * is positioned relative to this anchor location based on the
- * {@link #textAlign}, {@link #textBaseline} and {@link #textMargin} properties.
- * Furthermore, the text may be rotated using {@link #textAngle}.
- *
- * <p>Labels ignore events, so as to not interfere with event handlers on
- * underlying marks, such as bars. In the future, we may support event handlers
- * on labels.
- *
- * <p>See also the <a href="../../api/Label.html">Label guide</a>.
- *
- * @extends pv.Mark
- */
-pv.Label = function() {
- pv.Mark.call(this);
-};
-
-pv.Label.prototype = pv.extend(pv.Mark)
- .property("text")
- .property("font")
- .property("textAngle")
- .property("textStyle")
- .property("textAlign")
- .property("textBaseline")
- .property("textMargin")
- .property("textShadow");
-
-pv.Label.prototype.type = "label";
-
-/**
- * The character data to render; a string. The default value of the text
- * property is the identity function, meaning the label's associated datum will
- * be rendered using its <tt>toString</tt>.
- *
- * @type string
- * @name pv.Label.prototype.text
- */
-
-/**
- * The font format, per the CSS Level 2 specification. The default font is "10px
- * sans-serif", for consistency with the HTML 5 canvas element specification.
- * Note that since text is not wrapped, any line-height property will be
- * ignored. The other font-style, font-variant, font-weight, font-size and
- * font-family properties are supported.
- *
- * @see <a href="http://www.w3.org/TR/CSS2/fonts.html#font-shorthand">CSS2 fonts</a>
- * @type string
- * @name pv.Label.prototype.font
- */
-
-/**
- * The rotation angle, in radians. Text is rotated clockwise relative to the
- * anchor location. For example, with the default left alignment, an angle of
- * Math.PI / 2 causes text to proceed downwards. The default angle is zero.
- *
- * @type number
- * @name pv.Label.prototype.textAngle
- */
-
-/**
- * The text color. The name "textStyle" is used for consistency with "fillStyle"
- * and "strokeStyle", although it might be better to rename this property (and
- * perhaps use the same name as "strokeStyle"). The default color is black.
- *
- * @type string
- * @name pv.Label.prototype.textStyle
- * @see pv.color
- */
-
-/**
- * The horizontal text alignment. One of:<ul>
- *
- * <li>left
- * <li>center
- * <li>right
- *
- * </ul>The default horizontal alignment is left.
- *
- * @type string
- * @name pv.Label.prototype.textAlign
- */
-
-/**
- * The vertical text alignment. One of:<ul>
- *
- * <li>top
- * <li>middle
- * <li>bottom
- *
- * </ul>The default vertical alignment is bottom.
- *
- * @type string
- * @name pv.Label.prototype.textBaseline
- */
-
-/**
- * The text margin; may be specified in pixels, or in font-dependent units (such
- * as ".1ex"). The margin can be used to pad text away from its anchor location,
- * in a direction dependent on the horizontal and vertical alignment
- * properties. For example, if the text is left- and middle-aligned, the margin
- * shifts the text to the right. The default margin is 3 pixels.
- *
- * @type number
- * @name pv.Label.prototype.textMargin
- */
-
-/**
- * A list of shadow effects to be applied to text, per the CSS Text Level 3
- * text-shadow property. An example specification is "0.1em 0.1em 0.1em
- * rgba(0,0,0,.5)"; the first length is the horizontal offset, the second the
- * vertical offset, and the third the blur radius.
- *
- * @see <a href="http://www.w3.org/TR/css3-text/#text-shadow">CSS3 text</a>
- * @type string
- * @name pv.Label.prototype.textShadow
- */
-
-/**
- * Default properties for labels. See the individual properties for the default
- * values.
- *
- * @type pv.Label
- */
-pv.Label.prototype.defaults = new pv.Label()
- .extend(pv.Mark.prototype.defaults)
- .text(pv.identity)
- .font("10px sans-serif")
- .textAngle(0)
- .textStyle("black")
- .textAlign("left")
- .textBaseline("bottom")
- .textMargin(3);
-/**
- * Constructs a new line mark with default properties. Lines are not typically
- * constructed directly, but by adding to a panel or an existing mark via
- * {@link pv.Mark#add}.
- *
- * @class Represents a series of connected line segments, or <i>polyline</i>,
- * that can be stroked with a configurable color and thickness. Each
- * articulation point in the line corresponds to a datum; for <i>n</i> points,
- * <i>n</i>-1 connected line segments are drawn. The point is positioned using
- * the box model. Arbitrary paths are also possible, allowing radar plots and
- * other custom visualizations.
- *
- * <p>Like areas, lines can be stroked and filled with arbitrary colors. In most
- * cases, lines are only stroked, but the fill style can be used to construct
- * arbitrary polygons.
- *
- * <p>See also the <a href="../../api/Line.html">Line guide</a>.
- *
- * @extends pv.Mark
- */
-pv.Line = function() {
- pv.Mark.call(this);
-};
-
-pv.Line.prototype = pv.extend(pv.Mark)
- .property("lineWidth")
- .property("strokeStyle")
- .property("fillStyle")
- .property("segmented")
- .property("interpolate");
-
-pv.Line.prototype.type = "line";
-
-/**
- * The width of stroked lines, in pixels; used in conjunction with
- * <tt>strokeStyle</tt> to stroke the line.
- *
- * @type number
- * @name pv.Line.prototype.lineWidth
- */
-
-/**
- * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
- * stroke the line. The default value of this property is a categorical color.
- *
- * @type string
- * @name pv.Line.prototype.strokeStyle
- * @see pv.color
- */
-
-/**
- * The line fill style; if non-null, the interior of the line is closed and
- * filled with the specified color. The default value of this property is a
- * null, meaning that lines are not filled by default.
- *
- * @type string
- * @name pv.Line.prototype.fillStyle
- * @see pv.color
- */
-
-/**
- * Whether the line is segmented; whether variations in stroke style, line width
- * and the other properties are treated as fixed. Rendering segmented lines is
- * noticeably slower than non-segmented lines.
- *
- * <p>This property is <i>fixed</i>. See {@link pv.Mark}.
- *
- * @type boolean
- * @name pv.Line.prototype.segmented
- */
-
-/**
- * How to interpolate between values. Linear interpolation ("linear") is the
- * default, producing a straight line between points. For piecewise constant
- * functions (i.e., step functions), either "step-before" or "step-after" can be
- * specified.
- *
- * <p>Note: this property is currently supported only on non-segmented lines.
- *
- * <p>This property is <i>fixed</i>. See {@link pv.Mark}.
- *
- * @type string
- * @name pv.Line.prototype.interpolate
- */
-
-/**
- * Default properties for lines. By default, there is no fill and the stroke
- * style is a categorical color. The default interpolation is linear.
- *
- * @type pv.Line
- */
-pv.Line.prototype.defaults = new pv.Line()
- .extend(pv.Mark.prototype.defaults)
- .lineWidth(1.5)
- .strokeStyle(defaultStrokeStyle)
- .interpolate("linear");
-
-/** @private */
-var pv_Line_specials = {left:1, top:1, right:1, bottom:1, name:1};
-
-/** @private */
-pv.Line.prototype.bind = function() {
- pv.Mark.prototype.bind.call(this);
- var binds = this.binds,
- properties = binds.properties,
- specials = binds.specials = [];
- for (var i = 0, n = properties.length; i < n; i++) {
- var p = properties[i];
- if (p.name in pv_Line_specials) specials.push(p);
- }
-};
-
-/** @private */
-pv.Line.prototype.buildInstance = function(s) {
- if (this.index && !this.scene[0].segmented) {
- this.buildProperties(s, this.binds.specials);
- this.buildImplied(s);
- } else {
- pv.Mark.prototype.buildInstance.call(this, s);
- }
-};
-/**
- * Constructs a new rule with default properties. Rules are not typically
- * constructed directly, but by adding to a panel or an existing mark via
- * {@link pv.Mark#add}.
- *
- * @class Represents a horizontal or vertical rule. Rules are frequently used
- * for axes and grid lines. For example, specifying only the bottom property
- * draws horizontal rules, while specifying only the left draws vertical
- * rules. Rules can also be used as thin bars. The visual style is controlled in
- * the same manner as lines.
- *
- * <p>Rules are positioned exclusively the standard box model properties. The
- * following combinations of properties are supported:
- *
- * <table>
- * <thead><th style="width:12em;">Properties</th><th>Orientation</th></thead>
- * <tbody>
- * <tr><td>left</td><td>vertical</td></tr>
- * <tr><td>right</td><td>vertical</td></tr>
- * <tr><td>left, bottom, top</td><td>vertical</td></tr>
- * <tr><td>right, bottom, top</td><td>vertical</td></tr>
- * <tr><td>top</td><td>horizontal</td></tr>
- * <tr><td>bottom</td><td>horizontal</td></tr>
- * <tr><td>top, left, right</td><td>horizontal</td></tr>
- * <tr><td>bottom, left, right</td><td>horizontal</td></tr>
- * <tr><td>left, top, height</td><td>vertical</td></tr>
- * <tr><td>left, bottom, height</td><td>vertical</td></tr>
- * <tr><td>right, top, height</td><td>vertical</td></tr>
- * <tr><td>right, bottom, height</td><td>vertical</td></tr>
- * <tr><td>left, top, width</td><td>horizontal</td></tr>
- * <tr><td>left, bottom, width</td><td>horizontal</td></tr>
- * <tr><td>right, top, width</td><td>horizontal</td></tr>
- * <tr><td>right, bottom, width</td><td>horizontal</td></tr>
- * </tbody>
- * </table>
- *
- * <p>Small rules can be used as tick marks; alternatively, a {@link Dot} with
- * the "tick" shape can be used.
- *
- * <p>See also the <a href="../../api/Rule.html">Rule guide</a>.
- *
- * @see pv.Line
- * @extends pv.Mark
- */
-pv.Rule = function() {
- pv.Mark.call(this);
-};
-
-pv.Rule.prototype = pv.extend(pv.Mark)
- .property("width")
- .property("height")
- .property("lineWidth")
- .property("strokeStyle");
-
-pv.Rule.prototype.type = "rule";
-
-/**
- * The width of the rule, in pixels. If the left position is specified, the rule
- * extends rightward from the left edge; if the right position is specified, the
- * rule extends leftward from the right edge.
- *
- * @type number
- * @name pv.Rule.prototype.width
- */
-
-/**
- * The height of the rule, in pixels. If the bottom position is specified, the
- * rule extends upward from the bottom edge; if the top position is specified,
- * the rule extends downward from the top edge.
- *
- * @type number
- * @name pv.Rule.prototype.height
- */
-
-/**
- * The width of stroked lines, in pixels; used in conjunction with
- * <tt>strokeStyle</tt> to stroke the rule. The default value is 1 pixel.
- *
- * @type number
- * @name pv.Rule.prototype.lineWidth
- */
-
-/**
- * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
- * stroke the rule. The default value of this property is black.
- *
- * @type string
- * @name pv.Rule.prototype.strokeStyle
- * @see pv.color
- */
-
-/**
- * Default properties for rules. By default, a single-pixel black line is
- * stroked.
- *
- * @type pv.Rule
- */
-pv.Rule.prototype.defaults = new pv.Rule()
- .extend(pv.Mark.prototype.defaults)
- .lineWidth(1)
- .strokeStyle("black");
-
-/**
- * Constructs a new rule anchor with default properties. Rules support five
- * different anchors:<ul>
- *
- * <li>top
- * <li>left
- * <li>center
- * <li>bottom
- * <li>right
- *
- * </ul>In addition to positioning properties (left, right, top bottom), the
- * anchors support text rendering properties (text-align, text-baseline). Text is
- * rendered to appear outside the rule. Note that this behavior is different
- * from other mark anchors, which default to rendering text <i>inside</i> the
- * mark.
- *
- * <p>For consistency with the other mark types, the anchor positions are
- * defined in terms of their opposite edge. For example, the top anchor defines
- * the bottom property, such that a bar added to the top anchor grows upward.
- *
- * @param {string} name the anchor name; either a string or a property function.
- * @returns {pv.Anchor}
- */
-pv.Rule.prototype.anchor = function(name) {
- return pv.Bar.prototype.anchor.call(this, name)
- .textAlign(function(d) {
- switch (this.name()) {
- case "left": return "right";
- case "bottom":
- case "top":
- case "center": return "center";
- case "right": return "left";
- }
- })
- .textBaseline(function(d) {
- switch (this.name()) {
- case "right":
- case "left":
- case "center": return "middle";
- case "top": return "bottom";
- case "bottom": return "top";
- }
- });
-};
-
-/**
- * @private Overrides the default behavior of {@link pv.Mark.buildImplied} to
- * determine the orientation (vertical or horizontal) of the rule.
- *
- * @param s a node in the scene graph; the instance of the rule to build.
- */
-pv.Rule.prototype.buildImplied = function(s) {
- var l = s.left, r = s.right, t = s.top, b = s.bottom;
-
- /* Determine horizontal or vertical orientation. */
- if ((s.width != null)
- || ((l == null) && (r == null))
- || ((r != null) && (l != null))) {
- s.height = 0;
- } else {
- s.width = 0;
- }
-
- pv.Mark.prototype.buildImplied.call(this, s);
-};
-/**
- * Constructs a new, empty panel with default properties. Panels, with the
- * exception of the root panel, are not typically constructed directly; instead,
- * they are added to an existing panel or mark via {@link pv.Mark#add}.
- *
- * @class Represents a container mark. Panels allow repeated or nested
- * structures, commonly used in small multiple displays where a small
- * visualization is tiled to facilitate comparison across one or more
- * dimensions. Other types of visualizations may benefit from repeated and
- * possibly overlapping structure as well, such as stacked area charts. Panels
- * can also offset the position of marks to provide padding from surrounding
- * content.
- *
- * <p>All Protovis displays have at least one panel; this is the root panel to
- * which marks are rendered. The box model properties (four margins, width and
- * height) are used to offset the positions of contained marks. The data
- * property determines the panel count: a panel is generated once per associated
- * datum. When nested panels are used, property functions can declare additional
- * arguments to access the data associated with enclosing panels.
- *
- * <p>Panels can be rendered inline, facilitating the creation of sparklines.
- * This allows designers to reuse browser layout features, such as text flow and
- * tables; designers can also overlay HTML elements such as rich text and
- * images.
- *
- * <p>All panels have a <tt>children</tt> array (possibly empty) containing the
- * child marks in the order they were added. Panels also have a <tt>root</tt>
- * field which points to the root (outermost) panel; the root panel's root field
- * points to itself.
- *
- * <p>See also the <a href="../../api/">Protovis guide</a>.
- *
- * @extends pv.Bar
- */
-pv.Panel = function() {
- pv.Bar.call(this);
-
- /**
- * The child marks; zero or more {@link pv.Mark}s in the order they were
- * added.
- *
- * @see #add
- * @type pv.Mark[]
- */
- this.children = [];
- this.root = this;
-
- /**
- * The internal $dom field is set by the Protovis loader; see lang/init.js. It
- * refers to the script element that contains the Protovis specification, so
- * that the panel knows where in the DOM to insert the generated SVG element.
- *
- * @private
- */
- this.$dom = pv.Panel.$dom;
-};
-
-pv.Panel.prototype = pv.extend(pv.Bar)
- .property("canvas")
- .property("overflow");
-
-pv.Panel.prototype.type = "panel";
-
-/**
- * The canvas element; either the string ID of the canvas element in the current
- * document, or a reference to the canvas element itself. If null, a canvas
- * element will be created and inserted into the document at the location of the
- * script element containing the current Protovis specification. This property
- * only applies to root panels and is ignored on nested panels.
- *
- * <p>Note: the "canvas" element here refers to a <tt>div</tt> (or other suitable
- * HTML container element), <i>not</i> a <tt>canvas</tt> element. The name of
- * this property is a historical anachronism from the first implementation that
- * used HTML 5 canvas, rather than SVG.
- *
- * @type string
- * @name pv.Panel.prototype.canvas
- */
-
-/**
- * Default properties for panels. By default, the margins are zero, the fill
- * style is transparent.
- *
- * @type pv.Panel
- */
-pv.Panel.prototype.defaults = new pv.Panel()
- .extend(pv.Bar.prototype.defaults)
- .fillStyle(null)
- .overflow("visible");
-
-/**
- * Returns an anchor with the specified name. This method is overridden since
- * the behavior of Panel anchors is slightly different from normal anchors:
- * adding to an anchor adds to the anchor target's, rather than the anchor
- * target's parent. To avoid double margins, we override the anchor's proto so
- * that the margins are zero.
- *
- * @param {string} name the anchor name; either a string or a property function.
- * @returns {pv.Anchor} the new anchor.
- */
-pv.Panel.prototype.anchor = function(name) {
-
- /* A "view" of this panel whose margins appear to be zero. */
- function z() { return 0; }
- z.prototype = this;
- z.prototype.left = z.prototype.right = z.prototype.top = z.prototype.bottom = z;
-
- var anchor = pv.Bar.prototype.anchor.call(new z(), name)
- .data(function(d) { return [d]; });
- anchor.parent = this;
- return anchor;
-};
-
-/**
- * Adds a new mark of the specified type to this panel. Unlike the normal
- * {@link Mark#add} behavior, adding a mark to a panel does not cause the mark
- * to inherit from the panel. Since the contained marks are offset by the panel
- * margins already, inheriting properties is generally undesirable; of course,
- * it is always possible to change this behavior by calling {@link Mark#extend}
- * explicitly.
- *
- * @param {function} type the type of the new mark to add.
- * @returns {pv.Mark} the new mark.
- */
-pv.Panel.prototype.add = function(type) {
- var child = new type();
- child.parent = this;
- child.root = this.root;
- child.childIndex = this.children.length;
- this.children.push(child);
- return child;
-};
-
-/** @private TODO */
-pv.Panel.prototype.bind = function() {
- pv.Mark.prototype.bind.call(this);
- for (var i = 0; i < this.children.length; i++) {
- this.children[i].bind();
- }
-};
-
-/**
- * @private Evaluates all of the properties for this panel for the specified
- * instance <tt>s</tt> in the scene graph, including recursively building the
- * scene graph for child marks.
- *
- * @param s a node in the scene graph; the instance of the panel to build.
- * @see Mark#scene
- */
-pv.Panel.prototype.buildInstance = function(s) {
- pv.Bar.prototype.buildInstance.call(this, s);
- if (!s.children) s.children = [];
-
- /*
- * Build each child, passing in the parent (this panel) scene graph node. The
- * child mark's scene is initialized from the corresponding entry in the
- * existing scene graph, such that properties from the previous build can be
- * reused; this is largely to facilitate the recycling of SVG elements.
- */
- for (var i = 0; i < this.children.length; i++) {
- this.children[i].scene = s.children[i]; // possibly undefined
- this.children[i].build();
- }
-
- /*
- * Once the child marks have been built, the new scene graph nodes are removed
- * from the child marks and placed into the scene graph. The nodes cannot
- * remain on the child nodes because this panel (or a parent panel) may be
- * instantiated multiple times!
- */
- for (var i = 0; i < this.children.length; i++) {
- s.children[i] = this.children[i].scene;
- delete this.children[i].scene;
- }
-
- /* Delete any expired child scenes, should child marks have been removed. */
- s.children.length = this.children.length;
-};
-
-/**
- * @private Computes the implied properties for this panel for the specified
- * instance <tt>s</tt> in the scene graph. Panels have two implied
- * properties:<ul>
- *
- * <li>The <tt>canvas</tt> property references the DOM element, typically a DIV,
- * that contains the SVG element that is used to display the visualization. This
- * property may be specified as a string, referring to the unique ID of the
- * element in the DOM. The string is converted to a reference to the DOM
- * element. The width and height of the SVG element is inferred from this DOM
- * element. If no canvas property is specified, a new SVG element is created and
- * inserted into the document, using the panel dimensions; see
- * {@link #createCanvas}.
- *
- * <li>The <tt>children</tt> array, while not a property per se, contains the
- * scene graph for each child mark. This array is initialized to be empty, and
- * is populated above in {@link #buildInstance}.
- *
- * </ul>The current implementation creates the SVG element, if necessary, during
- * the build phase; in the future, it may be preferrable to move this to the
- * update phase, although then the canvas property would be undefined. In
- * addition, DOM inspection is necessary to define the implied width and height
- * properties that may be inferred from the DOM.
- *
- * @param s a node in the scene graph; the instance of the panel to build.
- */
-pv.Panel.prototype.buildImplied = function(s) {
- if (!this.parent) {
- var c = s.canvas;
- if (c) {
- if (typeof c == "string") c = document.getElementById(c);
-
- /* Clear the container if it's not associated with this panel. */
- if (c.$panel != this) {
- c.$panel = this;
- c.innerHTML = "";
- }
-
- /* If width and height weren't specified, inspect the container. */
- var w, h;
- if (s.width == null) {
- w = parseFloat(pv.css(c, "width"));
- s.width = w - s.left - s.right;
- }
- if (s.height == null) {
- h = parseFloat(pv.css(c, "height"));
- s.height = h - s.top - s.bottom;
- }
- } else if (s.$canvas) {
-
- /*
- * If the canvas property is null, and we previously created a canvas for
- * this scene node, reuse the previous canvas rather than creating a new
- * one.
- */
- c = s.$canvas;
- } else {
-
- /**
- * Returns the last element in the current document's body. The canvas
- * element is appended to this last element if another DOM element has not
- * already been specified via the <tt>$dom</tt> field.
- */
- function lastElement() {
- var node = document.body;
- while (node.lastChild && node.lastChild.tagName) {
- node = node.lastChild;
- }
- return (node == document.body) ? node : node.parentNode;
- }
-
- /* Insert a new container into the DOM. */
- c = s.$canvas = document.createElement("span");
- this.$dom // script element for text/javascript+protovis
- ? this.$dom.parentNode.insertBefore(c, this.$dom)
- : lastElement().appendChild(c);
- }
- s.canvas = c;
- }
- pv.Bar.prototype.buildImplied.call(this, s);
-};
-/**
- * Constructs a new dot mark with default properties. Images are not typically
- * constructed directly, but by adding to a panel or an existing mark via
- * {@link pv.Mark#add}.
- *
- * @class Represents an image. Images share the same layout and style properties as
- * bars, in conjunction with an external image such as PNG or JPEG. The image is
- * specified via the {@link #url} property. The fill, if specified, appears
- * beneath the image, while the optional stroke appears above the image.
- *
- * <p>TODO Restore support for dynamic images (such as heatmaps). These were
- * supported in the canvas implementation using the pixel buffer API; although
- * SVG does not support pixel manipulation, it is possible to embed a canvas
- * element in SVG using foreign objects.
- *
- * <p>TODO Allow different modes of image placement: "scale" -- scale and
- * preserve aspect ratio, "tile" -- repeat the image, "center" -- center the
- * image, "fill" -- scale without preserving aspect ratio.
- *
- * <p>See {@link pv.Bar} for details on positioning properties.
- *
- * @extends pv.Bar
- */
-pv.Image = function() {
- pv.Bar.call(this);
-};
-
-pv.Image.prototype = pv.extend(pv.Bar)
- .property("url");
-
-pv.Image.prototype.type = "image";
-
-/**
- * The URL of the image to display. The set of supported image types is
- * browser-dependent; PNG and JPEG are recommended.
- *
- * @type string
- * @name pv.Image.prototype.url
- */
-
-/**
- * Default properties for images. By default, there is no stroke or fill style.
- *
- * @type pv.Image
- */
-pv.Image.prototype.defaults = new pv.Image()
- .extend(pv.Bar.prototype.defaults)
- .fillStyle(null);
-/**
- * Constructs a new wedge with default properties. Wedges are not typically
- * constructed directly, but by adding to a panel or an existing mark via
- * {@link pv.Mark#add}.
- *
- * @class Represents a wedge, or pie slice. Specified in terms of start and end
- * angle, inner and outer radius, wedges can be used to construct donut charts
- * and polar bar charts as well. If the {@link #angle} property is used, the end
- * angle is implied by adding this value to start angle. By default, the start
- * angle is the previously-generated wedge's end angle. This design allows
- * explicit control over the wedge placement if desired, while offering
- * convenient defaults for the construction of radial graphs.
- *
- * <p>The center point of the circle is positioned using the standard box model.
- * The wedge can be stroked and filled, similar to {link Bar}.
- *
- * <p>See also the <a href="../../api/Wedge.html">Wedge guide</a>.
- *
- * @extends pv.Mark
- */
-pv.Wedge = function() {
- pv.Mark.call(this);
-};
-
-pv.Wedge.prototype = pv.extend(pv.Mark)
- .property("startAngle")
- .property("endAngle")
- .property("angle")
- .property("innerRadius")
- .property("outerRadius")
- .property("lineWidth")
- .property("strokeStyle")
- .property("fillStyle");
-
-pv.Wedge.prototype.type = "wedge";
-
-/**
- * The start angle of the wedge, in radians. The start angle is measured
- * clockwise from the 3 o'clock position. The default value of this property is
- * the end angle of the previous instance (the {@link Mark#sibling}), or -PI / 2
- * for the first wedge; for pie and donut charts, typically only the
- * {@link #angle} property needs to be specified.
- *
- * @type number
- * @name pv.Wedge.prototype.startAngle
- */
-
-/**
- * The end angle of the wedge, in radians. If not specified, the end angle is
- * implied as the start angle plus the {@link #angle}.
- *
- * @type number
- * @name pv.Wedge.prototype.endAngle
- */
-
-/**
- * The angular span of the wedge, in radians. This property is used if end angle
- * is not specified.
- *
- * @type number
- * @name pv.Wedge.prototype.angle
- */
-
-/**
- * The inner radius of the wedge, in pixels. The default value of this property
- * is zero; a positive value will produce a donut slice rather than a pie slice.
- * The inner radius can vary per-wedge.
- *
- * @type number
- * @name pv.Wedge.prototype.innerRadius
- */
-
-/**
- * The outer radius of the wedge, in pixels. This property is required. For
- * pies, only this radius is required; for donuts, the inner radius must be
- * specified as well. The outer radius can vary per-wedge.
- *
- * @type number
- * @name pv.Wedge.prototype.outerRadius
- */
-
-/**
- * The width of stroked lines, in pixels; used in conjunction with
- * <tt>strokeStyle</tt> to stroke the wedge's border.
- *
- * @type number
- * @name pv.Wedge.prototype.lineWidth
- */
-
-/**
- * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
- * stroke the wedge's border. The default value of this property is null,
- * meaning wedges are not stroked by default.
- *
- * @type string
- * @name pv.Wedge.prototype.strokeStyle
- * @see pv.color
- */
-
-/**
- * The wedge fill style; if non-null, the interior of the wedge is filled with
- * the specified color. The default value of this property is a categorical
- * color.
- *
- * @type string
- * @name pv.Wedge.prototype.fillStyle
- * @see pv.color
- */
-
-/**
- * Default properties for wedges. By default, there is no stroke and the fill
- * style is a categorical color.
- *
- * @type pv.Wedge
- */
-pv.Wedge.prototype.defaults = new pv.Wedge()
- .extend(pv.Mark.prototype.defaults)
- .startAngle(function() {
- var s = this.sibling();
- return s ? s.endAngle : -Math.PI / 2;
- })
- .innerRadius(0)
- .lineWidth(1.5)
- .strokeStyle(null)
- .fillStyle(defaultFillStyle.by(pv.index));
-
-/**
- * Returns the mid-radius of the wedge, which is defined as half-way between the
- * inner and outer radii.
- *
- * @see #innerRadius
- * @see #outerRadius
- * @returns {number} the mid-radius, in pixels.
- */
-pv.Wedge.prototype.midRadius = function() {
- return (this.innerRadius() + this.outerRadius()) / 2;
-};
-
-/**
- * Returns the mid-angle of the wedge, which is defined as half-way between the
- * start and end angles.
- *
- * @see #startAngle
- * @see #endAngle
- * @returns {number} the mid-angle, in radians.
- */
-pv.Wedge.prototype.midAngle = function() {
- return (this.startAngle() + this.endAngle()) / 2;
-};
-
-/**
- * Constructs a new wedge anchor with default properties. Wedges support five
- * different anchors:<ul>
- *
- * <li>outer
- * <li>inner
- * <li>center
- * <li>start
- * <li>end
- *
- * </ul>In addition to positioning properties (left, right, top bottom), the
- * anchors support text rendering properties (text-align, text-baseline,
- * textAngle). Text is rendered to appear inside the wedge.
- *
- * @param {string} name the anchor name; either a string or a property function.
- * @returns {pv.Anchor}
- */
-pv.Wedge.prototype.anchor = function(name) {
- var w = this;
- return pv.Mark.prototype.anchor.call(this, name)
- .left(function() {
- switch (this.name()) {
- case "outer": return w.left() + w.outerRadius() * Math.cos(w.midAngle());
- case "inner": return w.left() + w.innerRadius() * Math.cos(w.midAngle());
- case "start": return w.left() + w.midRadius() * Math.cos(w.startAngle());
- case "center": return w.left() + w.midRadius() * Math.cos(w.midAngle());
- case "end": return w.left() + w.midRadius() * Math.cos(w.endAngle());
- }
- })
- .right(function() {
- switch (this.name()) {
- case "outer": return w.right() + w.outerRadius() * Math.cos(w.midAngle());
- case "inner": return w.right() + w.innerRadius() * Math.cos(w.midAngle());
- case "start": return w.right() + w.midRadius() * Math.cos(w.startAngle());
- case "center": return w.right() + w.midRadius() * Math.cos(w.midAngle());
- case "end": return w.right() + w.midRadius() * Math.cos(w.endAngle());
- }
- })
- .top(function() {
- switch (this.name()) {
- case "outer": return w.top() + w.outerRadius() * Math.sin(w.midAngle());
- case "inner": return w.top() + w.innerRadius() * Math.sin(w.midAngle());
- case "start": return w.top() + w.midRadius() * Math.sin(w.startAngle());
- case "center": return w.top() + w.midRadius() * Math.sin(w.midAngle());
- case "end": return w.top() + w.midRadius() * Math.sin(w.endAngle());
- }
- })
- .bottom(function() {
- switch (this.name()) {
- case "outer": return w.bottom() + w.outerRadius() * Math.sin(w.midAngle());
- case "inner": return w.bottom() + w.innerRadius() * Math.sin(w.midAngle());
- case "start": return w.bottom() + w.midRadius() * Math.sin(w.startAngle());
- case "center": return w.bottom() + w.midRadius() * Math.sin(w.midAngle());
- case "end": return w.bottom() + w.midRadius() * Math.sin(w.endAngle());
- }
- })
- .textAlign(function() {
- switch (this.name()) {
- case "outer": return pv.Wedge.upright(w.midAngle()) ? "right" : "left";
- case "inner": return pv.Wedge.upright(w.midAngle()) ? "left" : "right";
- }
- return "center";
- })
- .textBaseline(function() {
- switch (this.name()) {
- case "start": return pv.Wedge.upright(w.startAngle()) ? "top" : "bottom";
- case "end": return pv.Wedge.upright(w.endAngle()) ? "bottom" : "top";
- }
- return "middle";
- })
- .textAngle(function() {
- var a = 0;
- switch (this.name()) {
- case "center":
- case "inner":
- case "outer": a = w.midAngle(); break;
- case "start": a = w.startAngle(); break;
- case "end": a = w.endAngle(); break;
- }
- return pv.Wedge.upright(a) ? a : (a + Math.PI);
- });
-};
-
-/**
- * Returns true if the specified angle is considered "upright", as in, text
- * rendered at that angle would appear upright. If the angle is not upright,
- * text is rotated 180 degrees to be upright, and the text alignment properties
- * are correspondingly changed.
- *
- * @param {number} angle an angle, in radius.
- * @returns {boolean} true if the specified angle is upright.
- */
-pv.Wedge.upright = function(angle) {
- angle = angle % (2 * Math.PI);
- angle = (angle < 0) ? (2 * Math.PI + angle) : angle;
- return (angle < Math.PI / 2) || (angle > 3 * Math.PI / 2);
-};
-
-/**
- * @private Overrides the default behavior of {@link pv.Mark.buildImplied} such
- * that the end angle is computed from the start angle and angle (angular span)
- * if not specified.
- *
- * @param s a node in the scene graph; the instance of the wedge to build.
- */
-pv.Wedge.prototype.buildImplied = function(s) {
- pv.Mark.prototype.buildImplied.call(this, s);
-
- /*
- * TODO If the angle or endAngle is updated by an event handler, the implied
- * properties won't recompute correctly, so this will lead to potentially
- * buggy redraw. How to re-evaluate implied properties on update?
- */
- if (s.endAngle == null) s.endAngle = s.startAngle + s.angle;
- if (s.angle == null) s.angle = s.endAngle - s.startAngle;
-};
-/**
- * @ignore
- * @namespace
- */
-pv.Layout = {};
-/**
- * Returns a new grid layout.
- *
- * @class A grid layout with regularly-sized rows and columns. <img
- * src="../grid.png" width="160" height="160" align="right"> The number of rows
- * and columns are determined from the array, which should be in row-major
- * order. For example, the 2&times;3 array:
- *
- * <pre>1 2 3
- * 4 5 6</pre>
- *
- * should be represented as:
- *
- * <pre>[[1, 2, 3], [4, 5, 6]]</pre>
- *
- * If your data is in column-major order, you can use {@link pv.transpose} to
- * transpose it.
- *
- * <p>This layout defines left, top, width, height and data properties. The data
- * property will be the associated element in the array. For example, if the
- * array is a two-dimensional array of values in the range [0,1], a simple
- * heatmap can be generated as:
- *
- * <pre>.add(pv.Bar)
- * .extend(pv.Layout.grid(array))
- * .fillStyle(pv.ramp("white", "black"))</pre>
- *
- * By default, the grid fills the full width and height of the parent panel.
- *
- * @param {array[]} arrays an array of arrays.
- * @returns {pv.Layout.grid} a grid layout.
- */
-pv.Layout.grid = function(arrays) {
- var rows = arrays.length, cols = arrays[0].length;
-
- /** @private */
- function w() { return this.parent.width() / cols; }
-
- /** @private */
- function h() { return this.parent.height() / rows; }
-
- /* A dummy mark, like an anchor, which the caller extends. */
- return new pv.Mark()
- .data(pv.blend(arrays))
- .left(function() { return w.call(this) * (this.index % cols); })
- .top(function() { return h.call(this) * Math.floor(this.index / cols); })
- .width(w)
- .height(h);
-};
-/**
- * Returns a new stack layout.
- *
- * @class A layout for stacking marks vertically or horizontally, using the
- * <i>cousin</i> instance. This layout is designed to be used for one of the
- * four positional properties in the box model, and changes behavior depending
- * on the property being evaluated:<ul>
- *
- * <li>bottom: cousin.bottom + cousin.height
- * <li>top: cousin.top + cousin.height
- * <li>left: cousin.left + cousin.width
- * <li>right: cousin.right + cousin.width
- *
- * </ul>If no cousin instance is available (for example, for first instance),
- * the specified offset is used. If no offset is specified, zero is used. For
- * example,
- *
- * <pre>new pv.Panel()
- * .width(150).height(150)
- * .add(pv.Panel)
- * .data([[1, 1.2, 1.7, 1.5, 1.7],
- * [.5, 1, .8, 1.1, 1.3],
- * [.2, .5, .8, .9, 1]])
- * .add(pv.Area)
- * .data(function(d) d)
- * .bottom(pv.Layout.stack())
- * .height(function(d) d * 40)
- * .left(function() this.index * 35)
- * .root.render();</pre>
- *
- * specifies a vertically-stacked area chart.
- *
- * @returns {pv.Layout.stack} a stack property function.
- * @see pv.Mark#cousin
- */
-pv.Layout.stack = function() {
- /** @private */
- var offset = function() { return 0; };
-
- /** @private */
- function layout() {
-
- /* Find the previous visible parent instance. */
- var i = this.parent.index, p, c;
- while ((i-- > 0) && !c) {
- p = this.parent.scene[i];
- if (p.visible) c = p.children[this.childIndex][this.index];
- }
-
- if (c) {
- switch (property) {
- case "bottom": return c.bottom + c.height;
- case "top": return c.top + c.height;
- case "left": return c.left + c.width;
- case "right": return c.right + c.width;
- }
- }
-
- return offset.apply(this, arguments);
- }
-
- /**
- * Sets the offset for this stack layout. The offset can either be specified
- * as a function or as a constant. If a function, the function is invoked in
- * the same context as a normal property function: <tt>this</tt> refers to the
- * mark, and the arguments are the full data stack. By default the offset is
- * zero.
- *
- * @function
- * @name pv.Layout.stack.prototype.offset
- * @param {function} f offset function, or constant value.
- * @returns {pv.Layout.stack} this.
- */
- layout.offset = function(f) {
- offset = (f instanceof Function) ? f : function() { return f; };
- return this;
- };
-
- return layout;
-};
-// TODO share code with Treemap
-// TODO vertical / horizontal orientation?
-
-/**
- * Returns a new icicle tree layout.
- *
- * @class A tree layout in the form of an icicle. <img src="../icicle.png"
- * width="160" height="160" align="right"> The first row corresponds to the root
- * of the tree; subsequent rows correspond to each tier. Rows are subdivided
- * into cells based on the size of nodes, per {@link #size}. Within a row, cells
- * are sorted by size.
- *
- * <p>This tree layout is intended to be extended (see {@link pv.Mark#extend})
- * by a {@link pv.Bar}. The data property returns an array of nodes for use by
- * other property functions. The following node attributes are supported:
- *
- * <ul>
- * <li><tt>left</tt> - the cell left position.
- * <li><tt>top</tt> - the cell top position.
- * <li><tt>width</tt> - the cell width.
- * <li><tt>height</tt> - the cell height.
- * <li><tt>depth</tt> - the node depth (tier; the root is 0).
- * <li><tt>keys</tt> - an array of string keys for the node.
- * <li><tt>size</tt> - the aggregate node size.
- * <li><tt>children</tt> - child nodes, if any.
- * <li><tt>data</tt> - the associated tree element, for leaf nodes.
- * </ul>
- *
- * To produce a default icicle layout, say:
- *
- * <pre>.add(pv.Bar)
- * .extend(pv.Layout.icicle(tree))</pre>
- *
- * To customize the tree to highlight leaf nodes bigger than 10,000 (1E4), you
- * might say:
- *
- * <pre>.add(pv.Bar)
- * .extend(pv.Layout.icicle(tree))
- * .fillStyle(function(n) n.data > 1e4 ? "#ff0" : "#fff")</pre>
- *
- * The format of the <tt>tree</tt> argument is any hierarchical object whose
- * leaf nodes are numbers corresponding to their size. For an example, and
- * information on how to convert tabular data into such a tree, see
- * {@link pv.Tree}. If the leaf nodes are not numbers, a {@link #size} function
- * can be specified to override how the tree is interpreted. This size function
- * can also be used to transform the data.
- *
- * <p>By default, the icicle fills the full width and height of the parent
- * panel. An optional root key can be specified using {@link #root} for
- * convenience.
- *
- * @param tree a tree (an object) who leaf attributes have sizes.
- * @returns {pv.Layout.icicle} a tree layout.
- */
-pv.Layout.icicle = function(tree) {
- var keys = [], sizeof = Number;
-
- /** @private */
- function accumulate(map) {
- var node = {size: 0, children: [], keys: keys.slice()};
- for (var key in map) {
- var child = map[key], size = sizeof(child);
- keys.push(key);
- if (isNaN(size)) {
- child = accumulate(child);
- } else {
- child = {size: size, data: child, keys: keys.slice()};
- }
- node.children.push(child);
- node.size += child.size;
- keys.pop();
- }
- node.children.sort(function(a, b) { return b.size - a.size; });
- return node;
- }
-
- /** @private */
- function scale(node, k) {
- node.size *= k;
- if (node.children) {
- for (var i = 0; i < node.children.length; i++) {
- scale(node.children[i], k);
- }
- }
- }
-
- /** @private */
- function depth(node, i) {
- i = i ? (i + 1) : 1;
- return node.children
- ? pv.max(node.children, function(n) { return depth(n, i); })
- : i;
- }
-
- /** @private */
- function layout(node) {
- if (node.children) {
- icify(node);
- for (var i = 0; i < node.children.length; i++) {
- layout(node.children[i]);
- }
- }
- }
-
- /** @private */
- function icify(node) {
- var left = node.left;
- for (var i = 0; i < node.children.length; i++) {
- var child = node.children[i], width = (child.size / node.size) * node.width;
- child.left = left;
- child.top = node.top + node.height;
- child.width = width;
- child.height = node.height;
- child.depth = node.depth + 1;
- left += width;
- if (child.children) {
- icify(child);
- }
- }
- }
-
- /** @private */
- function flatten(node, array) {
- if (node.children) {
- for (var i = 0; i < node.children.length; i++) {
- flatten(node.children[i], array);
- }
- }
- array.push(node)
- return array;
- }
-
- /** @private */
- function data() {
- var root = accumulate(tree);
- root.top = 0;
- root.left = 0;
- root.width = this.parent.width();
- root.height = this.parent.height() / depth(root);
- root.depth = 0;
- layout(root);
- return flatten(root, []).reverse();
- }
-
- /* A dummy mark, like an anchor, which the caller extends. */
- var mark = new pv.Mark()
- .data(data)
- .left(function(n) { return n.left; })
- .top(function(n) { return n.top; })
- .width(function(n) { return n.width; })
- .height(function(n) { return n.height; });
-
- /**
- * Specifies the root key; optional. The root key is prepended to the
- * <tt>keys</tt> attribute for all generated nodes. This method is provided
- * for convenience and does not affect layout.
- *
- * @param {string} v the root key.
- * @function
- * @name pv.Layout.icicle.prototype.root
- * @returns {pv.Layout.icicle} this.
- */
- mark.root = function(v) {
- keys = [v];
- return this;
- };
-
- /**
- * Specifies the sizing function. By default, the sizing function is
- * <tt>Number</tt>. The sizing function is invoked for each node in the tree
- * (passed to the constructor): the sizing function must return
- * <tt>undefined</tt> or <tt>NaN</tt> for internal nodes, and a number for
- * leaf nodes. The aggregate sizes of internal nodes will be automatically
- * computed by the layout.
- *
- * <p>For example, if the tree data structure represents a file system, with
- * files as leaf nodes, and each file has a <tt>bytes</tt> attribute, you can
- * specify a size function as:
- *
- * <pre>.size(function(d) d.bytes)</pre>
- *
- * This function will return <tt>undefined</tt> for internal nodes (since
- * these do not have a <tt>bytes</tt> attribute), and a number for leaf nodes.
- *
- * <p>Note that the built-in <tt>Math.sqrt</tt> and <tt>Math.log</tt> methods
- * can also be used as sizing functions. These function similarly to
- * <tt>Number</tt>, except perform a root and log scale, respectively.
- *
- * @param {function} f the new sizing function.
- * @function
- * @name pv.Layout.icicle.prototype.size
- * @returns {pv.Layout.icicle} this.
- */
- mark.size = function(f) {
- sizeof = f;
- return this;
- };
-
- return mark;
-};
-// TODO share code with Treemap
-// TODO inspect parent panel dimensions to set inner and outer radii
-
-/**
- * Returns a new sunburst tree layout.
- *
- * @class A tree layout in the form of a sunburst. <img
- * src="../sunburst.png" width="160" height="160" align="right"> The
- * center circle corresponds to the root of the tree; subsequent rings
- * correspond to each tier. Rings are subdivided into wedges based on the size
- * of nodes, per {@link #size}. Within a ring, wedges are sorted by size.
- *
- * <p>The tree layout is intended to be extended (see {@link pv.Mark#extend} by
- * a {@link pv.Wedge}. The data property returns an array of nodes for use by
- * other property functions. The following node attributes are supported:
- *
- * <ul>
- * <li><tt>left</tt> - the wedge left position.
- * <li><tt>top</tt> - the wedge top position.
- * <li><tt>innerRadius</tt> - the wedge inner radius.
- * <li><tt>outerRadius</tt> - the wedge outer radius.
- * <li><tt>startAngle</tt> - the wedge start angle.
- * <li><tt>endAngle</tt> - the wedge end angle.
- * <li><tt>angle</tt> - the wedge angle.
- * <li><tt>depth</tt> - the node depth (tier; the root is 0).
- * <li><tt>keys</tt> - an array of string keys for the node.
- * <li><tt>size</tt> - the aggregate node size.
- * <li><tt>children</tt> - child nodes, if any.
- * <li><tt>data</tt> - the associated tree element, for leaf nodes.
- * </ul>
- *
- * <p>To produce a default sunburst layout, say:
- *
- * <pre>.add(pv.Wedge)
- * .extend(pv.Layout.sunburst(tree))</pre>
- *
- * To only show nodes at a depth of two or greater, you might say:
- *
- * <pre>.add(pv.Wedge)
- * .extend(pv.Layout.sunburst(tree))
- * .visible(function(n) n.depth > 1)</pre>
- *
- * The format of the <tt>tree</tt> argument is a hierarchical object whose leaf
- * nodes are numbers corresponding to their size. For an example, and
- * information on how to convert tabular data into such a tree, see
- * {@link pv.Tree}. If the leaf nodes are not numbers, a {@link #size} function
- * can be specified to override how the tree is interpreted. This size function
- * can also be used to transform the data.
- *
- * <p>By default, the sunburst fills the full width and height of the parent
- * panel. An optional root key can be specified using {@link #root} for
- * convenience.
- *
- * @param tree a tree (an object) who leaf attributes have sizes.
- * @returns {pv.Layout.sunburst} a tree layout.
- */
-pv.Layout.sunburst = function(tree) {
- var keys = [], sizeof = Number, w, h, r;
-
- /** @private */
- function accumulate(map) {
- var node = {size: 0, children: [], keys: keys.slice()};
- for (var key in map) {
- var child = map[key], size = sizeof(child);
- keys.push(key);
- if (isNaN(size)) {
- child = accumulate(child);
- } else {
- child = {size: size, data: child, keys: keys.slice()};
- }
- node.children.push(child);
- node.size += child.size;
- keys.pop();
- }
- node.children.sort(function(a, b) { return b.size - a.size; });
- return node;
- }
-
- /** @private */
- function scale(node, k) {
- node.size *= k;
- if (node.children) {
- for (var i = 0; i < node.children.length; i++) {
- scale(node.children[i], k);
- }
- }
- }
-
- /** @private */
- function depth(node, i) {
- i = i ? (i + 1) : 1;
- return node.children
- ? pv.max(node.children, function(n) { return depth(n, i); })
- : i;
- }
-
- /** @private */
- function layout(node) {
- if (node.children) {
- wedgify(node);
- for (var i = 0; i < node.children.length; i++) {
- layout(node.children[i]);
- }
- }
- }
-
- /** @private */
- function wedgify(node) {
- var startAngle = node.startAngle;
- for (var i = 0; i < node.children.length; i++) {
- var child = node.children[i], angle = (child.size / node.size) * node.angle;
- child.startAngle = startAngle;
- child.angle = angle;
- child.endAngle = startAngle + angle;
- child.depth = node.depth + 1;
- child.left = w / 2;
- child.top = h / 2;
- child.innerRadius = Math.max(0, child.depth - .5) * r;
- child.outerRadius = (child.depth + .5) * r;
- startAngle += angle;
- if (child.children) {
- wedgify(child);
- }
- }
- }
-
- /** @private */
- function flatten(node, array) {
- if (node.children) {
- for (var i = 0; i < node.children.length; i++) {
- flatten(node.children[i], array);
- }
- }
- array.push(node)
- return array;
- }
-
- /** @private */
- function data() {
- var root = accumulate(tree);
- w = this.parent.width();
- h = this.parent.height();
- r = Math.min(w, h) / 2 / (depth(root) - .5);
- root.left = w / 2;
- root.top = h / 2;
- root.startAngle = 0;
- root.angle = 2 * Math.PI;
- root.endAngle = 2 * Math.PI;
- root.innerRadius = 0;
- root.outerRadius = r;
- root.depth = 0;
- layout(root);
- return flatten(root, []).reverse();
- }
-
- /* A dummy mark, like an anchor, which the caller extends. */
- var mark = new pv.Mark()
- .data(data)
- .left(function(n) { return n.left; })
- .top(function(n) { return n.top; })
- .startAngle(function(n) { return n.startAngle; })
- .angle(function(n) { return n.angle; })
- .innerRadius(function(n) { return n.innerRadius; })
- .outerRadius(function(n) { return n.outerRadius; });
-
- /**
- * Specifies the root key; optional. The root key is prepended to the
- * <tt>keys</tt> attribute for all generated nodes. This method is provided
- * for convenience and does not affect layout.
- *
- * @param {string} v the root key.
- * @function
- * @name pv.Layout.sunburst.prototype.root
- * @returns {pv.Layout.sunburst} this.
- */
- mark.root = function(v) {
- keys = [v];
- return this;
- };
-
- /**
- * Specifies the sizing function. By default, the sizing function is
- * <tt>Number</tt>. The sizing function is invoked for each node in the tree
- * (passed to the constructor): the sizing function must return
- * <tt>undefined</tt> or <tt>NaN</tt> for internal nodes, and a number for
- * leaf nodes. The aggregate sizes of internal nodes will be automatically
- * computed by the layout.
- *
- * <p>For example, if the tree data structure represents a file system, with
- * files as leaf nodes, and each file has a <tt>bytes</tt> attribute, you can
- * specify a size function as:
- *
- * <pre>.size(function(d) d.bytes)</pre>
- *
- * This function will return <tt>undefined</tt> for internal nodes (since
- * these do not have a <tt>bytes</tt> attribute), and a number for leaf nodes.
- *
- * <p>Note that the built-in <tt>Math.sqrt</tt> and <tt>Math.log</tt> methods
- * can be used as sizing functions. These function similarly to
- * <tt>Number</tt>, except perform a root and log scale, respectively.
- *
- * @param {function} f the new sizing function.
- * @function
- * @name pv.Layout.sunburst.prototype.size
- * @returns {pv.Layout.sunburst} this.
- */
- mark.size = function(f) {
- sizeof = f;
- return this;
- };
-
- return mark;
-};
-// TODO add `by` function for determining size (and children?)
-
-/**
- * Returns a new treemap tree layout.
- *
- * @class A tree layout in the form of an treemap. <img
- * src="../treemap.png" width="160" height="160" align="right"> Treemaps
- * are a form of space-filling layout that represents nodes as boxes, with child
- * nodes placed within parent boxes. The size of each box is proportional to the
- * size of the node in the tree.
- *
- * <p>This particular algorithm is taken from Bruls, D.M., C. Huizing, and
- * J.J. van Wijk, <a href="http://www.win.tue.nl/~vanwijk/stm.pdf">"Squarified
- * Treemaps"</a> in <i>Data Visualization 2000, Proceedings of the Joint
- * Eurographics and IEEE TCVG Sumposium on Visualization</i>, 2000,
- * pp. 33-42.
- *
- * <p>This tree layout is intended to be extended (see {@link pv.Mark#extend})
- * by a {@link pv.Bar}. The data property returns an array of nodes for use by
- * other property functions. The following node attributes are supported:
- *
- * <ul>
- * <li><tt>left</tt> - the cell left position.
- * <li><tt>top</tt> - the cell top position.
- * <li><tt>width</tt> - the cell width.
- * <li><tt>height</tt> - the cell height.
- * <li><tt>depth</tt> - the node depth (tier; the root is 0).
- * <li><tt>keys</tt> - an array of string keys for the node.
- * <li><tt>size</tt> - the aggregate node size.
- * <li><tt>children</tt> - child nodes, if any.
- * <li><tt>data</tt> - the associated tree element, for leaf nodes.
- * </ul>
- *
- * To produce a default treemap layout, say:
- *
- * <pre>.add(pv.Bar)
- * .extend(pv.Layout.treemap(tree))</pre>
- *
- * To display internal nodes, and color by depth, say:
- *
- * <pre>.add(pv.Bar)
- * .extend(pv.Layout.treemap(tree).inset(10))
- * .fillStyle(pv.Colors.category19().by(function(n) n.depth))</pre>
- *
- * The format of the <tt>tree</tt> argument is a hierarchical object whose leaf
- * nodes are numbers corresponding to their size. For an example, and
- * information on how to convert tabular data into such a tree, see
- * {@link pv.Tree}. If the leaf nodes are not numbers, a {@link #size} function
- * can be specified to override how the tree is interpreted. This size function
- * can also be used to transform the data.
- *
- * <p>By default, the treemap fills the full width and height of the parent
- * panel, and only leaf nodes are rendered. If an {@link #inset} is specified,
- * internal nodes will be rendered, each inset from their parent by the
- * specified margins. Rounding can be enabled using {@link #round}. Finally, an
- * optional root key can be specified using {@link #root} for convenience.
- *
- * @param tree a tree (an object) who leaf attributes have sizes.
- * @returns {pv.Layout.treemap} a tree layout.
- */
-pv.Layout.treemap = function(tree) {
- var keys = [], round, inset, sizeof = Number;
-
- /** @private */
- function rnd(i) {
- return round ? Math.round(i) : i;
- }
-
- /** @private */
- function accumulate(map) {
- var node = {size: 0, children: [], keys: keys.slice()};
- for (var key in map) {
- var child = map[key], size = sizeof(child);
- keys.push(key);
- if (isNaN(size)) {
- child = accumulate(child);
- } else {
- child = {size: size, data: child, keys: keys.slice()};
- }
- node.children.push(child);
- node.size += child.size;
- keys.pop();
- }
- node.children.sort(function(a, b) { return a.size - b.size; });
- return node;
- }
-
- /** @private */
- function scale(node, k) {
- node.size *= k;
- if (node.children) {
- for (var i = 0; i < node.children.length; i++) {
- scale(node.children[i], k);
- }
- }
- }
-
- /** @private */
- function ratio(row, l) {
- var rmax = -Infinity, rmin = Infinity, s = 0;
- for (var i = 0; i < row.length; i++) {
- var r = row[i].size;
- if (r < rmin) rmin = r;
- if (r > rmax) rmax = r;
- s += r;
- }
- s = s * s;
- l = l * l;
- return Math.max(l * rmax / s, s / (l * rmin));
- }
-
- /** @private */
- function squarify(node) {
- var row = [], mink = Infinity;
- var x = node.left + (inset ? inset.left : 0),
- y = node.top + (inset ? inset.top : 0),
- w = node.width - (inset ? inset.left + inset.right : 0),
- h = node.height - (inset ? inset.top + inset.bottom : 0),
- l = Math.min(w, h);
-
- scale(node, w * h / node.size);
-
- function position(row) {
- var s = pv.sum(row, function(node) { return node.size; }),
- hh = (l == 0) ? 0 : rnd(s / l);
-
- for (var i = 0, d = 0; i < row.length; i++) {
- var n = row[i], nw = rnd(n.size / hh);
- if (w == l) {
- n.left = x + d;
- n.top = y;
- n.width = nw;
- n.height = hh;
- } else {
- n.left = x;
- n.top = y + d;
- n.width = hh;
- n.height = nw;
- }
- d += nw;
- }
-
- if (w == l) {
- if (n) n.width += w - d; // correct rounding error
- y += hh;
- h -= hh;
- } else {
- if (n) n.height += h - d; // correct rounding error
- x += hh;
- w -= hh;
- }
- l = Math.min(w, h);
- }
-
- var children = node.children.slice(); // copy
- while (children.length > 0) {
- var child = children[children.length - 1];
- if (child.size <= 0) {
- children.pop();
- continue;
- }
- row.push(child);
-
- var k = ratio(row, l);
- if (k <= mink) {
- children.pop();
- mink = k;
- } else {
- row.pop();
- position(row);
- row.length = 0;
- mink = Infinity;
- }
- }
-
- if (row.length > 0) {
- position(row);
- }
-
- /* correct rounding error */
- if (w == l) {
- for (var i = 0; i < row.length; i++) {
- row[i].width += w;
- }
- } else {
- for (var i = 0; i < row.length; i++) {
- row[i].height += h;
- }
- }
- }
-
- /** @private */
- function layout(node) {
- if (node.children) {
- squarify(node);
- for (var i = 0; i < node.children.length; i++) {
- var child = node.children[i];
- child.depth = node.depth + 1;
- layout(child);
- }
- }
- }
-
- /** @private */
- function flatten(node, array) {
- if (node.children) {
- for (var i = 0; i < node.children.length; i++) {
- flatten(node.children[i], array);
- }
- }
- if (inset || !node.children) {
- array.push(node)
- }
- return array;
- }
-
- /** @private */
- function data() {
- var root = accumulate(tree);
- root.left = 0;
- root.top = 0;
- root.width = this.parent.width();
- root.height = this.parent.height();
- root.depth = 0;
- layout(root);
- return flatten(root, []).reverse();
- }
-
- /* A dummy mark, like an anchor, which the caller extends. */
- var mark = new pv.Mark()
- .data(data)
- .left(function(n) { return n.left; })
- .top(function(n) { return n.top; })
- .width(function(n) { return n.width; })
- .height(function(n) { return n.height; });
-
- /**
- * Enables or disables rounding. When rounding is enabled, the left, top,
- * width and height properties will be rounded to integer pixel values. The
- * rounding algorithm uses error accumulation to ensure an exact fit.
- *
- * @param {boolean} v whether rounding should be enabled.
- * @function
- * @name pv.Layout.treemap.prototype.round
- * @returns {pv.Layout.treemap} this.
- */
- mark.round = function(v) {
- round = v;
- return this;
- };
-
- /**
- * Specifies the margins to inset child nodes from their parents; as a side
- * effect, this also enables the display of internal nodes, which are hidden
- * by default. If only a single argument is specified, this value is used to
- * inset all four sides.
- *
- * @param {number} top the top margin.
- * @param {number} [right] the right margin.
- * @param {number} [bottom] the bottom margin.
- * @param {number} [left] the left margin.
- * @function
- * @name pv.Layout.treemap.prototype.inset
- * @returns {pv.Layout.treemap} this.
- */
- mark.inset = function(top, right, bottom, left) {
- if (arguments.length == 1) right = bottom = left = top;
- inset = {top:top, right:right, bottom:bottom, left:left};
- return this;
- };
-
- /**
- * Specifies the root key; optional. The root key is prepended to the
- * <tt>keys</tt> attribute for all generated nodes. This method is provided
- * for convenience and does not affect layout.
- *
- * @param {string} v the root key.
- * @function
- * @name pv.Layout.treemap.prototype.root
- * @returns {pv.Layout.treemap} this.
- */
- mark.root = function(v) {
- keys = [v];
- return this;
- };
-
- /**
- * Specifies the sizing function. By default, the sizing function is
- * <tt>Number</tt>. The sizing function is invoked for each node in the tree
- * (passed to the constructor): the sizing function must return
- * <tt>undefined</tt> or <tt>NaN</tt> for internal nodes, and a number for
- * leaf nodes. The aggregate sizes of internal nodes will be automatically
- * computed by the layout.
- *
- * <p>For example, if the tree data structure represents a file system, with
- * files as leaf nodes, and each file has a <tt>bytes</tt> attribute, you can
- * specify a size function as:
- *
- * <pre>.size(function(d) d.bytes)</pre>
- *
- * This function will return <tt>undefined</tt> for internal nodes (since
- * these do not have a <tt>bytes</tt> attribute), and a number for leaf nodes.
- *
- * <p>Note that the built-in <tt>Math.sqrt</tt> and <tt>Math.log</tt> methods
- * can be used as sizing functions. These function similarly to
- * <tt>Number</tt>, except perform a root and log scale, respectively.
- *
- * @param {function} f the new sizing function.
- * @function
- * @name pv.Layout.treemap.prototype.size
- * @returns {pv.Layout.treemap} this.
- */
- mark.size = function(f) {
- sizeof = f;
- return this;
- };
-
- return mark;
-};
- return pv;}();/*
- * Parses the Protovis specifications on load, allowing the use of JavaScript
- * 1.8 function expressions on browsers that only support JavaScript 1.6.
- *
- * @see pv.parse
- */
-pv.listen(window, "load", function() {
- var scripts = document.getElementsByTagName("script");
- for (var i = 0; i < scripts.length; i++) {
- var s = scripts[i];
- if (s.type == "text/javascript+protovis") {
- try {
- pv.Panel.$dom = s;
- window.eval(pv.parse(s.textContent || s.innerHTML)); // IE
- } catch (e) {
- pv.error(e);
- }
- delete pv.Panel.$dom;
- }
- }
- });