Browse files

Initial commit, not perfect yet.

  • Loading branch information...
1 parent a01576f commit 79e014bb2acac527ebfe142ae4698f492b974c99 @beaugunderson committed Apr 18, 2012
Showing with 796 additions and 1 deletion.
  1. +5 โˆ’1 README.md
  2. +1 โˆ’0 crossfilter.v1.min.js
  3. +4 โˆ’0 d3.v2.min.js
  4. +153 โˆ’0 index.css
  5. +68 โˆ’0 index.html
  6. +404 โˆ’0 index.js
  7. +161 โˆ’0 sleep.json
View
6 README.md
@@ -1,4 +1,8 @@
zeo-crossfilter
===============
-A crossfilter-based Zeo data viewer
+A crossfilter-based Zeo data viewer.
+
+[Crossfilter](https://github.com/square/crossfilter) was created by [Mike Bostock](https://github.com/mbostock) at Square.
+
+`sleep.json` is my Zeo sleep dataset and is released under a [CC0 license](http://creativecommons.org/publicdomain/zero/1.0/).
View
1 crossfilter.v1.min.js
@@ -0,0 +1 @@
+(function(a){function b(a){return a}function c(a,b){for(var c=0,d=b.length,e=new Array(d);c<d;++c)e[c]=a[b[c]];return e}function e(a){function b(b,c,d,e){while(d<e){var f=d+e>>1;a(b[f])<c?d=f+1:e=f}return d}function c(b,c,d,e){while(d<e){var f=d+e>>1;c<a(b[f])?e=f:d=f+1}return d}return c.right=c,c.left=b,c}function h(a){function b(a,b,c){var e=c-b,f=(e>>>1)+1;while(--f>0)d(a,f,e,b);return a}function c(a,b,c){var e=c-b,f;while(--e>0)f=a[b],a[b]=a[b+e],a[b+e]=f,d(a,1,e,b);return a}function d(b,c,d,e){var f=b[--e+c],g=a(f),h;while((h=c<<1)<=d){h<d&&a(b[e+h])>a(b[e+h+1])&&h++;if(g<=a(b[e+h]))break;b[e+c]=b[e+h],c=h}b[e+c]=f}return b.sort=c,b}function j(a){function c(c,d,e,f){var g=new Array(f=Math.min(e-d,f)),h,i,j,k;for(i=0;i<f;++i)g[i]=c[d++];b(g,0,f);if(d<e){h=a(g[0]);do if(j=a(k=c[d])>h)g[0]=k,h=a(b(g,0,f)[0]);while(++d<e)}return g}var b=h(a);return c}function l(a){function b(b,c,d){for(var e=c+1;e<d;++e){for(var f=e,g=b[e],h=a(g);f>c&&a(b[f-1])>h;--f)b[f]=b[f-1];b[f]=g}return b}return b}function n(a){function c(a,c,e){return(e-c<o?b:d)(a,c,e)}function d(b,d,e){var f=(e-d)/6|0,g=d+f,h=e-1-f,i=d+e-1>>1,j=i-f,k=i+f,l=b[g],m=a(l),n=b[j],o=a(n),p=b[i],q=a(p),r=b[k],s=a(r),u=b[h],v=a(u);m>o&&(t=l,l=n,n=t,t=m,m=o,o=t),s>v&&(t=r,r=u,u=t,t=s,s=v,v=t),m>q&&(t=l,l=p,p=t,t=m,m=q,q=t),o>q&&(t=n,n=p,p=t,t=o,o=q,q=t),m>s&&(t=l,l=r,r=t,t=m,m=s,s=t),q>s&&(t=p,p=r,r=t,t=q,q=s,s=t),o>v&&(t=n,n=u,u=t,t=o,o=v,v=t),o>q&&(t=n,n=p,p=t,t=o,o=q,q=t),s>v&&(t=r,r=u,u=t,t=s,s=v,v=t);var w=n,x=o,y=r,z=s;b[g]=l,b[j]=b[d],b[i]=p,b[k]=b[e-1],b[h]=u;var A=d+1,B=e-2,C=x<=z&&x>=z;if(C)for(var D=A;D<=B;++D){var E=b[D],F=a(E);if(F<x)D!==A&&(b[D]=b[A],b[A]=E),++A;else if(F>x)for(;;){var G=a(b[B]);if(G>x){B--;continue}if(G<x){b[D]=b[A],b[A++]=b[B],b[B--]=E;break}b[D]=b[B],b[B--]=E;break}}else for(var D=A;D<=B;D++){var E=b[D],F=a(E);if(F<x)D!==A&&(b[D]=b[A],b[A]=E),++A;else if(F>z)for(;;){var G=a(b[B]);if(G>z){B--;if(B<D)break;continue}G<x?(b[D]=b[A],b[A++]=b[B],b[B--]=E):(b[D]=b[B],b[B--]=E);break}}b[d]=b[A-1],b[A-1]=w,b[e-1]=b[B+1],b[B+1]=y,c(b,d,A-1),c(b,B+2,e);if(C)return b;if(A<g&&B>h){var H,G;while((H=a(b[A]))<=x&&H>=x)++A;while((G=a(b[B]))<=z&&G>=z)--B;for(var D=A;D<=B;D++){var E=b[D],F=a(E);if(F<=x&&F>=x)D!==A&&(b[D]=b[A],b[A]=E),A++;else if(F<=z&&F>=z)for(;;){var G=a(b[B]);if(G<=z&&G>=z){B--;if(B<D)break;continue}G<x?(b[D]=b[A],b[A++]=b[B],b[B--]=E):(b[D]=b[B],b[B--]=E);break}}}return c(b,A,B+1)}var b=l(a);return c}function v(a){return new Array(a)}function w(a,b){return function(c){var d=c.length;return[a.left(c,b,0,d),a.right(c,b,0,d)]}}function x(a,b){var c=b[0],d=b[1];return function(b){var e=b.length;return[a.left(b,c,0,e),a.left(b,d,0,e)]}}function y(a){return[0,a.length]}function z(){return null}function A(){return 0}function B(a){return a+1}function C(a){return a-1}function D(a){return function(b,c){return b+ +a(c)}}function E(a){return function(b,c){return b-a(c)}}function F(){function q(b){var c=f,d=b.length;return d&&(e=e.concat(b),l=s(l,f+=d),o.forEach(function(a){a(b,c,d)})),a}function r(a){function Q(b,d,e){F=b.map(a),J=K(H(e),0,e),F=c(F,J);var g=L(F),h=g[0],i=g[1],j;for(j=0;j<h;++j)l[J[j]+d]|=q;for(j=i;j<e;++j)l[J[j]+d]|=q;if(!d){t=F,v=J,O=h,P=i;return}var k=t,m=v,n=0,o=0;t=new Array(f),v=G(f,f);for(j=0;n<d&&o<e;++j)k[n]<F[o]?(t[j]=k[n],v[j]=m[n++]):(t[j]=F[o],v[j]=J[o++]+d);for(;n<d;++n,++j)t[j]=k[n],v[j]=m[n];for(;o<e;++o,++j)t[j]=F[o],v[j]=J[o]+d;g=L(t),O=g[0],P=g[1]}function R(a,b,c){N.forEach(function(a){a(F,J,b,c)}),F=J=null}function S(a){var b,c,d,e=a[0],f=a[1],g=[],h=[];if(e<O)for(b=e,c=Math.min(O,f);b<c;++b)l[d=v[b]]^=q,g.push(d);else if(e>O)for(b=O,c=Math.min(e,P);b<c;++b)l[d=v[b]]^=q,h.push(d);if(f>P)for(b=Math.max(e,P),c=f;b<c;++b)l[d=v[b]]^=q,g.push(d);else if(f<P)for(b=Math.max(O,f),c=P;b<c;++b)l[d=v[b]]^=q,h.push(d);return O=e,P=f,m.forEach(function(a){a(q,g,h)}),p}function T(a){return a==null?W():Array.isArray(a)?V(a):U(a)}function U(a){return S((L=w(d,a))(t))}function V(a){return S((L=x(d,a))(t))}function W(){return S((L=y)(t))}function X(a){var b=[],c=P,d;while(--c>=O&&a>0)l[d=v[c]]||(b.push(e[d]),--a);return b}function Y(a){function L(b,c,g,h){function N(){++o===n&&(p=u(p,k<<=1),i=u(i,k),n=I(k))}var j=d,p=G(o,n),q=x,t=F,v=o,w=0,y=0,A,B,C,D,E,L;K&&(q=t=z),d=new Array(o),o=0,i=v>1?s(i,f):G(f,n),v&&(C=(B=j[0]).key);while(y<h&&!((D=a(b[y]))>=D))++y;while(y<h){if(B&&C<=D){E=B,L=C,p[w]=o;if(B=j[++w])C=B.key}else E={key:D,value:t()},L=D;d[o]=E;while(!(D>L)){i[A=c[y]+g]=o,l[A]&r||(E.value=q(E.value,e[A]));if(++y>=h)break;D=a(b[y])}N()}while(w<v)d[p[w]=o]=j[w++],N();if(o>w)for(w=0;w<g;++w)i[w]=p[i[w]];A=m.indexOf(H),o>1?(H=M,J=P):(o===1?(H=O,J=Q):(H=z,J=z),i=null),m[A]=H}function M(a,b,c){if(a===q||K)return;var f,h,j;for(f=0,j=b.length;f<j;++f)l[h=b[f]]&r||(g=d[i[h]],g.value=x(g.value,e[h]));for(f=0,j=c.length;f<j;++f)(l[h=c[f]]&r)===a&&(g=d[i[h]],g.value=y(g.value,e[h]))}function O(a,b,c){if(a===q||K)return;var f,g,h,i=d[0];for(f=0,h=b.length;f<h;++f)l[g=b[f]]&r||(i.value=x(i.value,e[g]));for(f=0,h=c.length;f<h;++f)(l[g=c[f]]&r)===a&&(i.value=y(i.value,e[g]))}function P(){var a,b;for(a=0;a<o;++a)d[a].value=F();for(a=0;a<f;++a)l[a]&r||(b=d[i[a]],b.value=x(b.value,e[a]))}function Q(){var a,b=d[0];b.value=F();for(a=0;a<f;++a)l[a]&r||(b.value=x(b.value,e[a]))}function R(){return K&&(J(),K=!1),d}function S(a){var b=p(R(),0,d.length,a);return w.sort(b,0,b.length)}function T(a,b,d){return x=a,y=b,F=d,K=!0,c}function U(){return T(B,C,A)}function V(a){return T(D(a),E(a),A)}function W(a){function b(b){return a(b.value)}return p=j(b),w=h(b),c}function X(){return W(b)}function Y(){return o}var c={top:S,all:R,reduce:T,reduceCount:U,reduceSum:V,order:W,orderNatural:X,size:Y},d,i,k=8,n=I(k),o=0,p,w,x,y,F,H=z,J=z,K=!0;return arguments.length<1&&(a=b),m.push(H),N.push(L),L(t,v,0,f),U().orderNatural()}function Z(){var a=Y(z),b=a.all;return delete a.all,delete a.top,delete a.order,delete a.orderNatural,delete a.size,a.value=function(){return b()[0].value},a}var p={filter:T,filterExact:U,filterRange:V,filterAll:W,top:X,group:Y,groupAll:Z},q=1<<i++,r=~q,t,v,F,J,K=n(function(a){return F[a]}),L=y,N=[],O=0,P=0;return o.unshift(Q),o.push(R),i>k&&(l=u(l,k<<=1)),Q(e,0,f),R(e,0,f),p}function t(){function i(a,d,g){var i;if(h)return;for(i=d;i<f;++i)l[i]||(b=c(b,e[i]))}function j(a,f,g){var i,j,k;if(h)return;for(i=0,k=f.length;i<k;++i)l[j=f[i]]||(b=c(b,e[j]));for(i=0,k=g.length;i<k;++i)l[j=g[i]]===a&&(b=d(b,e[j]))}function k(){var a;b=g();for(a=0;a<f;++a)l[a]||(b=c(b,e[a]))}function n(b,e,f){return c=b,d=e,g=f,h=!0,a}function p(){return n(B,C,A)}function q(a){return n(D(a),E(a),A)}function r(){return h&&(k(),h=!1),b}var a={reduce:n,reduceCount:p,reduceSum:q,value:r},b,c,d,g,h=!0;return m.push(j),o.push(i),i(e,0,f),p()}function v(){return f}var a={add:q,dimension:r,groupAll:t,size:v},e=[],f=0,i=0,k=8,l=p(0),m=[],o=[];return arguments.length?q(arguments[0]):a}function G(a,b){return(b<257?p:b<65537?q:r)(a)}function H(a){var b=G(a,a);for(var c=-1;++c<a;)b[c]=c;return b}function I(a){return a===8?256:a===16?65536:4294967296}F.version="1.0.2",F.permute=c;var d=F.bisect=e(b);d.by=e;var f=F.heap=h(b);f.by=h;var i=F.heapselect=j(b);i.by=j;var k=F.insertionsort=l(b);k.by=l;var m=F.quicksort=n(b);m.by=n;var o=32,p=v,q=v,r=v,s=b,u=b;typeof Uint8Array!="undefined"&&(p=function(a){return new Uint8Array(a)},q=function(a){return new Uint16Array(a)},r=function(a){return new Uint32Array(a)},s=function(a,b){var c=new a.constructor(b);return c.set(a),c},u=function(a,b){var c;switch(b){case 16:c=q(a.length);break;case 32:c=r(a.length);break;default:throw new Error("invalid array width!")}return c.set(a),c}),a.crossfilter=F})(this);
View
4 d3.v2.min.js
4 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
153 index.css
@@ -0,0 +1,153 @@
+body {
+ font-family: "Helvetica Neue";
+ margin: 10px;
+ min-height: 2000px;
+}
+
+#body {
+ position: relative;
+}
+
+footer {
+ padding: 2em 0 1em 0;
+ font-size: 12px;
+}
+
+h1 {
+ font-size: 96px;
+ margin-top: .3em;
+ margin-bottom: 0;
+}
+
+h1 + h2 {
+ margin-top: 0;
+}
+
+h2 {
+ font-weight: 400;
+ font-size: 28px;
+}
+
+h1, h2 {
+ font-family: "Yanone Kaffeesatz";
+ text-rendering: optimizeLegibility;
+}
+
+#body > p {
+ line-height: 1.5em;
+ width: 640px;
+ text-rendering: optimizeLegibility;
+}
+
+#charts {
+ padding: 10px 0;
+}
+
+.chart {
+ display: inline-block;
+ height: 151px;
+ margin-bottom: 20px;
+}
+
+.reset {
+ padding-left: 1em;
+ font-size: smaller;
+ color: #ccc;
+}
+
+.background.bar {
+ fill: #ccc;
+}
+
+.foreground.bar {
+ fill: steelblue;
+}
+
+.axis path, .axis line {
+ fill: none;
+ stroke: #000;
+ shape-rendering: crispEdges;
+}
+
+.axis text {
+ font: 10px sans-serif;
+}
+
+.brush rect.extent {
+ fill: steelblue;
+ fill-opacity: .125;
+}
+
+.brush .resize path {
+ fill: #eee;
+ stroke: #666;
+}
+
+#pillow-chart {
+ width: 260px;
+}
+
+#wake-chart {
+ width: 260px;
+}
+
+#hours-chart {
+ width: 140px;
+}
+
+#awakenings-chart {
+ width: 160px;
+}
+
+#weekday-chart {
+ width: 140px;
+}
+
+#zq-chart {
+ width: 200px;
+}
+
+#date-chart {
+ width: 920px;
+}
+
+#night-list {
+ min-height: 1024px;
+}
+
+#night-list .night {
+ margin-bottom: .4em;
+ line-height: 1.5em;
+ background: #eee;
+ width: 640px;
+ margin-bottom: 1px;
+}
+
+#night-list .night div {
+ display: inline-block;
+ width: 128px;
+}
+
+#night-list .wake,
+#night-list .pillow {
+ color: #999;
+}
+
+#night-list div.wake,
+#night-list div.hours,
+#night-list div.pillow {
+ width: 160px;
+ padding-right: 10px;
+ text-align: right;
+}
+
+#night-list .good {
+ color: green;
+}
+
+aside {
+ position: absolute;
+ left: 740px;
+ font-size: smaller;
+ width: 220px;
+}
View
68 index.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+
+ <title>Zeo + Crossfilter</title>
+
+ <script src="crossfilter.v1.min.js"></script>
+ <script src="d3.v2.min.js"></script>
+
+ <script src="index.js"></script>
+
+ <style>
+ @import url(http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:400,700);
+ @import url(index.css);
+ </style>
+ </head>
+ <body>
+ <div id="body">
+ <!--
+ <a href="javascript:filter([null, [100, 150], null, null])">arrival delay</a>?
+ <a href="javascript:filter([null, null, [1700, 2000], null])">longer</a>
+ <a href="javascript:filter([null, null, [0, 300], null])">shorter</a>
+ <a href="javascript:filter([null, [80, 150], null, [new Date(2001, 0, 12), new Date(2001, 0, 13)]])">January 12</a>
+ <a href="javascript:filter([null, null, null, [new Date(2001, 0, 27), new Date(2001, 0, 29)]])">weekends</a>
+ <a href="javascript:filter([null, null, null, [new Date(2001, 0, 29), new Date(2001, 1, 3)]])">weekdays</a>
+ <a href="javascript:filter([[4, 7], null, null, null])">mornings</a>
+ <a href="javascript:filter([[21, 24], null, null, null])">nights</a>
+ -->
+
+ <div id="charts">
+ <div id="pillow-chart" class="chart">
+ <div class="title">Head-to-pillow time</div>
+ </div>
+
+ <div id="wake-chart" class="chart">
+ <div class="title">Wake-up time</div>
+ </div>
+
+ <div id="hours-chart" class="chart">
+ <div class="title">Hours of sleep</div>
+ </div>
+
+ <div id="awakenings-chart" class="chart">
+ <div class="title">Awakenings</div>
+ </div>
+
+ <div id="weekday-chart" class="chart">
+ <div class="title">Day of week</div>
+ </div>
+
+ <div id="zq-chart" class="chart">
+ <div class="title">ZQ score</div>
+ </div>
+
+ <div id="date-chart" class="chart">
+ <div class="title">Date</div>
+ </div>
+ </div>
+
+ <aside id="totals"><span id="active">-</span> of <span id="total">-</span> nights selected.</aside>
+
+ <div id="lists">
+ <div id="night-list" class="list"></div>
+ </div>
+ </div>
+ </body>
+</html>
View
404 index.js
@@ -0,0 +1,404 @@
+d3.json("sleep.json", function(sleep) {
+
+ // Various formatters.
+ var formatNumber = d3.format(",d"),
+ formatFloat = d3.format(",.2f"),
+ formatChange = d3.format("+,d"),
+ formatDate = d3.time.format("%B %d, %Y"),
+ formatTime = d3.time.format("%I:%M %p");
+
+ var nights = [];
+
+ function dateDifference(a, b) {
+ // The number of milliseconds in one day
+ var ONE_DAY = 1000 * 60 * 60 * 24;
+
+ // Convert both dates to milliseconds
+ var aTime = a.getTime();
+ var bTime = b.getTime();
+
+ // Calculate the difference in milliseconds
+ var differenceTime = Math.abs(aTime - bTime);
+
+ // Convert back to days and return
+ return Math.round(differenceTime / ONE_DAY);
+ }
+
+ function parseDate(d) {
+ if (d.hour !== undefined &&
+ d.minute !== undefined &&
+ d.second !== undefined) {
+ return new Date(d.year, d.month, d.day, d.hour, d.minute, d.second);
+ }
+
+ return new Date(d.year, d.month, d.day);
+ }
+
+ // XXX temporary hack
+ var added = {};
+
+ sleep.forEach(function(d, i) {
+ var entryDate = parseDate(d.data.startDate);
+
+ if (added[entryDate] !== true) {
+ added[entryDate] = true;
+
+ nights.push({
+ index: i,
+ date: entryDate,
+ pillow: parseDate(d.data.bedTime),
+ awakenings: d.data.awakenings,
+ wake: parseDate(d.data.riseTime),
+ hours: d.data.totalZ / 60,
+ zq: d.data.zq,
+ dayOfWeek: entryDate.getDay(),
+ morningFeel: d.data.morningFeel,
+ timeInWake: d.data.timeInWake
+ });
+ }
+ });
+
+ // Create the crossfilter for the relevant dimensions and groups.
+ var night = crossfilter(nights),
+ all = night.groupAll(),
+
+ date = night.dimension(function(d) { return d3.time.day(d.date); }),
+ dates = date.group(),
+
+ awakening = night.dimension(function(d) { return d.awakenings; }),
+ awakenings = awakening.group(),
+
+ weekday = night.dimension(function(d) { return d.dayOfWeek; }),
+ weekdays = weekday.group(),
+
+ zq = night.dimension(function(d) { return d.zq; }),
+ zqs = zq.group(),
+
+ pillow = night.dimension(function(d) { return d.pillow.getHours() + d.pillow.getMinutes() / 60; }),
+ pillows = pillow.group(Math.floor),
+
+ wake = night.dimension(function(d) { return d.wake.getHours() + d.wake.getMinutes() / 60; }),
+ wakes = wake.group(Math.floor),
+
+ hour = night.dimension(function(d) { return d.hours; }),
+ hours = hour.group(Math.floor);
+
+ var dateRange = d3.extent(nights, function(night) {
+ return night.date;
+ });
+
+ var charts = [
+ barChart()
+ .dimension(pillow)
+ .group(pillows)
+ .x(d3.scale.linear()
+ .domain([0, 24])
+ .rangeRound([0, 10 * 24])),
+
+ barChart()
+ .dimension(wake)
+ .group(wakes)
+ .x(d3.scale.linear()
+ .domain([0, 24])
+ .rangeRound([0, 10 * 24])),
+
+ barChart()
+ .dimension(hour)
+ .group(hours)
+ .x(d3.scale.linear()
+ .domain([0, 11])
+ .rangeRound([0, 10 * 11])),
+
+ barChart()
+ .dimension(awakening)
+ .group(awakenings)
+ .x(d3.scale.linear()
+ .domain([0, 12])
+ .rangeRound([0, 10 * 12])),
+
+ barChart()
+ .dimension(weekday)
+ .group(weekdays)
+ .x(d3.scale.linear()
+ .domain([0, 7])
+ .rangeRound([0, 10 * 7])),
+
+ barChart()
+ .dimension(zq)
+ .group(zqs)
+ .x(d3.scale.linear()
+ .domain([30, 110])
+ .rangeRound([30, 2 * 110])),
+
+ barChart()
+ .dimension(date)
+ .group(dates)
+ .round(d3.time.day.round)
+ .x(d3.time.scale()
+ .domain(dateRange)
+ .range([0, 4 * dateDifference(dateRange[0], dateRange[1])]))
+ ];
+
+ // Given our array of charts, which we assume are in the same order as the
+ // .chart elements in the DOM, bind the charts to the DOM and render them.
+ // We also listen to the chart's brush events to update the display.
+ var chart = d3.selectAll(".chart")
+ .data(charts)
+ .each(function(chart) { chart.on("brush", renderAll).on("brushend", renderAll); });
+
+ // Render the initial lists.
+ var list = d3.selectAll(".list")
+ .data([nightList]);
+
+ // Render the total.
+ d3.selectAll("#total")
+ .text(formatNumber(night.size()));
+
+ renderAll();
+
+ // Renders the specified chart or list.
+ function render(method) {
+ d3.select(this).call(method);
+ }
+
+ // Whenever the brush moves, re-rendering everything.
+ function renderAll() {
+ chart.each(render);
+ list.each(render);
+ d3.select("#active").text(formatNumber(all.value()));
+ }
+
+ window.filter = function(filters) {
+ filters.forEach(function(d, i) { charts[i].filter(d); });
+ renderAll();
+ };
+
+ window.reset = function(i) {
+ charts[i].filter(null);
+ renderAll();
+ };
+
+ // The table at the bottom of the page
+ function nightList(div) {
+ var dateList = date.top(50);
+
+ div.each(function() {
+ var date = d3.select(this).selectAll(".night")
+ .data(dateList).order();
+
+ var dateEnter = date.enter().append("div")
+ .attr("class", "night");
+
+ dateEnter.append("div")
+ .attr("class", "nightDate")
+ .text(function(d) { return formatDate(d.date); });
+
+ dateEnter.append("div")
+ .attr("class", "pillow")
+ .text(function(d) { return formatTime(d.pillow); });
+
+ dateEnter.append("div")
+ .attr("class", "wake")
+ .text(function(d) { return formatTime(d.wake); });
+
+ dateEnter.append("div")
+ .attr("class", "hours")
+ .classed("good", function(d) { return d.hours >= 7.5; })
+ .text(function(d) { return formatFloat(d.hours) + " hours"; });
+
+ date.exit().remove();
+ });
+ }
+
+ function barChart() {
+ if (!barChart.id) barChart.id = 0;
+
+ var margin = {top: 10, right: 10, bottom: 20, left: 10},
+ x,
+ y = d3.scale.linear().range([100, 0]),
+ id = barChart.id++,
+ axis = d3.svg.axis().orient("bottom"),
+ brush = d3.svg.brush(),
+ brushDirty,
+ dimension,
+ group,
+ round;
+
+ function chart(div) {
+ var width = x.range()[1],
+ height = y.range()[0];
+
+ y.domain([0, group.top(1)[0].value]);
+
+ div.each(function() {
+ var div = d3.select(this),
+ g = div.select("g");
+
+ // Create the skeletal chart.
+ if (g.empty()) {
+ div.select(".title").append("a")
+ .attr("href", "javascript:reset(" + id + ")")
+ .attr("class", "reset")
+ .text("reset")
+ .style("display", "none");
+
+ g = div.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 + ")");
+
+ g.append("clipPath")
+ .attr("id", "clip-" + id)
+ .append("rect")
+ .attr("width", width)
+ .attr("height", height);
+
+ g.selectAll(".bar")
+ .data(["background", "foreground"])
+ .enter().append("path")
+ .attr("class", function(d) { return d + " bar"; })
+ .datum(group.all());
+
+ g.selectAll(".foreground.bar")
+ .attr("clip-path", "url(#clip-" + id + ")");
+
+ g.append("g")
+ .attr("class", "axis")
+ .attr("transform", "translate(0," + height + ")")
+ .call(axis);
+
+ // Initialize the brush component with pretty resize handles.
+ var gBrush = g.append("g").attr("class", "brush").call(brush);
+ gBrush.selectAll("rect").attr("height", height);
+ gBrush.selectAll(".resize").append("path").attr("d", resizePath);
+ }
+
+ // Only redraw the brush if set externally.
+ if (brushDirty) {
+ brushDirty = false;
+ g.selectAll(".brush").call(brush);
+ div.select(".title a").style("display", brush.empty() ? "none" : null);
+ if (brush.empty()) {
+ g.selectAll("#clip-" + id + " rect")
+ .attr("x", 0)
+ .attr("width", width);
+ } else {
+ var extent = brush.extent();
+ g.selectAll("#clip-" + id + " rect")
+ .attr("x", x(extent[0]))
+ .attr("width", x(extent[1]) - x(extent[0]));
+ }
+ }
+
+ g.selectAll(".bar").attr("d", barPath);
+ });
+
+ function barPath(groups) {
+ var path = [],
+ i = -1,
+ n = groups.length,
+ d;
+ while (++i < n) {
+ d = groups[i];
+ path.push("M", x(d.key), ",", height, "V", y(d.value), "h9V", height);
+ }
+ return path.join("");
+ }
+
+ function resizePath(d) {
+ var e = +(d == "e"),
+ x = e ? 1 : -1,
+ y = height / 3;
+ return "M" + (.5 * x) + "," + y
+ + "A6,6 0 0 " + e + " " + (6.5 * x) + "," + (y + 6)
+ + "V" + (2 * y - 6)
+ + "A6,6 0 0 " + e + " " + (.5 * x) + "," + (2 * y)
+ + "Z"
+ + "M" + (2.5 * x) + "," + (y + 8)
+ + "V" + (2 * y - 8)
+ + "M" + (4.5 * x) + "," + (y + 8)
+ + "V" + (2 * y - 8);
+ }
+ }
+
+ brush.on("brushstart.chart", function() {
+ var div = d3.select(this.parentNode.parentNode.parentNode);
+ div.select(".title a").style("display", null);
+ });
+
+ brush.on("brush.chart", function() {
+ var g = d3.select(this.parentNode),
+ extent = brush.extent();
+ if (round) g.select(".brush")
+ .call(brush.extent(extent = extent.map(round)))
+ .selectAll(".resize")
+ .style("display", null);
+ g.select("#clip-" + id + " rect")
+ .attr("x", x(extent[0]))
+ .attr("width", x(extent[1]) - x(extent[0]));
+ dimension.filterRange(extent);
+ });
+
+ brush.on("brushend.chart", function() {
+ if (brush.empty()) {
+ var div = d3.select(this.parentNode.parentNode.parentNode);
+ div.select(".title a").style("display", "none");
+ div.select("#clip-" + id + " rect").attr("x", null).attr("width", "100%");
+ dimension.filterAll();
+ }
+ });
+
+ chart.margin = function(_) {
+ if (!arguments.length) return margin;
+ margin = _;
+ return chart;
+ };
+
+ chart.x = function(_) {
+ if (!arguments.length) return x;
+ x = _;
+ axis.scale(x);
+ brush.x(x);
+ return chart;
+ };
+
+ chart.y = function(_) {
+ if (!arguments.length) return y;
+ y = _;
+ return chart;
+ };
+
+ chart.dimension = function(_) {
+ if (!arguments.length) return dimension;
+ dimension = _;
+ return chart;
+ };
+
+ chart.filter = function(_) {
+ if (_) {
+ brush.extent(_);
+ dimension.filterRange(_);
+ } else {
+ brush.clear();
+ dimension.filterAll();
+ }
+ brushDirty = true;
+ return chart;
+ };
+
+ chart.group = function(_) {
+ if (!arguments.length) return group;
+ group = _;
+ return chart;
+ };
+
+ chart.round = function(_) {
+ if (!arguments.length) return round;
+ round = _;
+ return chart;
+ };
+
+ return d3.rebind(chart, brush, "on");
+ }
+});
View
161 sleep.json
161 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.

0 comments on commit 79e014b

Please sign in to comment.