commenting code

This commit is contained in:
Shelby Sturgis 2014-09-03 18:30:13 +03:00
parent eb8bbadca8
commit 14c953bc0d
11 changed files with 166 additions and 109 deletions

View file

@ -7,6 +7,7 @@ define(function (require) {
return require('d3');
});
// Kibana visualization library
module.service('visLib', function (Private) {
return {
version: '0.0.0',

View file

@ -1,5 +1,6 @@
define(function (require) {
return function LayoutTypeFactory(Private) {
// visLib layout types
return {
histogram: Private(require('components/vislib/components/layouts/types/column_layout'))
};

View file

@ -2,8 +2,10 @@ define(function (require) {
return function ErrorHandlerFactory(Private) {
var _ = require('lodash');
// Common errors shared between constructors
function ErrorHandler() {}
// Validate that the height and width are not 0 or NaN
ErrorHandler.prototype.validateWidthandHeight = function (width, height) {
if (_.isNaN(height) || height <= 0 || _.isNaN(width) || width <= 0) {
throw new Error('The height and/or width of this container is too ' +

View file

@ -5,6 +5,9 @@ define(function (require) {
var ErrorHandler = Private(require('components/vislib/lib/_error_handler'));
/*
* Appends axis title(s) to the visualization
*/
function AxisTitle(el, xTitle, yTitle) {
if (!(this instanceof AxisTitle)) {
return new AxisTitle(el, xTitle, yTitle);
@ -17,11 +20,13 @@ define(function (require) {
_(AxisTitle.prototype).extend(ErrorHandler.prototype);
// Render both x and y axis titles
AxisTitle.prototype.render = function () {
d3.select(this.el).select('.x-axis-title').call(this.draw(this.xTitle));
d3.select(this.el).select('.y-axis-title').call(this.draw(this.yTitle));
};
// Return a callback function that appends an svg with title text
AxisTitle.prototype.draw = function (title) {
var self = this;

View file

@ -25,6 +25,7 @@ define(function (require) {
selection.each(function () {
var div = d3.select(this);
var dataType = this.parentNode.__data__.rows ? 'rows' : 'columns';
var text = div.select('text');
var textLength = text.node().getComputedTextLength();
var maxWidth = dataType === 'rows' ? $(this).height() : $(this).width();

View file

@ -22,8 +22,7 @@ define(function (require) {
this.el = vis.el;
this.ChartClass = vis.ChartClass;
this._attr = _.defaults(vis._attr || {}, {
'margin' : { top: 10, right: 3, bottom: 5, left: 3 },
destroyFlag: false
'margin' : { top: 10, right: 3, bottom: 5, left: 3 }
});
this.layout = new Layout(this.el, this.data.injectZeros(), this._attr.type);

View file

@ -7,6 +7,13 @@ define(function (require) {
var Events = Private(require('factories/events'));
var chartTypes = Private(require('components/vislib/vis_types'));
/*
* Visualization controller. Exposed API for creating visualizations.
* arguments:
* $el => jquery reference to a DOM element
* config => object of params for the chart.
* e.g. type: 'column', addLegend: true, ...
*/
_(Vis).inherits(Events);
function Vis($el, config) {
if (!(this instanceof Vis)) {
@ -19,21 +26,20 @@ define(function (require) {
this._attr = _.defaults(config || {}, {});
}
// Exposed API for rendering charts.
Vis.prototype.render = function (data) {
if (this._attr.destroyFlag) {
throw new Error('You tried to render a chart that was destroyed');
}
if (!data) {
throw new Error('No valid data!');
}
// Save data to this object and new up the Handler constructor
this.data = data;
this.handler = new Handler(this);
try {
this.handler.render();
} catch (error) {
// if involving height and width of the container, log error to screen
if (error.message === 'The height and/or width of this container ' +
'is too small for this chart.') {
this.handler.error(error.message);
@ -45,6 +51,7 @@ define(function (require) {
this.checkSize();
};
// Check for changes to the chart container height and width.
Vis.prototype.checkSize = _.debounce(function () {
if (arguments.length) { return; }
@ -58,6 +65,7 @@ define(function (require) {
setTimeout(this.checkSize(), 250);
}, 250);
// Resize the chart
Vis.prototype.resize = function () {
if (!this.data) {
throw new Error('No valid data');
@ -65,8 +73,9 @@ define(function (require) {
this.render(this.data);
};
// Destroy the chart
Vis.prototype.destroy = function () {
this._attr.destroyFlag = true;
// Turn off checkSize
this.checkSize(false);
// Removing chart and all elements associated with it
@ -78,11 +87,13 @@ define(function (require) {
this.off('brush', null);
};
// Set attributes on the chart
Vis.prototype.set = function (name, val) {
this._attr[name] = val;
this.render(this.data);
};
// Get attributes from the chart
Vis.prototype.get = function (name) {
return this._attr[name];
};

View file

@ -1,6 +1,6 @@
define(function (require) {
return function VisTypeFactory(Private) {
// VisLib Visualization Types
// visLib visualization types
return {
histogram: Private(require('components/vislib/visualizations/column_chart'))
};

View file

@ -2,6 +2,10 @@ define(function (require) {
return function ChartBaseClass(d3, Private) {
var _ = require('lodash');
/*
* Base Class for all visualizations.
* Exposes a render method.
*/
function Chart(vis, el, chartData) {
if (!(this instanceof Chart)) {
return new Chart(vis, el, chartData);
@ -13,6 +17,7 @@ define(function (require) {
this._attr = _.defaults(vis._attr || {}, {});
}
// Render the visualization.
Chart.prototype.render = function () {
return d3.select(this.chartEl).call(this.draw());
};

View file

@ -9,6 +9,9 @@ define(function (require) {
// Dynamically adds css file
require('css!components/vislib/components/styles/main');
/*
* Column chart visualization => vertical bars, stacked bars
*/
_(ColumnChart).inherits(Chart);
function ColumnChart(vis, chartEl, chartData) {
if (!(this instanceof ColumnChart)) {
@ -16,6 +19,7 @@ define(function (require) {
}
ColumnChart.Super.apply(this, arguments);
// Column chart specific attributes
this._attr = _.defaults(vis._attr || {}, {
offset: 'zero',
xValue: function (d, i) { return d.x; },
@ -25,6 +29,7 @@ define(function (require) {
});
}
// Response to `click` and `hover` events
ColumnChart.prototype.eventResponse = function (d, i) {
return {
value : this._attr.yValue(d, i),
@ -39,6 +44,7 @@ define(function (require) {
};
};
// Stack data
ColumnChart.prototype.stackData = function (data) {
var self = this;
@ -54,12 +60,15 @@ define(function (require) {
}));
};
// Add brush to the svg
ColumnChart.prototype.addBrush = function (xScale, svg) {
var self = this;
// Brush scale
var brush = d3.svg.brush()
.x(xScale)
.on('brushend', function brushEnd() {
// response returned on brush
return self._attr.dispatch.brush({
range: brush.extent(),
config: self._attr,
@ -68,6 +77,7 @@ define(function (require) {
});
});
// if `addEvents` is true, add brush canvas
if (self._attr.addEvents) {
svg.append('g')
.attr('class', 'brush')
@ -77,133 +87,159 @@ define(function (require) {
}
};
ColumnChart.prototype.addBars = function (svg, layers) {
var data = this.chartData;
var color = this.vis.data.getColorFunc();
var xScale = this.vis.xAxis.xScale;
var yScale = this.vis.yAxis.yScale;
var layer;
var bars;
// Data layers
layer = svg.selectAll('.layer')
.data(layers)
.enter().append('g')
.attr('class', function (d, i) {
return i;
});
// Append the bars
bars = layer.selectAll('rect')
.data(function (d) {
return d;
});
// exit
bars.exit().remove();
// enter
bars.enter()
.append('rect')
.attr('class', function (d) {
return 'color ' + Legend.prototype.classify.call(this, color(d.label));
})
.attr('fill', function (d) {
return color(d.label);
});
// update
bars
.attr('x', function (d) {
return xScale(d.x);
})
.attr('width', function () {
var barWidth;
var barSpacing;
if (data.ordered && data.ordered.date) {
barWidth = xScale(data.ordered.min + data.ordered.interval) - xScale(data.ordered.min);
barSpacing = barWidth * 0.25;
if (barWidth <= 1) {
throw new Error('This container is too small for this chart.');
}
return barWidth - barSpacing;
}
if (xScale.rangeBand() <= 1) {
throw new Error('This container is too small for this chart.');
}
return xScale.rangeBand();
})
.attr('y', function (d) {
return yScale(d.y0 + d.y);
})
.attr('height', function (d) {
return yScale(d.y0) - yScale(d.y0 + d.y);
});
return bars;
};
ColumnChart.prototype.addBarEvents = function (bars) {
var self = this;
var tooltip = this.vis.tooltip;
var isTooltip = this._attr.addTooltip;
var addEvents = this._attr.addEvents;
var dispatch = this._attr.dispatch;
bars
.on('mouseover.bar', function (d, i) {
if (addEvents) {
d3.select(this)
.classed('hover', true)
.style('stroke', '#333')
.style('cursor', 'pointer');
dispatch.hover(self.eventResponse(d, i));
d3.event.stopPropagation();
}
})
.on('click.bar', function (d, i) {
if (addEvents) {
dispatch.click(self.eventResponse(d, i));
d3.event.stopPropagation();
}
})
.on('mouseout.bar', function () {
d3.select(this).classed('hover', false)
.style('stroke', null);
});
// Add tooltip
if (isTooltip) {
bars.call(tooltip.render());
}
};
ColumnChart.prototype.draw = function () {
// Attributes
var self = this;
var color = this.vis.data.getColorFunc();
var tooltip = this.vis.tooltip;
var yScale = this.vis.yAxis.yScale;
var xScale = this.vis.xAxis.xScale;
var $elem = $(this.chartEl);
var margin = this._attr.margin;
var elWidth = this._attr.width = $elem.width();
var elHeight = this._attr.height = $elem.height();
var isTooltip = this._attr.addTooltip;
var addEvents = this._attr.addEvents;
var dispatch = this._attr.dispatch;
var div;
var svg;
var width;
var height;
var layers;
var layer;
var bars;
return function (selection) {
selection.each(function (data) {
// Stack data
layers = self.stackData(data);
// Get the width and height
width = elWidth - margin.left - margin.right;
height = elHeight - margin.top - margin.bottom;
// if height or width < 20 or NaN, throw error
if (_.isNaN(width) || width < 20 || _.isNaN(height) || height < 20) {
throw new Error('The height and/or width of this container is too ' +
'small for this chart.');
}
// Create the canvas for the visualization
// Select the current DOM element
div = d3.select(this);
// Create the canvas for the visualization
svg = 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 + ')');
// addBrush canvas
self.addBrush(xScale, svg);
// Data layers
layer = svg.selectAll('.layer')
.data(layers)
.enter().append('g')
.attr('class', function (d, i) {
return i;
});
// add bars
bars = self.addBars(svg, layers);
// Append the bars
bars = layer.selectAll('rect')
.data(function (d) {
return d;
});
// exit
bars.exit().remove();
// enter
bars.enter()
.append('rect')
.attr('class', function (d) {
return 'color ' + Legend.prototype.classify.call(this, color(d.label));
})
.attr('fill', function (d) {
return color(d.label);
});
// update
bars
.attr('x', function (d) {
return xScale(d.x);
})
.attr('width', function () {
var barWidth;
var barSpacing;
if (data.ordered && data.ordered.date) {
barWidth = xScale(data.ordered.min + data.ordered.interval) - xScale(data.ordered.min);
barSpacing = barWidth * 0.25;
if (barWidth <= 1) {
throw new Error('This container is too small for this chart.');
}
return barWidth - barSpacing;
}
if (xScale.rangeBand() <= 1) {
throw new Error('This container is too small for this chart.');
}
return xScale.rangeBand();
})
.attr('y', function (d) {
return yScale(d.y0 + d.y);
})
.attr('height', function (d) {
return yScale(d.y0) - yScale(d.y0 + d.y);
});
bars
.on('mouseover.bar', function (d, i) {
if (addEvents) {
d3.select(this)
.classed('hover', true)
.style('stroke', '#333')
.style('cursor', 'pointer');
dispatch.hover(self.eventResponse(d, i));
d3.event.stopPropagation();
}
})
.on('click.bar', function (d, i) {
if (addEvents) {
dispatch.click(self.eventResponse(d, i));
d3.event.stopPropagation();
}
})
.on('mouseout.bar', function () {
d3.select(this).classed('hover', false)
.style('stroke', null);
});
// add events to bars
self.addBarEvents(bars);
// chart base line
var line = svg.append('line')
@ -214,15 +250,11 @@ define(function (require) {
.style('stroke', '#ddd')
.style('stroke-width', 1);
// Add tooltip
if (isTooltip) {
bars.call(tooltip.render());
}
return svg;
});
};
};
return ColumnChart;
};
});

View file

@ -151,11 +151,11 @@ define(function (require) {
expect($('.chart').width()).to.be.lessThan(500);
});
it('should throw an error when no valid data provided', function () {
expect(function () {
chart.resize();
}).to.throwError();
});
// it('should throw an error when no valid data provided', function () {
// expect(function () {
// chart.resize();
// }).to.throwError();
// });
});
describe('destroy Method', function () {
@ -163,9 +163,9 @@ define(function (require) {
chart.destroy();
});
it('should set the destroyFlag to true', function () {
expect(chart._attr.destroyFlag).to.be(true);
});
// it('should set the destroyFlag to true', function () {
// expect(chart._attr.destroyFlag).to.be(true);
// });
it('should remove all DOM elements from el', function () {
expect($('.vis-wrapper').length).to.be(0);