d4-rails 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,9 +1,9 @@
1
1
  /* d4-rails includes d4 library to provide DSL for d3 for Rails applications
2
2
  * License: MIT
3
3
  */
4
- /*! d4 - v0.1.0
4
+ /*! d4 - v0.4.0
5
5
  * License: MIT Expat
6
- * Date: 2014-02-23
6
+ * Date: 2014-03-02
7
7
  */
8
8
  /*!
9
9
  Functions "each", "extend", and "isFunction" based on Underscore.js 1.5.2
@@ -42,8 +42,10 @@
42
42
  root.d4 = d4;
43
43
  }
44
44
 
45
+ d4.charts = {};
45
46
  d4.features = {};
46
47
  d4.parsers = {};
48
+ d4.builders = {};
47
49
 
48
50
  var each = d4.each = d4.forEach = function(obj, iterator, context) {
49
51
  var nativeForEach = Array.prototype.forEach,
@@ -69,22 +71,21 @@
69
71
  }
70
72
  };
71
73
 
72
- var isFunction = function(obj) {
73
- return !!(obj && obj.constructor && obj.call && obj.apply);
74
- };
75
-
76
- var isNotFunction = function(obj) {
77
- return !isFunction(obj);
78
- };
79
-
80
- var assert = function(message) {
74
+ var err = function() {
75
+ var parts = Array.prototype.slice.call(arguments);
76
+ var message = parts.shift();
77
+ var regexp;
78
+ each(parts, function(str, i){
79
+ regexp = new RegExp('\\{' + i + '\\}', 'gi');
80
+ message = message.replace(regexp, str);
81
+ });
81
82
  throw new Error('[d4] ' + message);
82
83
  };
83
84
 
84
85
  var validateBuilder = function(builder) {
85
86
  each(['configure'], function(funct) {
86
- if (!builder[funct] || isNotFunction(builder[funct])) {
87
- assert('The supplied builder does not have a ' + funct + ' function');
87
+ if (!builder[funct] || d4.isNotFunction(builder[funct])) {
88
+ err('The supplied builder does not have a {0} function', funct);
88
89
  }
89
90
  });
90
91
  return builder;
@@ -99,7 +100,7 @@
99
100
 
100
101
  var assignDefaults = function(config, defaultBuilder) {
101
102
  if (!defaultBuilder) {
102
- assert('No builder defined');
103
+ err('No builder defined');
103
104
  }
104
105
  var opts = d4.merge({
105
106
  width: 400,
@@ -133,9 +134,9 @@
133
134
  if(selection){
134
135
  each(d3.keys(feature._proxiedFunctions), function(key){
135
136
  each(feature._proxiedFunctions[key], function(proxiedArgs){
136
- selection[key].apply(selection, proxiedArgs)
137
+ selection[key].apply(selection, proxiedArgs);
137
138
  });
138
- })
139
+ });
139
140
  }
140
141
  };
141
142
 
@@ -151,7 +152,7 @@
151
152
  opts.builder.configure(opts, data);
152
153
  linkFeatures(opts, data);
153
154
  } else {
154
- assert('No builder defined');
155
+ err('No builder defined');
155
156
  }
156
157
  };
157
158
 
@@ -164,9 +165,47 @@
164
165
  this.svg.append('defs');
165
166
  };
166
167
 
168
+ /*!
169
+ Normally d4 series elements inside the data array to be in a specific
170
+ format, which is designed to support charts which require multiple data
171
+ series. However, some charts can easily be used to display only a single data
172
+ series in which case the default structure is overly verbose. In these cases
173
+ d4 accepts the simplified objects in the array payload and silently
174
+ parses them using the d4.nestedGroup parser. It will configure the parser's
175
+ dimensions based on the configuration applied to the chart object itself.
176
+ */
177
+ var applyDefaultParser = function(opts, data) {
178
+ if(opts.yKey !== opts.valueKey){
179
+ opts.valueKey = opts.yKey;
180
+ }
181
+ var parsed = d4.parsers.nestedGroup()
182
+ .x(opts.xKey)
183
+ .y(opts.yKey)
184
+ .nestKey(opts.xKey)
185
+ .value(opts.valueKey)(data);
186
+ return parsed.data;
187
+ };
188
+
189
+ var prepareData = function(opts, data) {
190
+ var needsParsing = false, keys, item;
191
+ if(data.length > 0){
192
+ item = data[0];
193
+ if(d4.isArray(item)) {
194
+ needsParsing = true;
195
+ } else {
196
+ keys = d3.keys(item);
197
+ if(keys.indexOf('key') + keys.indexOf('values') <= 0) {
198
+ needsParsing = true;
199
+ }
200
+ }
201
+ }
202
+ return needsParsing ? applyDefaultParser(opts, data) : data;
203
+ };
204
+
167
205
  var applyScaffold = function(opts) {
168
206
  return function(selection) {
169
207
  selection.each(function(data) {
208
+ data = prepareData(opts, data);
170
209
  scaffoldChart.bind(opts, this)(data);
171
210
  build(opts, data);
172
211
  });
@@ -221,7 +260,7 @@
221
260
 
222
261
  var mixin = function(feature, index) {
223
262
  if (!feature) {
224
- assert('You need to supply an object to mixin.');
263
+ err('You need to supply an object to mixin.');
225
264
  }
226
265
  var name = d3.keys(feature)[0];
227
266
  var overrides = extractOverrides.bind(this)(feature, name);
@@ -233,7 +272,7 @@
233
272
 
234
273
  var mixout = function(name) {
235
274
  if (!name) {
236
- assert('A name is required in order to mixout a chart feature.');
275
+ err('A name is required in order to mixout a chart feature.');
237
276
  }
238
277
 
239
278
  delete this.features[name];
@@ -244,16 +283,60 @@
244
283
 
245
284
  var using = function(name, funct) {
246
285
  var feature = this.features[name];
247
- if (isNotFunction(funct)) {
248
- assert('You must supply a continuation function in order to use a chart feature.');
286
+ if (d4.isNotFunction(funct)) {
287
+ err('You must supply a continuation function in order to use a chart feature.');
249
288
  }
250
289
  if (!feature) {
251
- assert('Could not find feature: "' + name + '", maybe you forgot to mix it in?');
290
+ err('Could not find feature: "{0}", maybe you forgot to mix it in?', name);
252
291
  } else {
253
292
  funct.bind(this)(feature);
254
293
  }
255
294
  };
256
295
 
296
+ /**
297
+ * This function allows you to register a reusable chart with d4.
298
+ * @param {String} name - accessor name for chart.
299
+ * @param {Function} funct - function which will instantiate the chart.
300
+ * @returns a reference to the chart function
301
+ */
302
+ d4.chart = function(name, funct) {
303
+ d4.charts[name] = funct;
304
+ return d4.charts[name];
305
+ };
306
+
307
+ /**
308
+ * This function allows you to register a reusable chart feature with d4.
309
+ * @param {String} name - accessor name for chart feature.
310
+ * @param {Function} funct - function which will instantiate the chart feature.
311
+ * @returns a reference to the chart feature
312
+ */
313
+ d4.feature = function(name, funct) {
314
+ d4.features[name] = funct;
315
+ return d4.features[name];
316
+ };
317
+
318
+ /**
319
+ * This function allows you to register a reusable chart builder with d4.
320
+ * @param {String} name - accessor name for chart builder.
321
+ * @param {Function} funct - function which will instantiate the chart builder.
322
+ * @returns a reference to the chart builder
323
+ */
324
+ d4.builder = function(name, funct) {
325
+ d4.builders[name] = funct;
326
+ return d4.builders[name];
327
+ };
328
+
329
+ /**
330
+ * This function allows you to register a reusable data parser with d4.
331
+ * @param {String} name - accessor name for data parser.
332
+ * @param {Function} funct - function which will instantiate the data parser.
333
+ * @returns a reference to the data parser
334
+ */
335
+ d4.parser = function(name, funct) {
336
+ d4.parsers[name] = funct;
337
+ return d4.parsers[name];
338
+ };
339
+
257
340
  d4.baseChart = function(config, defaultBuilder) {
258
341
  var opts = assignDefaults(config, defaultBuilder);
259
342
  var chart = applyScaffold(opts);
@@ -327,7 +410,7 @@
327
410
  *##### Examples
328
411
  *
329
412
  * // Mixout the yAxis which is provided as a default
330
- * var chart = d4.columnChart()
413
+ * var chart = d4.charts.column()
331
414
  * .mixout('yAxis');
332
415
  *
333
416
  * // Now test that the feature has been removed.
@@ -372,7 +455,7 @@
372
455
  *##### Examples
373
456
  *
374
457
  * // Mixout the yAxis which is provided as a default
375
- * var chart = d4.columnChart()
458
+ * var chart = d4.charts.column()
376
459
  * .mixout('yAxis');
377
460
  *
378
461
  * // Now test that the feature has been removed.
@@ -384,25 +467,37 @@
384
467
  return opts.mixins;
385
468
  };
386
469
 
387
- /**
388
- * Based on D3's own functor function.
389
- * > If the specified value is a function, returns the specified value. Otherwise,
390
- * > returns a function that returns the specified value. This method is used
391
- * > internally as a lazy way of upcasting constant values to functions, in
392
- * > cases where a property may be specified either as a function or a constant.
393
- * > For example, many D3 layouts allow properties to be specified this way,
394
- * > and it simplifies the implementation if we automatically convert constant
395
- * > values to functions.
396
- *
397
- * @param {Varies} funct - An function or other variable to be wrapped in a function
398
- */
399
- d4.functor = function(funct) {
400
- return isFunction(funct) ? funct : function() {
401
- return funct;
402
- };
470
+ return chart;
471
+ };
472
+
473
+ /**
474
+ * Based on D3's own functor function.
475
+ * > If the specified value is a function, returns the specified value. Otherwise,
476
+ * > returns a function that returns the specified value. This method is used
477
+ * > internally as a lazy way of upcasting constant values to functions, in
478
+ * > cases where a property may be specified either as a function or a constant.
479
+ * > For example, many D3 layouts allow properties to be specified this way,
480
+ * > and it simplifies the implementation if we automatically convert constant
481
+ * > values to functions.
482
+ *
483
+ * @param {Varies} funct - An function or other variable to be wrapped in a function
484
+ */
485
+ d4.functor = function(funct) {
486
+ return d4.isFunction(funct) ? funct : function() {
487
+ return funct;
403
488
  };
489
+ };
404
490
 
405
- return chart;
491
+ d4.isArray = Array.isArray || function(val) {
492
+ return Object.prototype.toString.call(val) === '[object Array]';
493
+ };
494
+
495
+ d4.isFunction = function(obj) {
496
+ return !!(obj && obj.constructor && obj.call && obj.apply);
497
+ };
498
+
499
+ d4.isNotFunction = function(obj) {
500
+ return !d4.isFunction(obj);
406
501
  };
407
502
 
408
503
  d4.merge = function(options, overrides) {
@@ -434,38 +529,16 @@
434
529
  * global d4: false
435
530
  */
436
531
  'use strict';
437
- var columnChartBuilder = function() {
438
- var configureX = function(chart, data) {
439
- if (!chart.x) {
440
- chart.xRoundBands = chart.xRoundBands || 0.3;
441
- chart.x = d3.scale.ordinal()
442
- .domain(data.map(function(d) {
443
- return d[this.xKey];
444
- }.bind(chart)))
445
- .rangeRoundBands([0, chart.width - chart.margin.left - chart.margin.right], chart.xRoundBands);
446
- }
447
- };
448
-
449
- var configureY = function(chart, data) {
450
- if (!chart.y) {
451
- chart.y = d3.scale.linear()
452
- .domain(d3.extent(data, function(d) {
453
- return d[this.yKey];
454
- }.bind(chart)));
455
- }
456
- chart.y.range([chart.height - chart.margin.top - chart.margin.bottom, 0])
457
- .clamp(true)
458
- .nice();
459
- };
460
-
461
- var configureScales = function(chart, data) {
462
- configureX.bind(this)(chart, data);
463
- configureY.bind(this)(chart, data);
464
- };
465
532
 
533
+ var columnChartBuilder = function() {
466
534
  var builder = {
467
535
  configure: function(chart, data) {
468
- configureScales.bind(this)(chart, data);
536
+ if(!chart.x){
537
+ d4.builders.ordinalScaleForNestedData(chart, data, 'x');
538
+ }
539
+ if(!chart.y){
540
+ d4.builders.linearScaleForNestedData(chart, data, 'y');
541
+ }
469
542
  }
470
543
  };
471
544
  return builder;
@@ -490,7 +563,7 @@
490
563
  { x: '2013', y:40 },
491
564
  { x: '2014', y:50 },
492
565
  ];
493
- var chart = d4.columnChart();
566
+ var chart = d4.charts.column();
494
567
  d3.select('#example')
495
568
  .datum(data)
496
569
  .call(chart);
@@ -505,7 +578,7 @@ The default format may not be desired and so we'll override it:
505
578
  ['2013', 40],
506
579
  ['2014', 50]
507
580
  ];
508
- var chart = d4.columnChart()
581
+ var chart = d4.charts.column()
509
582
  .xKey(0)
510
583
  .yKey(1);
511
584
 
@@ -514,12 +587,12 @@ The default format may not be desired and so we'll override it:
514
587
  .call(chart);
515
588
 
516
589
  */
517
- d4.columnChart = function columnChart() {
590
+ d4.chart('column', function columnChart() {
518
591
  var chart = d4.baseChart({}, columnChartBuilder);
519
592
  [{
520
- 'bars': d4.features.columnSeries
593
+ 'bars': d4.features.stackedColumnSeries
521
594
  }, {
522
- 'barLabels': d4.features.columnLabels
595
+ 'barLabels': d4.features.stackedColumnLabels
523
596
  }, {
524
597
  'xAxis': d4.features.xAxis
525
598
  }, {
@@ -528,7 +601,7 @@ The default format may not be desired and so we'll override it:
528
601
  chart.mixin(feature);
529
602
  });
530
603
  return chart;
531
- };
604
+ });
532
605
  }).call(this);
533
606
 
534
607
  (function() {
@@ -539,44 +612,14 @@ The default format may not be desired and so we'll override it:
539
612
  'use strict';
540
613
 
541
614
  var groupedColumnChartBuilder = function() {
542
- var extractValues = function(data, key) {
543
- var values = data.map(function(obj){
544
- return obj.values.map(function(i){
545
- return i[key];
546
- }.bind(this));
547
- }.bind(this));
548
- return d3.merge(values);
549
- };
550
-
551
- var configureX = function(chart, data) {
552
- if (!chart.x) {
553
- var xData = extractValues(data, chart.xKey);
554
- chart.xRoundBands = chart.xRoundBands || 0.3;
555
- chart.x = d3.scale.ordinal()
556
- .domain(xData)
557
- .rangeRoundBands([0, chart.width - chart.margin.left - chart.margin.right], chart.xRoundBands);
558
- }
559
- };
560
-
561
- var configureY = function(chart, data) {
562
- if (!chart.y) {
563
- var yData = extractValues(data, chart.yKey);
564
- var ext = d3.extent(yData);
565
- chart.y = d3.scale.linear().domain([Math.min(0, ext[0]),ext[1]]);
566
- }
567
- chart.y.range([chart.height - chart.margin.top - chart.margin.bottom, 0])
568
- .clamp(true)
569
- .nice();
570
- };
571
-
572
- var configureScales = function(chart, data) {
573
- configureX.bind(this)(chart, data);
574
- configureY.bind(this)(chart, data);
575
- };
576
-
577
615
  var builder = {
578
616
  configure: function(chart, data) {
579
- configureScales.bind(this)(chart, data);
617
+ if(!chart.x){
618
+ d4.builders.ordinalScaleForNestedData(chart, data, 'x');
619
+ }
620
+ if(!chart.y){
621
+ d4.builders.linearScaleForNestedData(chart, data, 'y');
622
+ }
580
623
  }
581
624
  };
582
625
  return builder;
@@ -620,7 +663,7 @@ relative distribution.
620
663
  .y('unitsSold')
621
664
  .value('unitsSold')(data);
622
665
 
623
- var chart = d4.groupedColumnChart()
666
+ var chart = d4.charts.groupedColumn()
624
667
  .width($('#example').width())
625
668
  .xKey('year')
626
669
  .yKey('unitsSold')
@@ -631,7 +674,7 @@ relative distribution.
631
674
  .call(chart);
632
675
 
633
676
  */
634
- d4.groupedColumnChart = function groupedColumnChart() {
677
+ d4.chart('groupedColumn', function groupedColumnChart() {
635
678
  var chart = d4.baseChart({
636
679
  accessors: ['groupsOf'],
637
680
  groupsOf: 1
@@ -648,7 +691,7 @@ relative distribution.
648
691
  chart.mixin(feature);
649
692
  });
650
693
  return chart;
651
- };
694
+ });
652
695
  }).call(this);
653
696
 
654
697
  (function() {
@@ -659,44 +702,14 @@ relative distribution.
659
702
  'use strict';
660
703
 
661
704
  var lineChartBuilder = function() {
662
- var extractValues = function(data, key) {
663
- var values = data.map(function(obj){
664
- return obj.values.map(function(i){
665
- return i[key];
666
- }.bind(this));
667
- }.bind(this));
668
- return d3.merge(values);
669
- };
670
-
671
- var configureX = function(chart, data) {
672
- if (!chart.x) {
673
- var xData = extractValues(data, chart.xKey);
674
- chart.xRoundBands = chart.xRoundBands || 0.3;
675
- chart.x = d3.scale.ordinal()
676
- .domain(xData)
677
- .rangeRoundBands([0, chart.width - chart.margin.left - chart.margin.right], chart.xRoundBands);
678
- }
679
- };
680
-
681
- var configureY = function(chart, data) {
682
- if (!chart.y) {
683
- var yData = extractValues(data, chart.yKey);
684
- var ext = d3.extent(yData);
685
- chart.y = d3.scale.linear().domain(ext);
686
- }
687
- chart.y.range([chart.height - chart.margin.top - chart.margin.bottom, 0])
688
- .clamp(true)
689
- .nice();
690
- };
691
-
692
- var configureScales = function(chart, data) {
693
- configureX.bind(this)(chart, data);
694
- configureY.bind(this)(chart, data);
695
- };
696
-
697
705
  var builder = {
698
706
  configure: function(chart, data) {
699
- configureScales.bind(this)(chart, data);
707
+ if(!chart.x){
708
+ d4.builders.ordinalScaleForNestedData(chart, data, 'x');
709
+ }
710
+ if(!chart.y){
711
+ d4.builders.linearScaleForNestedData(chart, data, 'y');
712
+ }
700
713
  }
701
714
  };
702
715
  return builder;
@@ -744,7 +757,7 @@ relative distribution.
744
757
  return 'unitsSold';
745
758
  })(data);
746
759
 
747
- var chart = d4.lineChart()
760
+ var chart = d4.charts.line()
748
761
  .width($('#example').width())
749
762
  .xKey('year')
750
763
  .yKey('unitsSold');
@@ -754,12 +767,12 @@ relative distribution.
754
767
  .call(chart);
755
768
 
756
769
  */
757
- d4.lineChart = function lineChart() {
770
+ d4.chart('line', function lineChart() {
758
771
  var chart = d4.baseChart({}, lineChartBuilder);
759
772
  [{
760
- 'linesSeries': d4.features.lineSeries
773
+ 'lineSeries': d4.features.lineSeries
761
774
  },{
762
- 'linesSeriesLabels': d4.features.lineSeriesLabels
775
+ 'lineSeriesLabels': d4.features.lineSeriesLabels
763
776
  }, {
764
777
  'xAxis': d4.features.xAxis
765
778
  }, {
@@ -768,7 +781,7 @@ relative distribution.
768
781
  chart.mixin(feature);
769
782
  });
770
783
  return chart;
771
- };
784
+ });
772
785
  }).call(this);
773
786
  (function() {
774
787
  /*!
@@ -778,37 +791,15 @@ relative distribution.
778
791
  'use strict';
779
792
 
780
793
  var rowChartBuilder = function() {
781
- var configureX = function(chart, data) {
782
- if (!chart.x) {
783
- chart.x = d3.scale.linear()
784
- .domain(d3.extent(data, function(d) {
785
- return d[chart.xKey];
786
- }.bind(this)));
787
- }
788
- chart.x.range([0, chart.width - chart.margin.right - chart.margin.left])
789
- .clamp(true)
790
- .nice();
791
- };
792
-
793
- var configureY = function(chart, data) {
794
- if (!chart.y) {
795
- chart.yRoundBands = chart.yRoundBands || 0.3;
796
- chart.y = d3.scale.ordinal()
797
- .domain(data.map(function(d) {
798
- return d[chart.yKey];
799
- }.bind(this)))
800
- .rangeRoundBands([chart.height - chart.margin.top - chart.margin.bottom, 0], chart.yRoundBands);
801
- }
802
- };
803
-
804
- var configureScales = function(chart, data) {
805
- configureX.bind(this)(chart, data);
806
- configureY.bind(this)(chart, data);
807
- };
808
-
809
794
  var builder = {
810
795
  configure: function(chart, data) {
811
- configureScales.bind(this)(chart, data);
796
+ if(!chart.x){
797
+ d4.builders.linearScaleForNestedData(chart, data, 'x');
798
+ }
799
+
800
+ if(!chart.y){
801
+ d4.builders.ordinalScaleForNestedData(chart, data, 'y');
802
+ }
812
803
  }
813
804
  };
814
805
  return builder;
@@ -833,14 +824,14 @@ relative distribution.
833
824
  { y: '2013', x:40 },
834
825
  { y: '2014', x:50 },
835
826
  ];
836
- var chart = d4.rowChart();
827
+ var chart = d4.charts.row();
837
828
  d3.select('#example')
838
829
  .datum(data)
839
830
  .call(chart);
840
831
 
841
832
 
842
833
  */
843
- d4.rowChart = function rowChart() {
834
+ d4.chart('row', function rowChart() {
844
835
  var chart = d4.baseChart({
845
836
  margin: {
846
837
  top: 20,
@@ -861,7 +852,7 @@ relative distribution.
861
852
  chart.mixin(feature);
862
853
  });
863
854
  return chart;
864
- };
855
+ });
865
856
  }).call(this);
866
857
  (function() {
867
858
  /*!
@@ -871,49 +862,21 @@ relative distribution.
871
862
  'use strict';
872
863
 
873
864
  var scatterPlotBuilder = function() {
874
- var configureX = function(chart, data) {
875
- if (!chart.x) {
876
- var ext = d3.extent(data, function(d) {
877
- return d[chart.xKey];
878
- }.bind(this));
879
- chart.x = d3.scale.linear()
880
- .domain(ext)
881
- .nice()
882
- .clamp(true);
865
+ var configureScales = function(chart, data) {
866
+ if(!chart.x){
867
+ d4.builders.linearScaleForNestedData(chart, data, 'x');
883
868
  }
884
- chart.x.range([0, chart.width - chart.margin.left - chart.margin.right]);
885
- };
886
869
 
887
- var configureY = function(chart, data) {
888
- if (!chart.y) {
889
- var ext = d3.extent(data, function(d) {
890
- return d[chart.yKey];
891
- }.bind(this));
892
- chart.y = d3.scale.linear()
893
- .domain(ext)
894
- .nice()
895
- .clamp(true);
870
+ if(!chart.y){
871
+ d4.builders.linearScaleForNestedData(chart, data, 'y');
896
872
  }
897
- chart.y.range([chart.height - chart.margin.top - chart.margin.bottom, 0]);
898
- };
899
873
 
900
- var configureZ = function(chart, data) {
901
- if (!chart.z) {
902
- var ext = d3.extent(data, function(d) {
903
- return d[chart.zKey];
904
- }.bind(this));
905
- chart.z = d3.scale.linear()
906
- .domain(ext)
907
- .nice()
908
- .clamp(true);
874
+ if(!chart.z){
875
+ d4.builders.linearScaleForNestedData(chart, data, 'z');
876
+ var min = 5;
877
+ var max = Math.max(min + 1, (chart.height - chart.margin.top - chart.margin.bottom)/10);
878
+ chart.z.range([min, max]);
909
879
  }
910
- var maxSize = (chart.height - chart.margin.top - chart.margin.bottom);
911
- chart.z.range([maxSize / data.length, maxSize / (data.length * 5)]);
912
- };
913
- var configureScales = function(chart, data) {
914
- configureX.bind(this)(chart, data);
915
- configureY.bind(this)(chart, data);
916
- configureZ.bind(this)(chart, data);
917
880
  };
918
881
 
919
882
  var builder = {
@@ -924,13 +887,13 @@ relative distribution.
924
887
  return builder;
925
888
  };
926
889
 
927
- d4.scatterPlot = function() {
890
+ d4.chart('scatterPlot', function() {
928
891
  var chart = d4.baseChart({
929
892
  accessors: ['z', 'zKey'],
930
893
  zKey: 'z'
931
894
  }, scatterPlotBuilder);
932
895
  [{
933
- 'circles': d4.features.scatterSeries
896
+ 'circles': d4.features.dotSeries
934
897
  }, {
935
898
  'xAxis': d4.features.xAxis
936
899
  }, {
@@ -939,7 +902,7 @@ relative distribution.
939
902
  chart.mixin(feature);
940
903
  });
941
904
  return chart;
942
- };
905
+ });
943
906
  }).call(this);
944
907
 
945
908
  (function() {
@@ -950,58 +913,25 @@ relative distribution.
950
913
  'use strict';
951
914
 
952
915
  var stackedColumnChartBuilder = function() {
953
- var extractValues = function(data, key) {
954
- var values = data.map(function(obj){
955
- return obj.values.map(function(i){
956
- return i[key];
957
- }.bind(this));
958
- }.bind(this));
959
- return d3.merge(values);
960
- };
961
-
962
- var configureX = function(chart, data) {
963
- if (!chart.x) {
964
- var xData = extractValues(data, chart.xKey);
965
- chart.xRoundBands = chart.xRoundBands || 0.3;
966
- chart.x = d3.scale.ordinal()
967
- .domain(xData)
968
- .rangeRoundBands([0, chart.width - chart.margin.left - chart.margin.right], chart.xRoundBands);
969
- }
970
- };
971
-
972
- var configureY = function(chart, data) {
973
- if (!chart.y) {
974
- var ext = d3.extent(d3.merge(data.map(function(obj){
975
- return d3.extent(obj.values, function(d){
976
- return d.y + d.y0;
977
- });
978
- })));
979
- chart.y = d3.scale.linear().domain([Math.min(0, ext[0]),ext[1]]);
980
- }
981
- chart.y.range([chart.height - chart.margin.top - chart.margin.bottom, 0])
982
- .clamp(true)
983
- .nice();
984
- };
985
-
986
- var configureScales = function(chart, data) {
987
- configureX.bind(this)(chart, data);
988
- configureY.bind(this)(chart, data);
989
- };
990
-
991
916
  var builder = {
992
917
  configure: function(chart, data) {
993
- configureScales.bind(this)(chart, data);
918
+ if(!chart.x){
919
+ d4.builders.ordinalScaleForNestedData(chart, data, 'x');
920
+ }
921
+ if(!chart.y){
922
+ d4.builders.linearScaleForNestedData(chart, data, 'y');
923
+ }
994
924
  }
995
925
  };
996
926
  return builder;
997
927
  };
998
928
 
999
- d4.stackedColumnChart = function stackedColumnChart() {
929
+ d4.chart('stackedColumn', function stackedColumnChart() {
1000
930
  var chart = d4.baseChart({}, stackedColumnChartBuilder);
1001
931
  [{
1002
932
  'bars': d4.features.stackedColumnSeries
1003
933
  }, {
1004
- 'columnLabels': d4.features.stackedColumnLabels
934
+ 'barLabels': d4.features.stackedColumnLabels
1005
935
  }, {
1006
936
  'connectors': d4.features.stackedColumnConnectors
1007
937
  }, {
@@ -1012,7 +942,7 @@ relative distribution.
1012
942
  chart.mixin(feature);
1013
943
  });
1014
944
  return chart;
1015
- };
945
+ });
1016
946
  }).call(this);
1017
947
 
1018
948
  (function() {
@@ -1166,7 +1096,7 @@ relative distribution.
1166
1096
  return builder;
1167
1097
  };
1168
1098
 
1169
- d4.waterfallChart = function waterfallChart() {
1099
+ d4.chart('waterfall', function waterfallChart() {
1170
1100
  var chart = d4.baseChart({
1171
1101
  accessors: ['orientation'],
1172
1102
  orientation: orientation
@@ -1188,7 +1118,7 @@ relative distribution.
1188
1118
  });
1189
1119
 
1190
1120
  return chart;
1191
- };
1121
+ });
1192
1122
  }).call(this);
1193
1123
 
1194
1124
  (function() {
@@ -1197,7 +1127,7 @@ relative distribution.
1197
1127
  * global d4: false
1198
1128
  */
1199
1129
  'use strict';
1200
- d4.features.arrow = function(name) {
1130
+ d4.feature('arrow', function(name) {
1201
1131
  return {
1202
1132
  accessors: {
1203
1133
  tipSize: function(){
@@ -1257,7 +1187,7 @@ relative distribution.
1257
1187
  return arrow;
1258
1188
  }
1259
1189
  };
1260
- };
1190
+ });
1261
1191
  }).call(this);
1262
1192
 
1263
1193
  (function() {
@@ -1267,96 +1197,50 @@ relative distribution.
1267
1197
  */
1268
1198
 
1269
1199
  'use strict';
1270
- d4.features.columnLabels = function(name) {
1271
- return {
1272
- accessors: {
1273
- x: function(d) {
1274
- return this.x(d[this.xKey]) + (this.x.rangeBand() / 2);
1275
- },
1276
-
1277
- y: function(d) {
1278
- var height = Math.abs(this.y(d[this.yKey]) - this.y(0));
1279
- return (d[this.yKey] < 0 ? this.y(d[this.yKey]) - height : this.y(d[this.yKey])) - 5;
1280
- },
1281
-
1282
- text: function(d) {
1283
- return d3.format('').call(this, d[this.yKey]);
1284
- }
1285
- },
1286
- render: function(scope, data) {
1287
- this.featuresGroup.append('g').attr('class', name);
1288
- var label = this.svg.select('.'+name).selectAll('.'+name).data(data);
1289
- label.enter().append('text');
1290
- label.exit().remove();
1291
- label.attr('class', 'column-label')
1292
- .text(scope.accessors.text.bind(this))
1293
- .attr('x', scope.accessors.x.bind(this))
1294
- .attr('y', scope.accessors.y.bind(this));
1295
- return label;
1296
- }
1297
- };
1298
- };
1299
- }).call(this);
1300
-
1301
- /*!
1302
-
1303
- DEPRECATION WARNING: This feature is deprecated in favor of using the nested
1304
- column series renderer. Intrinsicly this makes sense because a normal column
1305
- chart is mearly a stacked column chart with only one series.
1306
- */
1307
- (function() {
1308
- /*!
1309
- * global d3: false
1310
- * global d4: false
1311
- */
1312
- 'use strict';
1313
- d4.features.columnSeries = function(name) {
1200
+ d4.feature('dotSeries', function(name) {
1314
1201
  return {
1315
1202
  accessors: {
1316
- x: function(d) {
1203
+ cx: function(d) {
1317
1204
  return this.x(d[this.xKey]);
1318
1205
  },
1319
1206
 
1320
- y: function(d) {
1321
- return d[this.yKey] < 0 ? this.y(0) : this.y(d[this.yKey]);
1322
- },
1323
-
1324
- width: function() {
1325
- return this.x.rangeBand();
1207
+ cy: function(d) {
1208
+ return this.y(d[this.yKey]);
1326
1209
  },
1327
1210
 
1328
- height: function(d) {
1329
- return Math.abs(this.y(d[this.yKey]) - this.y(0));
1211
+ r: function(d) {
1212
+ return this.z(d[this.zKey]);
1330
1213
  },
1331
1214
 
1332
- classes: function(d, i) {
1333
- return d[this.yKey] < 0 ? 'bar negative fill series' + i : 'bar positive fill series' + i;
1215
+ classes : function(d, i) {
1216
+ return 'dot series' + i + ' fill';
1334
1217
  }
1335
1218
  },
1336
1219
  render: function(scope, data) {
1337
1220
  this.featuresGroup.append('g').attr('class', name);
1338
- var series = this.svg.select('.' + name).selectAll('.' + name + 'Series').data(data);
1339
- series.enter().append('g');
1340
- series.exit().remove();
1341
- series.attr('class', function(d, i) {
1342
- return 'series' + i;
1343
- });
1221
+ var group = this.svg.select('.' + name).selectAll('.group')
1222
+ .data(data)
1223
+ .enter().append('g')
1224
+ .attr('class', function(d,i) {
1225
+ return 'series'+ i + ' ' + this.yKey;
1226
+ }.bind(this));
1344
1227
 
1345
- var bar = series.selectAll('rect').data(function(d) {
1346
- return [d];
1347
- });
1348
- bar.enter().append('rect');
1349
- bar.exit().remove();
1350
- bar.attr('class', scope.accessors.classes.bind(this))
1351
- .attr('x', scope.accessors.x.bind(this))
1352
- .attr('y', scope.accessors.y.bind(this))
1353
- .attr('width', scope.accessors.width.bind(this))
1354
- .attr('height', scope.accessors.height.bind(this));
1228
+ var dots = group.selectAll('circle')
1229
+ .data(function(d) {
1230
+ return d.values;
1231
+ }.bind(this));
1355
1232
 
1356
- return bar;
1233
+ dots.enter().append('circle');
1234
+ dots.exit().remove();
1235
+ dots
1236
+ .attr('class', scope.accessors.classes.bind(this))
1237
+ .attr('r', scope.accessors.r.bind(this))
1238
+ .attr('cx', scope.accessors.cx.bind(this))
1239
+ .attr('cy', scope.accessors.cy.bind(this));
1240
+ return dots;
1357
1241
  }
1358
1242
  };
1359
- };
1243
+ });
1360
1244
  }).call(this);
1361
1245
 
1362
1246
  (function() {
@@ -1365,7 +1249,7 @@ relative distribution.
1365
1249
  * global d4: false
1366
1250
  */
1367
1251
  'use strict';
1368
- d4.features.grid = function(name) {
1252
+ d4.feature('grid', function(name) {
1369
1253
 
1370
1254
  return {
1371
1255
  accessors: {
@@ -1404,7 +1288,7 @@ relative distribution.
1404
1288
  .tickFormat(''));
1405
1289
  }
1406
1290
  };
1407
- };
1291
+ });
1408
1292
  }).call(this);
1409
1293
  (function() {
1410
1294
  /*!
@@ -1412,7 +1296,7 @@ relative distribution.
1412
1296
  * global d4: false
1413
1297
  */
1414
1298
  'use strict';
1415
- d4.features.groupedColumnLabels = function(name) {
1299
+ d4.feature('groupedColumnLabels', function(name) {
1416
1300
  return {
1417
1301
  accessors: {
1418
1302
  x: function(d, i) {
@@ -1452,7 +1336,7 @@ relative distribution.
1452
1336
  return text;
1453
1337
  }
1454
1338
  };
1455
- };
1339
+ });
1456
1340
  }).call(this);
1457
1341
 
1458
1342
  (function() {
@@ -1461,7 +1345,7 @@ relative distribution.
1461
1345
  * global d4: false
1462
1346
  */
1463
1347
  'use strict';
1464
- d4.features.groupedColumnSeries = function(name) {
1348
+ d4.feature('groupedColumnSeries', function(name) {
1465
1349
  var sign = function(val) {
1466
1350
  return (val > 0) ? 'positive' : 'negative';
1467
1351
  };
@@ -1515,7 +1399,7 @@ relative distribution.
1515
1399
  return rect;
1516
1400
  }
1517
1401
  };
1518
- };
1402
+ });
1519
1403
  }).call(this);
1520
1404
 
1521
1405
  (function() {
@@ -1525,7 +1409,7 @@ relative distribution.
1525
1409
  */
1526
1410
 
1527
1411
  'use strict';
1528
- d4.features.lineSeriesLabels = function(name) {
1412
+ d4.feature('lineSeriesLabels', function(name) {
1529
1413
  return {
1530
1414
  accessors: {
1531
1415
  x: function(d) {
@@ -1560,7 +1444,7 @@ relative distribution.
1560
1444
  return label;
1561
1445
  }
1562
1446
  };
1563
- };
1447
+ });
1564
1448
  }).call(this);
1565
1449
  (function() {
1566
1450
  /*!
@@ -1569,7 +1453,7 @@ relative distribution.
1569
1453
  */
1570
1454
 
1571
1455
  'use strict';
1572
- d4.features.lineSeries = function(name) {
1456
+ d4.feature('lineSeries', function(name) {
1573
1457
  return {
1574
1458
  accessors: {
1575
1459
  x: function(d) {
@@ -1608,7 +1492,7 @@ relative distribution.
1608
1492
  });
1609
1493
  }
1610
1494
  };
1611
- };
1495
+ });
1612
1496
  }).call(this);
1613
1497
 
1614
1498
  (function() {
@@ -1618,7 +1502,7 @@ relative distribution.
1618
1502
  */
1619
1503
 
1620
1504
  'use strict';
1621
- d4.features.referenceLine = function(name) {
1505
+ d4.feature('referenceLine', function(name) {
1622
1506
  return {
1623
1507
  accessors: {
1624
1508
  x1: function() {
@@ -1649,7 +1533,7 @@ relative distribution.
1649
1533
  return referenceLine;
1650
1534
  }
1651
1535
  };
1652
- };
1536
+ });
1653
1537
  }).call(this);
1654
1538
 
1655
1539
  (function() {
@@ -1659,11 +1543,12 @@ relative distribution.
1659
1543
  */
1660
1544
 
1661
1545
  'use strict';
1662
- d4.features.rowLabels = function(name) {
1546
+ d4.feature('rowLabels', function(name) {
1663
1547
  return {
1664
1548
  accessors: {
1665
1549
  x: function(d) {
1666
- return this.x(Math.min(0, d[this.xKey])) + Math.abs(this.x(d[this.xKey]) - this.x(0)) + 20;
1550
+ var width = (Math.abs(this.x(d[this.xKey])) + this.x(d[this.xKey]))/2;
1551
+ return Math.max(this.x(0), width) + 10;
1667
1552
  },
1668
1553
 
1669
1554
  y: function(d) {
@@ -1676,17 +1561,27 @@ relative distribution.
1676
1561
  },
1677
1562
  render: function(scope, data) {
1678
1563
  this.featuresGroup.append('g').attr('class', name);
1679
- var label = this.svg.select('.'+name).selectAll('.'+name).data(data);
1680
- label.enter().append('text');
1681
- label.exit().remove();
1682
- label.attr('class', 'column-label')
1564
+ var group = this.svg.select('.' + name).selectAll('.group')
1565
+ .data(data)
1566
+ .enter().append('g')
1567
+ .attr('class', function(d, i) {
1568
+ return 'series' + i + ' ' + this.xKey;
1569
+ }.bind(this));
1570
+
1571
+ var text = group.selectAll('text')
1572
+ .data(function(d) {
1573
+ return d.values;
1574
+ }.bind(this));
1575
+ text.exit().remove();
1576
+ text.enter().append('text')
1683
1577
  .text(scope.accessors.text.bind(this))
1684
- .attr('x', scope.accessors.x.bind(this))
1685
- .attr('y', scope.accessors.y.bind(this));
1686
- return label;
1578
+ .attr('class', 'column-label')
1579
+ .attr('y', scope.accessors.y.bind(this))
1580
+ .attr('x', scope.accessors.x.bind(this));
1581
+ return text;
1687
1582
  }
1688
1583
  };
1689
- };
1584
+ });
1690
1585
  }).call(this);
1691
1586
 
1692
1587
  (function() {
@@ -1696,86 +1591,58 @@ relative distribution.
1696
1591
  */
1697
1592
 
1698
1593
  'use strict';
1699
- d4.features.rowSeries = function(name) {
1594
+ d4.feature('rowSeries', function(name) {
1595
+ var sign = function(val){
1596
+ return (val > 0) ? 'positive' : 'negative';
1597
+ };
1598
+
1700
1599
  return {
1701
1600
  accessors: {
1702
1601
  x: function(d) {
1703
- return this.x(Math.min(0, d[this.xKey]));
1602
+ var xVal = d[this.xKey] - Math.max(0, d[this.xKey]);
1603
+ return this.x(xVal);
1704
1604
  },
1705
1605
 
1706
1606
  y: function(d) {
1707
1607
  return this.y(d[this.yKey]);
1708
1608
  },
1709
1609
 
1710
- height: function() {
1711
- return this.y.rangeBand();
1712
- },
1713
-
1714
1610
  width: function(d) {
1715
1611
  return Math.abs(this.x(d[this.xKey]) - this.x(0));
1716
1612
  },
1717
1613
 
1718
- classes: function(d, i) {
1719
- return d[this.xKey] < 0 ? 'bar negative fill series' + i : 'bar positive fill series' + i;
1614
+ height: function() {
1615
+ return this.y.rangeBand();
1616
+ },
1617
+
1618
+ classes: function(d,i) {
1619
+ return 'bar fill item'+ i + ' ' + sign(d.y) + ' ' + d[this.xKey];
1720
1620
  }
1721
1621
  },
1722
1622
  render: function(scope, data) {
1723
1623
  this.featuresGroup.append('g').attr('class', name);
1724
- var bar = this.svg.select('.'+name).selectAll('.'+name).data(data);
1725
- bar.enter().append('rect');
1726
- bar.exit().remove();
1727
- bar.attr('class', scope.accessors.classes.bind(this))
1624
+ var group = this.svg.select('.' + name).selectAll('.group')
1625
+ .data(data)
1626
+ .enter().append('g')
1627
+ .attr('class', function(d,i) {
1628
+ return 'series'+ i + ' ' + this.yKey;
1629
+ }.bind(this));
1630
+
1631
+ var rect = group.selectAll('rect')
1632
+ .data(function(d) {
1633
+ return d.values;
1634
+ }.bind(this));
1635
+
1636
+ rect.enter().append('rect')
1637
+ .attr('class', scope.accessors.classes.bind(this))
1728
1638
  .attr('x', scope.accessors.x.bind(this))
1729
1639
  .attr('y', scope.accessors.y.bind(this))
1730
1640
  .attr('width', scope.accessors.width.bind(this))
1731
1641
  .attr('height', scope.accessors.height.bind(this));
1732
-
1733
- return bar;
1734
- }
1735
- };
1736
- };
1737
- }).call(this);
1738
-
1739
- (function() {
1740
- /*!
1741
- * global d3: false
1742
- * global d4: false
1743
- */
1744
-
1745
- 'use strict';
1746
- d4.features.scatterSeries = function(name) {
1747
- return {
1748
- accessors: {
1749
- cx: function(d) {
1750
- return this.x(d[this.xKey]);
1751
- },
1752
-
1753
- cy: function(d) {
1754
- return this.y(d[this.yKey]);
1755
- },
1756
-
1757
- r: function(d) {
1758
- return this.z(d[this.zKey]);
1759
- },
1760
-
1761
- classes : function(d, i) {
1762
- return 'dot series' + i + ' fill';
1763
- }
1764
- },
1765
- render: function(scope, data) {
1766
- this.featuresGroup.append('g').attr('class', name);
1767
- var dots = this.svg.select('.'+name).selectAll('.'+name).data(data);
1768
- dots.enter().append('circle');
1769
- dots.attr('class', scope.accessors.classes.bind(this))
1770
- .attr('r', scope.accessors.r.bind(this))
1771
- .attr('cx', scope.accessors.cx.bind(this))
1772
- .attr('cy', scope.accessors.cy.bind(this));
1773
-
1774
- // returning a selection allows d4 to bind d3 events to it.
1775
- return dots;
1642
+ return rect;
1776
1643
  }
1777
1644
  };
1778
- };
1645
+ });
1779
1646
  }).call(this);
1780
1647
 
1781
1648
  (function() {
@@ -1791,7 +1658,7 @@ relative distribution.
1791
1658
  location. This creates a messy collection of crisscrossing lines.
1792
1659
  */
1793
1660
  'use strict';
1794
- d4.features.stackedColumnConnectors = function(name) {
1661
+ d4.feature('stackedColumnConnectors', function(name) {
1795
1662
 
1796
1663
  return {
1797
1664
  accessors: {
@@ -1860,7 +1727,7 @@ relative distribution.
1860
1727
  return lines;
1861
1728
  }
1862
1729
  };
1863
- };
1730
+ });
1864
1731
  }).call(this);
1865
1732
 
1866
1733
  (function() {
@@ -1870,7 +1737,7 @@ relative distribution.
1870
1737
  */
1871
1738
 
1872
1739
  'use strict';
1873
- d4.features.stackedColumnLabels = function(name) {
1740
+ d4.feature('stackedColumnLabels', function(name) {
1874
1741
  var sign = function(val) {
1875
1742
  return val > 0 ? 'positive' : 'negative';
1876
1743
  };
@@ -1882,13 +1749,22 @@ relative distribution.
1882
1749
  },
1883
1750
 
1884
1751
  y: function(d) {
1885
- var halfHeight = Math.abs(this.y(d.y0) - this.y(d.y0 + d.y)) / 2;
1886
- var yVal = d.y0 + d.y;
1887
- return (yVal < 0 ? this.y(d.y0) : this.y(yVal)) + halfHeight;
1752
+ if(typeof d.y0 !== 'undefined'){
1753
+ var halfHeight = Math.abs(this.y(d.y0) - this.y(d.y0 + d.y)) / 2;
1754
+ var yVal = d.y0 + d.y;
1755
+ return (yVal < 0 ? this.y(d.y0) : this.y(yVal)) + halfHeight;
1756
+ } else {
1757
+ var height = Math.abs(this.y(d[this.yKey]) - this.y(0));
1758
+ return (d[this.yKey] < 0 ? this.y(d[this.yKey]) - height : this.y(d[this.yKey])) - 5;
1759
+ }
1888
1760
  },
1889
1761
 
1890
1762
  text: function(d) {
1891
- if(Math.abs(this.y(d.y0) - this.y(d.y0 + d.y)) > 20) {
1763
+ if(typeof d.y0 !== 'undefined'){
1764
+ if(Math.abs(this.y(d.y0) - this.y(d.y0 + d.y)) > 20) {
1765
+ return d3.format('').call(this, d[this.valueKey]);
1766
+ }
1767
+ } else {
1892
1768
  return d3.format('').call(this, d[this.valueKey]);
1893
1769
  }
1894
1770
  }
@@ -1915,7 +1791,7 @@ relative distribution.
1915
1791
  return text;
1916
1792
  }
1917
1793
  };
1918
- };
1794
+ });
1919
1795
  }).call(this);
1920
1796
 
1921
1797
  (function() {
@@ -1925,7 +1801,7 @@ relative distribution.
1925
1801
  */
1926
1802
 
1927
1803
  'use strict';
1928
- d4.features.stackedColumnSeries = function(name) {
1804
+ d4.feature('stackedColumnSeries', function(name) {
1929
1805
  var sign = function(val){
1930
1806
  return (val > 0) ? 'positive' : 'negative';
1931
1807
  };
@@ -1937,8 +1813,12 @@ relative distribution.
1937
1813
  },
1938
1814
 
1939
1815
  y: function(d) {
1940
- var yVal = d.y0 + d.y;
1941
- return yVal < 0 ? this.y(d.y0) : this.y(yVal);
1816
+ if(d.y0){
1817
+ var yVal = d.y0 + d.y;
1818
+ return yVal < 0 ? this.y(d.y0) : this.y(yVal);
1819
+ } else {
1820
+ return d[this.yKey] < 0 ? this.y(0) : this.y(d[this.yKey]);
1821
+ }
1942
1822
  },
1943
1823
 
1944
1824
  width: function() {
@@ -1946,7 +1826,11 @@ relative distribution.
1946
1826
  },
1947
1827
 
1948
1828
  height: function(d) {
1949
- return Math.abs(this.y(d.y0) - this.y(d.y0 + d.y));
1829
+ if(d.y0){
1830
+ return Math.abs(this.y(d.y0) - this.y(d.y0 + d.y));
1831
+ }else {
1832
+ return Math.abs(this.y(d[this.yKey]) - this.y(0));
1833
+ }
1950
1834
  },
1951
1835
 
1952
1836
  classes: function(d,i) {
@@ -1976,7 +1860,7 @@ relative distribution.
1976
1860
  return rect;
1977
1861
  }
1978
1862
  };
1979
- };
1863
+ });
1980
1864
  }).call(this);
1981
1865
 
1982
1866
  (function() {
@@ -1986,7 +1870,7 @@ relative distribution.
1986
1870
  */
1987
1871
 
1988
1872
  'use strict';
1989
- d4.features.trendLine = function(name) {
1873
+ d4.feature('trendLine', function(name) {
1990
1874
  return {
1991
1875
  accessors: {
1992
1876
  x1: function() {
@@ -2050,7 +1934,7 @@ relative distribution.
2050
1934
  return trendLine;
2051
1935
  }
2052
1936
  };
2053
- };
1937
+ });
2054
1938
  }).call(this);
2055
1939
 
2056
1940
  (function() {
@@ -2076,7 +1960,7 @@ the direction of the lines.
2076
1960
  `classes` - applies the class to the connector lines.
2077
1961
 
2078
1962
  */
2079
- d4.features.waterfallConnectors = function(name) {
1963
+ d4.feature('waterfallConnectors', function(name) {
2080
1964
  return {
2081
1965
  accessors: {
2082
1966
  x: function(d) {
@@ -2163,7 +2047,7 @@ the direction of the lines.
2163
2047
  return lines;
2164
2048
  }
2165
2049
  };
2166
- };
2050
+ });
2167
2051
  }).call(this);
2168
2052
 
2169
2053
  (function() {
@@ -2173,7 +2057,7 @@ the direction of the lines.
2173
2057
  */
2174
2058
 
2175
2059
  'use strict';
2176
- d4.features.xAxis = function(name) {
2060
+ d4.feature('xAxis', function(name) {
2177
2061
  return {
2178
2062
  accessors: {
2179
2063
  format: function(xAxis) {
@@ -2189,7 +2073,7 @@ the direction of the lines.
2189
2073
 
2190
2074
  }
2191
2075
  };
2192
- };
2076
+ });
2193
2077
  }).call(this);
2194
2078
 
2195
2079
  (function() {
@@ -2199,7 +2083,7 @@ the direction of the lines.
2199
2083
  */
2200
2084
 
2201
2085
  'use strict';
2202
- d4.features.yAxis = function(name) {
2086
+ d4.feature('yAxis', function(name) {
2203
2087
 
2204
2088
  // FIXME: This should be a util function
2205
2089
  // Extracted from: http://bl.ocks.org/mbostock/7555321
@@ -2246,7 +2130,7 @@ the direction of the lines.
2246
2130
  .call(wrap, this.margin.left);
2247
2131
  }
2248
2132
  };
2249
- };
2133
+ });
2250
2134
  }).call(this);
2251
2135
  (function() {
2252
2136
  /*! global d3: false */
@@ -2295,7 +2179,7 @@ Keep reading for more information on these various accessor functions.
2295
2179
  {"year" : "2010", "category" : "Category Four", "value" : 5 }]
2296
2180
 
2297
2181
  **/
2298
- d4.parsers.nestedGroup = function nestedGroup() {
2182
+ d4.parser('nestedGroup', function nestedGroup() {
2299
2183
 
2300
2184
  var opts = {
2301
2185
  x: {
@@ -2349,7 +2233,7 @@ Keep reading for more information on these various accessor functions.
2349
2233
  };
2350
2234
 
2351
2235
  parser.nestKey = function(funct) {
2352
- opts.nestKey = funct.bind(opts);
2236
+ opts.nestKey = d4.functor(funct).bind(opts);
2353
2237
  return parser;
2354
2238
  };
2355
2239
 
@@ -2369,7 +2253,7 @@ Keep reading for more information on these various accessor functions.
2369
2253
  };
2370
2254
 
2371
2255
  return parser;
2372
- };
2256
+ });
2373
2257
  }).call(this);
2374
2258
 
2375
2259
  (function() {
@@ -2472,7 +2356,7 @@ The `parser` variable will now be an object containing the following structure:
2472
2356
  }
2473
2357
 
2474
2358
  **/
2475
- d4.parsers.nestedStack = function nestedStack() {
2359
+ d4.parser('nestedStack', function nestedStack() {
2476
2360
 
2477
2361
  var opts = {
2478
2362
  x: {
@@ -2567,7 +2451,7 @@ The `parser` variable will now be an object containing the following structure:
2567
2451
  };
2568
2452
 
2569
2453
  return parser;
2570
- };
2454
+ });
2571
2455
  }).call(this);
2572
2456
 
2573
2457
  (function() {
@@ -2668,7 +2552,7 @@ The `parser` variable will now be an object containing the following structure:
2668
2552
  * y - an object with a key representing the y accessor and an array of values
2669
2553
 
2670
2554
  **/
2671
- d4.parsers.waterfall = function waterfall() {
2555
+ d4.parser('waterfall', function waterfall() {
2672
2556
 
2673
2557
  var opts = {
2674
2558
  x: {
@@ -2762,7 +2646,7 @@ The `parser` variable will now be an object containing the following structure:
2762
2646
  };
2763
2647
 
2764
2648
  parser.nestKey = function(funct) {
2765
- opts.nestKey = funct.bind(opts);
2649
+ opts.nestKey = d4.functor(funct).bind(opts);
2766
2650
  return parser;
2767
2651
  };
2768
2652
 
@@ -2782,5 +2666,72 @@ The `parser` variable will now be an object containing the following structure:
2782
2666
  };
2783
2667
 
2784
2668
  return parser;
2669
+ });
2670
+ }).call(this);
2671
+
2672
+ (function() {
2673
+ /*!
2674
+ * global d3: false
2675
+ * global d4: false
2676
+ */
2677
+ 'use strict';
2678
+
2679
+ var extractValues = function(data, key) {
2680
+ var values = data.map(function(obj) {
2681
+ return obj.values.map(function(i) {
2682
+ return i[key];
2683
+ }.bind(this));
2684
+ }.bind(this));
2685
+ return d3.merge(values);
2686
+ };
2687
+
2688
+ var rangeFor = function(chart, dimension) {
2689
+
2690
+ // This may not be a very robust approach.
2691
+ switch (dimension) {
2692
+ case 'x':
2693
+ return [0, chart.width - chart.margin.left - chart.margin.right];
2694
+ case 'y':
2695
+ return [chart.height - chart.margin.top - chart.margin.bottom, 0];
2696
+ default:
2697
+ return [];
2698
+ }
2785
2699
  };
2700
+
2701
+ /**
2702
+ * Creates a linear scale for a dimension of a given chart.
2703
+ * @param {Object} d4 chart object
2704
+ * @param {Array} data array
2705
+ * @param {string} string represnting a dimension e.g. `x`,`y`.
2706
+ * @returns {Object} Chart scale object
2707
+ */
2708
+ d4.builder('linearScaleForNestedData', function(chart, data, dimension) {
2709
+ var key = chart[dimension + 'Key'];
2710
+ var ext = d3.extent(d3.merge(data.map(function(obj) {
2711
+ return d3.extent(obj.values, function(d) {
2712
+ return d[key] + (d.y0 || 0);
2713
+ });
2714
+ })));
2715
+ chart[dimension] = d3.scale.linear();
2716
+ return chart[dimension].domain([Math.min(0, ext[0]), ext[1]])
2717
+ .range(rangeFor(chart, dimension))
2718
+ .clamp(true)
2719
+ .nice();
2720
+ });
2721
+
2722
+ /**
2723
+ * Creates an ordinal scale for a dimension of a given chart.
2724
+ * @param {Object} d4 chart object
2725
+ * @param {Array} data array
2726
+ * @param {string} string represnting a dimension e.g. `x`,`y`.
2727
+ * @returns {Object} Chart scale object
2728
+ */
2729
+ d4.builder('ordinalScaleForNestedData', function(chart, data, dimension) {
2730
+ var parsedData = extractValues(data, chart[dimension + 'Key']);
2731
+ var bands = chart[dimension + 'RoundBands'] = chart[dimension + 'RoundBands'] || 0.3;
2732
+ chart[dimension] = d3.scale.ordinal();
2733
+ return chart[dimension]
2734
+ .domain(parsedData)
2735
+ .rangeRoundBands(rangeFor(chart, dimension), bands);
2736
+ });
2786
2737
  }).call(this);