fixing vislib paths, etc.

This commit is contained in:
Shelby Sturgis 2014-06-18 12:54:42 -07:00
parent c8e1eb361f
commit adbb6b8b1f
18 changed files with 2645 additions and 16 deletions

3
.gitignore vendored
View file

@ -1,7 +1,6 @@
.DS_Store
node_modules
trash
src/bower_components/K4D3
src/bower_components/elasticsearch
build
target
target

View file

@ -1,7 +1,7 @@
define(function (require) {
var _ = require('lodash');
var $ = require('jquery');
var k4 = require('components/k4d3/index');
var vislib = require('components/vislib/index');
var app = require('modules').get('apps/discover');
@ -15,7 +15,7 @@ define(function (require) {
var init = function () {
// This elem should already have a height/width
var myChart = new k4.Chart(elem[0], {
var myChart = new vislib.Chart(elem[0], {
});

View file

@ -1,5 +1,5 @@
define(function (require) {
var k4d3 = require('components/k4d3/index');
var vislib = require('components/vislib/index');
var $ = require('jquery');
var _ = require('lodash');
var typeDefs = require('apps/visualize/saved_visualizations/_type_defs');
@ -90,7 +90,7 @@ define(function (require) {
_.merge(params, vis.params);
_.defaults(params, typeDefinition.params);
chart = new k4d3.Chart($visualize[0], params);
chart = new vislib.Chart($visualize[0], params);
// For each type of interaction, assign the the handler if the vis object has it
// otherwise use the typeDef, otherwise, do nothing.

@ -1 +0,0 @@
Subproject commit 65ca81b5f0f4a905918d0a96a9ec554578231c52

View file

@ -0,0 +1,24 @@
/*
* Main module
* Accepts an html element and a config object.
* Calls all other K4 modules.
* Returns the charting function.
*/
define(function(require) {
return function(elem, args) {
var type = args.type,
charts = {
'histogram': require('src/modules/histogram'),
'line': require('src/modules/lineChart'),
'area': require('src/modules/areaChart'),
};
if (typeof(charts[type]) !== 'function') { throw type + " is not a supported k4 function."; }
var chartFunc = charts[type](elem, args);
return chartFunc;
};
});

View file

@ -0,0 +1,13 @@
define(function(require) {
var k4 = {
version: '0.0.0',
legend: require('src/modules/legend'),
Chart: require('src/core'),
histogram: require('src/modules/histogram'),
line: require('src/modules/lineChart'),
area: require('src/modules/areaChart'),
};
return k4;
});

View file

@ -0,0 +1,574 @@
define(function(require) {
'use strict';
var d3 = require('lib/d3/d3'),
selectionFn = require('src/utils/selection'),
zeroInjection = require('src/utils/zeroInjection'),
legendFn = require('src/modules/legend'),
colorFn = require('src/utils/colorspace');
return function area(elem, args) {
if (typeof args === 'undefined') { args = {}; }
var chart = {},
/* ***** Chart Options ***** */
addLegend = args.addLegend || true,
addTooltip = args.addTooltip || true,
shareYAxis = args.shareYAxis || false,
/* ************************* */
/* ***** Chart Flags ******* */
destroyFlag = false,
/* ************************* */
/* ***** Chart Globals ******* */
dispatch = d3.dispatch('hover', 'click', 'mouseenter', 'mouseleave', 'mouseout', 'mouseover', 'brush'),
margin = args.margin || { top: 35, right: 15, bottom: 35, left: 50 },
$elem = $(elem), // cached jquery version of element
vis = d3.select(elem),
height,
width,
latestData,
chartwrapper,
selection,
selectors,
colDomain,
getColors,
legend,
tip,
allLayers,
color = d3.scale.category10(),
mousemove,
scrolltop,
allItms = false,
xValue = function(d, i) { return d.x; },
yValue = function(d, i) { return d.y; };
/* ************************* */
chart.render = function(data) {
// removes elements to redraw the chart on subsequent calls
d3.select(elem).selectAll('*').remove();
// store a copy of the data sent to render, so that it can be resent with .resize()
latestData = data;
if (destroyFlag || !elem) {
if (destroyFlag) { throw new Error('you destroyed this chart and then tried to use it again'); }
else { throw new Error('there is no element present'); }
}
// HTML div in which charts are contained
chartwrapper = d3.select(elem).append('div')
.attr('class', 'chartwrapper')
.style('height', $(elem).height() + 'px');
// Selection function - returns an array of DOM elements with data bound
selectors = selectionFn(chartwrapper[0][0], latestData);
try { selection = d3.selectAll(selectors); }
catch(error) { return; }
// get colDomain from chart obj
colDomain = chart.colorDomain(selection);
// color domain
getColors = colorFn(colDomain.length);
chart.colorObj = _.zipObject(colDomain, getColors);
// Chart options
if (addLegend) {
legend = legendFn(elem, selection, chart);
allItms = d3.select('.legendwrapper').selectAll('li.legends');
}
if (addTooltip) { tip = d3.select(elem).append('div').attr('class', 'k4tip'); }
try {
selection.each(function(d, i) {
var that = this;
chart.createAreaChart({'data': d, 'index': i, 'this': that});
});
} catch (err) {
if (err.message === 'chart too small') { chart.error(); }
console.group(err);
}
return chart;
};
/* Color domain */
chart.colorDomain = function(selection) {
var items = [];
selection.each(function(d) {
d.series.forEach(function(label) {
if (label.label) { items.push(label.label); }
else { items.push(d.yAxisLabel); }
});
});
items = _.uniq(items);
return items;
};
/* Function for global yAxis */
chart.globalYAxisFn = function(selection) {
var yArray = [];
selection.each(function(d) {
return d3.max(d.series, function(layer) {
return d3.max(layer.values, function(d) {
yArray.push(d.y);
});
});
});
return d3.max(yArray);
};
chart.getBounds = function(data) {
var bounds = [];
data.series.map(function(series) {
series.values.map(function(d, i) {
bounds.push({
x: xValue.call(series, d, i),
y: yValue.call(series, d, i)
});
});
});
return bounds;
};
chart.createAreaChart= function(args) {
if (typeof args === 'undefined') { args = {}; }
var data = args.data,
that = args.this,
// width, height, margins
elemWidth = parseInt(d3.select(that).style('width'), 10),
elemHeight = parseInt(d3.select(that).style('height'), 10),
width = elemWidth - margin.left - margin.right, // width of the parent element ???
height = elemHeight - margin.top - margin.bottom, // height of the parent element ???
seriesData = [],
xAxisLabel = data.xAxisLabel,
yAxisLabel = data.yAxisLabel,
label = data.label,
xAxisFormatter = data.xAxisFormatter,
yAxisFormatter = data.yAxisFormatter,
tooltipFormatter = data.tooltipFormatter,
brush;
data.series.map(function(series) {
seriesData.push(series);
});
var interpolate = "linear",
xTickScale = d3.scale.linear().clamp(true).domain([80, 300, 800]).range([0, 2, 4]),
xTickN = Math.floor(xTickScale(width)),
ytickScale = d3.scale.linear().clamp(true).domain([20, 40, 1000]).range([0, 1, 10]),
ytickN = Math.floor(ytickScale(height)),
xScale = d3.time.scale()
.domain(d3.extent(chart.getBounds(data), function(d) { return d.x; }))
.range([0, width]),
yScale = d3.scale.linear()
.domain([0, d3.max(chart.getBounds(data), function(d) { return d.y; })])
.range([height, 0]),
xAxis = d3.svg.axis().scale(xScale).ticks(xTickN).tickPadding(5).tickFormat(xAxisFormatter).orient("bottom"),
yAxis = d3.svg.axis().scale(yScale).ticks(ytickN).tickPadding(4).tickFormat(yAxisFormatter).orient("left"),
area = d3.svg.area().x(X).y0(height).y1(Y),
line = d3.svg.line().interpolate("linear").x(X).y(Y),
globalYAxis = chart.globalYAxisFn(selection);
// setting the y scale domain
if (shareYAxis) { yScale.domain([0, globalYAxis]).nice(ytickN); }
else {
yScale.domain([0, d3.max(chart.getBounds(data), function(d) { return d.y; })]).nice(ytickN);
}
var svg = d3.select(that).append("svg")
.attr("class", "canvas")
.attr("width", "100%")
.attr("height", "100%");
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// background rect
g.append('rect')
.attr('class', 'chart-bkgd')
.attr('width', width)
.attr('height', height);
g.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll('text')
.call(chart.tickText, width)
.on('mouseover', function(d) {
var hh = tip[0][0].scrollHeight;
mousemove = { left: d3.event.pageX, top: d3.event.pageY};
scrolltop = document.body.scrollTop;
d3.select(that).style('cursor', 'default');
return tip.datum(d).text(d)
.style('top', mousemove.top - scrolltop - hh/2 + 'px')
.style('left', mousemove.left + 20 + 'px')
.style('visibility', 'visible');
})
.on('mouseout', function() {
d3.select(that).classed('hover', false).style('stroke', null);
tip.style('visibility', 'hidden');
});
g.append("g")
.attr("class", "y axis")
.call(yAxis);
// Axis labels
g.append('text')
.attr('class', 'x-axis-label')
.attr('text-anchor', 'middle')
.attr('x', width / 2)
.attr('y', height + 30)
.text(xAxisLabel);
g.append('text')
.attr('class', 'y-axis-label')
.attr('text-anchor', 'middle')
.attr('x', -height / 2)
.attr('y', -40)
.attr('dy', '.75em')
.attr('transform', 'rotate(-90)')
.text(yAxisLabel);
// Chart title
g.append('text')
.attr('class', 'charts-label')
.attr('text-anchor', 'middle')
.attr('x', width / 2)
.attr('y', -10)
.text(label)
.call(chart.tickText, width)
.on('mouseover', function(d) {
var hh = tip[0][0].scrollHeight;
mousemove = { left: d3.event.pageX, top: d3.event.pageY};
scrolltop = document.body.scrollTop;
d3.select(that).style('cursor', 'default');
return tip.text(d.label)
.style('top', mousemove.top - scrolltop - hh/2 + 'px')
.style('left', mousemove.left + 20 + 'px')
.style('visibility', 'visible');
})
.on('mouseout', function() {
d3.select(that).classed('hover', false).style('stroke', null);
tip.style('visibility', 'hidden');
});
var lines = g.selectAll(".lines")
.data(seriesData)
.enter().append("g")
.attr("class", "lines");
lines.append("path")
.attr("d", function(d) { return line(d.values); })
.attr("fill", "none")
.style("stroke", function(d) { return d.label ? chart.colorObj[d.label] : chart.colorObj[yAxisLabel]; })
.style("stroke-width", "3px");
lines.append("path")
.attr("d", function(d) { return area(d.values); })
.style("fill", function(d) {
return d.label ? chart.colorObj[d.label] : chart.colorObj[yAxisLabel];
})
.style("stroke", function(d) { return d.label ? chart.colorObj[d.label] : chart.colorObj[yAxisLabel]; })
.style("stroke-width", "3px")
.style("opacity", 0.5);
var layer = g.selectAll(".layer")
.data(seriesData)
.enter().append("g")
.attr("class", function(d) { return "r" + d.label; })
.attr("stroke", function(d) { return d.label ? chart.colorObj[d.label] : chart.colorObj[yAxisLabel]; });
var circle = layer.selectAll(".points")
.data(function(d) { return d.values; })
.enter().append("circle")
.attr("class", "points")
.attr("cx", function(d) { return xScale(d.x); })
.attr("cy", function(d) { return yScale(d.y); })
/* css styling */
.attr("r", 8)
.attr("fill", "#ffffff")
.attr("stroke-width", 3.5)
.attr("opacity", 0)
.on("mouseover", function(d, i) {
var point = d3.select(this);
point.attr("opacity", 0.5);
d3.select(this)
.classed('hover', true)
.style('stroke', '#333')
.style('cursor', 'pointer');
dispatch.hover({
value: yValue(d, i),
point: d,
pointIndex: i,
series: data.series,
config: args,
data: latestData,
e: d3.event
});
d3.event.stopPropagation();
})
.on("mousemove", function(d) {
var datum, hh = tip[0][0].scrollHeight;
mousemove = { left: d3.event.pageX, top: d3.event.pageY};
scrolltop = document.body.scrollTop;
if (typeof d.label !== 'undefined') {
datum = { label: d.label, x: d.x, y: d.y };
} else {
datum = { x: d.x, y: d.y };
}
tip.datum(datum)
.text(tooltipFormatter)
.style('top', mousemove.top - scrolltop - hh/2 + 'px')
.style('left', mousemove.left + 20 + 'px')
.style('visibility', 'visible');
})
.on('click', function (d, i) {
dispatch.click({
value: yValue(d, i),
point: d,
pointIndex: i,
series: data.series,
config: args,
data: latestData,
e: d3.event
});
d3.event.stopPropagation();
})
.on("mouseout", function() {
var point = d3.select(this);
point.attr("opacity", 0);
tip.style('visibility', 'hidden');
});
if (addTooltip) {
// **** hilite series on hover
allLayers = vis.select('path');
//var allLayers = svg.selectAll('.rect');
lines.on(
'mouseover', function (d, i) {
// hilite chart layer
allLayers.style('opacity', 0.3);
var layerClass = '.' + d3.select(this).node().classList[2],
mylayer = vis.selectAll(layerClass).style('opacity', 1);
// stroke this rect
d3.select(this)
.classed('hover', true)
.style('stroke', '#333')
.style('cursor', 'pointer');
// hilite legend item
if (allItms) {
allItms.style('opacity', 0.3);
var itm = d3.select('.legendwrapper').select('li.legends' + layerClass).style('opacity', 1);
}
});
}
/* Event Selection: BRUSH */
brush = d3.svg.brush()
.x(xScale)
.on('brushend', function brushend() {
var selected, start, lastVal, end, selectedRange;
// selected is used to determine the range for ordinal scales
selected = xScale.domain().filter(function(d) {
return (brush.extent()[0] <= xScale(d)) && (xScale(d) <= brush.extent()[1]);
});
start = selected[0];
lastVal = selected.length - 1;
end = selected[lastVal];
selectedRange = [start, end];
return brush.extent()[0] instanceof Date ?
dispatch.brush({
range: brush.extent(),
config: args,
e: d3.event,
data: latestData
}) :
dispatch.brush({
range: selectedRange,
config: args,
e: d3.event,
data: latestData
});
});
if (dispatch.on('brush')) {
g.append('g')
.attr('class', 'brush')
.call(brush)
.selectAll('rect')
.attr('height', height);
}
/* ************************** */
lines.on("mouseout", function() {
allLayers.style('opacity', 1);
allItms.style('opacity', 1);
});
function X(d) { return xScale(d.x); }
function Y(d) { return yScale(d.y); }
return svg;
};
// getters / setters
chart.resize = _.debounce(function() {
if (latestData) {
chart.render(latestData);
}
}, 200);
// enable auto-resize
var prevSize;
(function checkSize() {
var size = $elem.width() + ':' + $elem.height();
if (prevSize !== size) {
chart.resize();
}
prevSize = size;
setTimeout(checkSize, 250);
}());
/* Function for truncating x axis tick labels */
chart.tickText = function (text, width) {
var n = text[0].length,
maxw = width / n * 0.9,
tickn = Math.floor(maxw);
text.each(function () {
var text = d3.select(this),
length = this.getComputedTextLength(),
tspan = text.text();
if ( length > maxw ) {
var str = text.text(),
avg = length / str.length,
end = Math.floor(maxw / avg);
str = str.substr(0, end) + '...';
tspan = text.text(str);
}
});
};
chart.margin = function(_) {
if (!arguments.length) { return margin; }
margin.top = typeof _.top !== 'undefined' ? _.top : margin.top;
margin.right = typeof _.right !== 'undefined' ? _.right : margin.right;
margin.bottom = typeof _.bottom !== 'undefined' ? _.bottom : margin.bottom;
margin.left = typeof _.left !== 'undefined' ? _.left : margin.left;
return chart;
};
chart.width = function(_) {
if (!arguments.length) { return width; }
width = _;
return chart;
};
chart.height = function(_) {
if (!arguments.length) { return height; }
height = _;
return chart;
};
chart.x = function(_) {
if (!arguments.length) { return xValue; }
xValue = _;
return chart;
};
chart.y = function(_) {
if (!arguments.length) { return yValue; }
yValue = _;
return chart;
};
chart.interpolate = function (_) {
if (!arguments.length) { return interpolate; }
interpolate = _;
return chart;
};
chart.error = function() {
// Removes the legend container
d3.select(elem).selectAll('*').remove();
var errorWrapper = d3.select(elem).append('div')
.attr('class', 'errorWrapper')
.style('height', function() { return $(elem).height() + 'px'; })
.style('text-align', 'center');
errorWrapper.append('p')
.style('font-size', '18px')
.style('margin-top', function() { return $(elem).height() / 3 + 'px'; })
.style('line-height', '18px')
.text('The container is too small for this chart.');
return chart;
};
chart.off = function(event) {
dispatch.on(event, null);
return chart;
};
chart.destroy = function(_) {
/*
Destroys all charts associated with the parent element
if the argument passed is true. By default the argument
is true.
*/
if (!arguments.length || _) {
destroyFlag = _ || true;
// Removing chart and all elements associated with it
d3.select(elem).selectAll('*').remove();
// Cleaning up event listeners
chart.off('click');
chart.off('hover');
chart.off('brush');
d3.select(window).on('resize', null);
}
destroyFlag = _;
return chart;
};
chart.dispatch = dispatch;
d3.rebind(chart, dispatch, 'on');
d3.select(window).on('resize', chart.resize);
return chart;
};
});

View file

@ -0,0 +1,717 @@
define(function(require) {
'use strict';
var d3 = require('lib/d3/d3'),
_ = require('lib/lodash/dist/lodash'),
selectionFn = require('src/utils/selection'),
zeroInjection = require('src/utils/zeroInjection'),
legendFn = require('src/modules/legend'),
getColor = require('src/utils/colorspace');
// Dynamically adds css file
require('lib/require-css/css!../css/k4.d3');
return function histogram(elem, args) {
if (typeof args === 'undefined') { args = {}; }
var chart = {},
/* ******* Error Handling ******** */
destroyFlag = false,
/* ***************************** */
/* ******* Chart options ******* */
shareYAxis = args.shareYAxis || false,
addLegend = args.addLegend || false,
addTooltip = args.addTooltip || false,
/* ***************************** */
/* ******** Chart methods ******* */
margin = args.margin || { top: 35, right: 15, bottom: 35, left: 60 },
offset = args.offset || 'zero',
//color = args.color ? d3.scale.linear().range(args.color) : d3.scale.ordinal().range(['#e24700', '#f9e593']),
dispatch = d3.dispatch('hover', 'click', 'mouseenter', 'mouseleave', 'mouseout', 'mouseover', 'brush'),
getY = function(d, i) { return d.y; },
getX = function(d, i) { return d.x; },
/* ***************************** */
colDomain,
/* Parameters defined within the chart.render function */
stack = d3.layout.stack().values(function(d) { return d.values; }),
$elem = $(elem), // cached jquery version of element
vis = d3.select(elem), allLayers,
globalYAxis, mousemove, scrolltop,
selection, elemWidth, elemHeight, width, height,
layers, n, yGroupMax, yStackMax, keys,
xScale, yScale, xAxis, yAxis,
layer, bars, legend, tip,
xAxisFormatter, yAxisFormatter, tooltipFormatter, dataLength, latestData, chartwrapper,
allItms = false;
/* ***************************** */
chart.getColors = function(selection) {
try {
if (!selection) { throw new Error("No valid selection"); }
var colorDomain = chart.getColorDomain(selection),
lengthOfColorDomain = colorDomain.length,
colorArray = getColor(lengthOfColorDomain),
colorDict;
colorDict = chart.getColorDict(colorDomain, colorArray);
return colorDict;
}
catch(error) {
console.group("chart.getColors: " + error);
}
};
chart.getColorDict = function(colorDomain, colorArray) {
try {
if (!colorDomain) { throw new Error("No valid colorDomain"); }
if (!colorArray) { throw new Error("No valid colorArray"); }
var colorDict = _.zipObject(colorDomain, colorArray);
return colorDict;
}
catch(error) {
console.group("chart.getColorDict" + error);
}
};
/* Color domain */
chart.getColorDomain = function(selection) {
try {
if (!selection) { throw new Error("No valid selection"); }
var items = [];
selection.each(function(d) {
d.series.forEach(function(label) {
if (label.label) { items.push(label.label); }
else { items.push(d.yAxisLabel); }
});
});
items = _.uniq(items);
return items;
}
catch(error) {
console.group("chart.getColorDomain: " + error);
}
};
chart.render = function render(data) {
// store a copy of the data sent to render, so that it can be resent with .resize()
latestData = data;
// removes elements to redraw the chart on subsequent calls
d3.select(elem).selectAll('*').remove();
if (destroyFlag || !elem) {
if (destroyFlag) { throw new Error('you destroyed this chart and then tried to use it again'); }
else { throw new Error('there is no element present'); }
}
// HTML div in which charts are contained
chartwrapper = d3.select(elem).append('div')
.attr('class', 'chartwrapper')
.style('height', $(elem).height() + 'px');
// Selection function - returns an array of DOM elements with data bound
try { selection = d3.selectAll(selectionFn(chartwrapper[0][0], latestData)); }
catch(error) { return; }
// get colDomain from chart obj
var colors = chart.getColors(selection);
// Chart options
if (addLegend) {
legend = legendFn(elem, colors, chart);
allItms = d3.select('.legendwrapper').selectAll('li.legends');
}
if (addTooltip) { tip = d3.select(elem).append('div').attr('class','k4tip'); }
try {
selection.each(function (d, i) {
var that = this;
chart.iterateSelection({
'data': d,
'index': i,
'this': that,
'colors': colors
});
});
} catch (err) {
if (err.message === 'chart too small') { chart.error(); }
console.group(err);
}
return chart;
};
/* Color domain */
chart.colorDomain = function(selection) {
var items = [];
selection.each(function(d) {
d.series.forEach(function(label) {
if (label.label) { items.push(label.label); }
else { items.push(d.yAxisLabel); }
});
});
items = _.uniq(items);
return items;
};
/* Function for global yAxis */
chart.globalYAxisFn = function(selection) {
var yArray = [];
selection.each(function(d) {
d = zeroInjection(d);
return d3.max(stack(d.series), function(layer) {
return d3.max(layer.values, function(d) {
if (offset === 'group') { yArray.push(d.y); }
else { yArray.push(d.y0 + d.y); }
});
});
});
return d3.max(yArray);
};
chart.iterateSelection = function(args) {
if (typeof args === 'undefined') { args = {}; }
var data = zeroInjection(args.data),
that = args.this,
colors = args.colors,
brush;
// Data formatters
xAxisFormatter = data.xAxisFormatter || function(v) { return v; };
yAxisFormatter = data.yAxisFormatter;
tooltipFormatter = data.tooltipFormatter;
// adds the label value to each data point
// within the values array for displaying in the tooltip
data.series.forEach(function(d) {
d.values.forEach(function(e) {
var label = d.label ? d.label : data.yAxisLabel;
e.label = label;
});
});
// width, height, margins
elemWidth = parseInt(d3.select(that).style('width'), 10);
elemHeight = parseInt(d3.select(that).style('height'), 10);
width = elemWidth - margin.left - margin.right; // width of the parent element ???
height = elemHeight - margin.top - margin.bottom; // height of the parent element ???
// preparing the data and scales
dataLength = data.series[0].values.length;
stack.offset(offset);
layers = stack(data.series);
n = layers.length; // number of layers
globalYAxis = chart.globalYAxisFn(selection);
yGroupMax = d3.max(layers, function(layer) {
return d3.max(layer.values, function(d) { return d.y; });
});
yStackMax = d3.max(layers, function(layer) {
return d3.max(layer.values, function(d) { return d.y0 + d.y; });
});
keys = d3.set(layers[0].values.map(function(d) { return d.x; })).values();
/* Error Handler that prevents a chart from being rendered when
there are too many data points for the width of the container. */
if (width/dataLength <= 4) { throw new Error('chart too small'); }
/* ************************** DATE FORMAT *************************************************** */
if ( data.ordered !== undefined && data.ordered.date !== undefined) {
var milsInterval = data.ordered.interval,
testInterval, dateoffset;
if (milsInterval < 2419200000) { testInterval = 'week'; dateoffset = (milsInterval / 604800000); }
if (milsInterval < 604800000) { testInterval = 'day'; dateoffset = (milsInterval / 86400000); }
if (milsInterval < 86400000) { testInterval = 'hour'; dateoffset = (milsInterval / 3600000); }
if (milsInterval < 3600000) { testInterval = 'minute'; dateoffset = (milsInterval / 60000); }
if (milsInterval < 60000) { testInterval = 'second'; dateoffset = (milsInterval / 1000); }
// apply interval to last date in keys
var maxIntervalOffset = d3.time[testInterval].offset( new Date(data.ordered.max), dateoffset );
var minIntervalOffset = d3.time[testInterval].offset( new Date(data.ordered.min), -dateoffset );
xScale = d3.time.scale().domain([minIntervalOffset, maxIntervalOffset]).range([0, width]);
} else {
xScale = d3.scale.ordinal().domain(keys).rangeRoundBands([0, width], 0.1);
}
/* ******************************************************************************************** */
yScale = d3.scale.linear().range([height, 0]);
var xTickScale = d3.scale.linear().clamp(true).domain([80, 300, 800]).range([0, 2, 4]);
var xTicks = Math.floor(xTickScale(width));
xAxis = d3.svg.axis()
.scale(xScale)
.ticks(xTicks)
.tickPadding(5)
.tickFormat(xAxisFormatter)
.orient('bottom');
// tickScale uses height to get tickN value for ticks() and nice()
var tickScale = d3.scale.linear().clamp(true).domain([20, 40, 1000]).range([0, 1, 10]),
tickN = Math.floor(tickScale(height));
yAxis = d3.svg.axis()
.scale(yScale)
.ticks(tickN)
.tickPadding(4)
.tickFormat(yAxisFormatter)
.orient('left');
// setting the y scale domain
if (shareYAxis) { yScale.domain([0, globalYAxis]).nice(tickN); }
else {
if (offset === 'group') { yScale.domain([0, yGroupMax]).nice(tickN); }
else { yScale.domain([0, yStackMax]).nice(tickN); }
}
// maps color domain to range
//color.domain([0, colDomain.length - 1]);
// canvas
var svg = d3.select(that).append('svg')
.attr('class', 'canvas')
.attr('width', '100%')
.attr('height', '100%')
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
// background rect
svg.append('rect')
.attr('class', 'chart-bkgd')
.attr('width', width)
.attr('height', height);
// x axis
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis)
.selectAll('text')
.call(chart.tickText, width)
.on('mouseover', function(d) {
var hh = tip[0][0].scrollHeight;
mousemove = { left: d3.event.pageX, top: d3.event.pageY};
scrolltop = document.body.scrollTop;
d3.select(that).style('cursor', 'default');
return tip.datum(d).text(d)
.style('top', mousemove.top - scrolltop - hh/2 + 'px')
.style('left', mousemove.left + 20 + 'px')
.style('visibility', 'visible');
})
.on('mouseout', function() {
d3.select(that).classed('hover', false).style('stroke', null);
tip.style('visibility', 'hidden');
});
// y axis
svg.append('g')
.attr('class', 'y axis')
.call(yAxis);
// Axis labels
svg.append('text')
.attr('class', 'x-axis-label')
.attr('text-anchor', 'middle')
.attr('x', width / 2)
.attr('y', height + 30)
.text(data.xAxisLabel);
svg.append('text')
.attr('class', 'y-axis-label')
.attr('text-anchor', 'middle')
.attr('x', -height / 2)
.attr('y', function() {
// get width of y axis group for label offset
var ww = svg.select('.y.axis').node().getBBox();
return -1 * ww.width - 14;
})
.attr('dy', '.75em')
.attr('transform', 'rotate(-90)')
.text(data.yAxisLabel);
// Chart title
svg.append('text')
.attr('class', 'charts-label')
.attr('text-anchor', 'middle')
.attr('x', width / 2)
.attr('y', -10)
.text(data.label)
.call(chart.tickText, width)
.on('mouseover', function(d) {
var hh = tip[0][0].scrollHeight;
mousemove = { left: d3.event.pageX, top: d3.event.pageY};
scrolltop = document.body.scrollTop;
d3.select(that).style('cursor', 'default');
return tip.text(d.label)
.style('top', mousemove.top - scrolltop - hh/2 + 'px')
.style('left', mousemove.left + 20 + 'px')
.style('visibility', 'visible');
})
.on('mouseout', function() {
d3.select(that).classed('hover', false).style('stroke', null);
tip.style('visibility', 'hidden');
});
/* Event Selection: BRUSH */
brush = d3.svg.brush()
.x(xScale)
.on('brushend', function brushend() {
var selected, start, lastVal, end, selectedRange;
// selected is used to determine the range for ordinal scales
selected = xScale.domain().filter(function(d) {
return (brush.extent()[0] <= xScale(d)) && (xScale(d) <= brush.extent()[1]);
});
start = selected[0];
lastVal = selected.length - 1;
end = selected[lastVal];
selectedRange = [start, end];
return brush.extent()[0] instanceof Date ?
dispatch.brush({
range: brush.extent(),
config: args,
e: d3.event,
data: latestData
}) :
dispatch.brush({
range: selectedRange,
config: args,
e: d3.event,
data: latestData
});
});
if (dispatch.on('brush')) {
svg.append('g')
.attr('class', 'brush')
.call(brush)
.selectAll('rect')
.attr('height', height);
}
/* ************************** */
// layers
layer = svg.selectAll('.layer')
.data(function (d) { return d.series; })
.enter().append('g')
.attr('class', function (d) {
if (!d.label) {
return colors[data.yAxisLabel];
} else {
return colors[d.label];
}
})
.style('fill', function (d) {
if (!d.label) {
return colors[data.yAxisLabel];
} else {
return colors[d.label];
}
});
bars = layer.selectAll('rect')
.data(function (d) { return d.values; });
// enter
bars.enter().append('rect')
.attr('class', function (d, i) {
// Regex to remove ., /, white space, *, ;, (, ), :, , from labels.
var label = d.label !== undefined ?
d.label.replace(/[.]+|[/]+|[\s]+|[*]+|[;]+|[(]+|[)]+|[:]+|[,]+/g, '') :
data.yAxisLabel.replace(/[.]+|[/]+|[\s]+|[*]+|[;]+|[(]+|[)]+|[:]+|[,]+/g, '');
return 'rl rl-' + label;
})
.on('mouseover', function(d, i) {
d3.select(this)
.classed('hover', true)
.style('stroke', '#333')
.style('cursor', 'pointer');
dispatch.hover({
value: getY(d, i),
point: d,
pointIndex: i,
series: data.series,
config: args,
data: latestData,
e: d3.event
});
d3.event.stopPropagation();
})
.on('click', function (d, i) {
dispatch.click({
value: getY(d, i),
point: d,
pointIndex: i,
series: data.series,
config: args,
data: latestData,
e: d3.event
});
d3.event.stopPropagation();
});
if (addTooltip) {
// **** hilite series on hover
allLayers = vis.selectAll('rect');
var itm, itmRect, ht, ot, legendwrap = d3.select('.legendwrapper');
//var allLayers = svg.selectAll('.rect');
bars.on('mouseover', function (d) {
// hilite chart layer
allLayers.style('opacity', 0.3);
var layerClass = '.rl-' + d.label.replace(/[.]+|[/]+|[\s]+|[*]+|[;]+|[(]+|[)]+|[:]+|[,]+/g, ''),
mylayer = vis.selectAll(layerClass).style('opacity', 1);
// stroke this rect
d3.select(this)
.classed('hover', true)
.style('stroke', '#333');
// .style('cursor', 'pointer');
// hilite legend item
if ( allItms ) {
allItms.style('opacity', 0.3);
var itm = d3.select('.legendwrapper')
.select(layerClass)
.style('opacity', 1);
}
// scroll legend
if ( chart.headerOpen === true ) {
ht = legendwrap.node().getBoundingClientRect().height;
if ( itm.node() ) {
ot = itm.node().offsetTop;
legendwrap.node().scrollTop = 35+ot-ht;
} else {
legendwrap.node().scrollTop = 0;
}
}
});
bars.on('mousemove', function(d) {
var datum, hh = tip[0][0].scrollHeight;
mousemove = { left: d3.event.pageX, top: d3.event.pageY};
scrolltop = document.body.scrollTop;
if (typeof d.label !== 'undefined') {
datum = { label: d.label, x: d.x, y: d.y };
} else {
datum = { x: d.x, y: d.y };
}
tip.datum(datum)
.text(tooltipFormatter)
.style('top', mousemove.top - scrolltop - hh/2 + 'px')
.style('left', mousemove.left + 20 + 'px')
.style('visibility', 'visible');
});
bars.on('mouseout', function() {
d3.select(this).classed('hover', false).style('stroke', null);
tip.style('visibility', 'hidden');
allLayers.style('opacity', 1);
allItms.style('opacity', 1);
});
}
// update
switch (offset) {
case 'group':
bars
// ****** DATE FORMAT *******
.attr('x', function (d, i, j) {
return data.ordered === undefined || !data.ordered.date ?
xScale(d.x) + xScale.rangeBand() / n * j :
xScale(d.x) - (width / (keys.length + 1) / 2);
})
.attr('width', function() {
return data.ordered === undefined || !data.ordered.date ?
xScale.rangeBand() / n : width / (keys.length + 1) / n;
})
.attr('y', function (d) { return yScale(d.y); })
.attr('height', function (d) { return height - yScale(d.y); });
break;
default:
bars
.attr('x', function (d) { return xScale(d.x); })
.attr('width', function() {
return data.ordered === undefined || !data.ordered.date ?
xScale.rangeBand() :
xScale(data.ordered.min + data.ordered.interval) - xScale(data.ordered.min) - 2;
})
.attr('y', function (d) { return yScale(d.y0 + d.y); })
.attr('height', function (d) { return yScale(d.y0) - yScale(d.y0 + d.y); });
break;
}
// exit
bars.exit().remove();
return svg;
};
/* Function for truncating x axis tick labels */
chart.tickText = function (text, width) {
var n = text[0].length,
maxw = width / n * 0.9,
tickn = Math.floor(maxw);
text.each(function () {
var text = d3.select(this),
length = this.getComputedTextLength(),
tspan = text.text();
if ( length > maxw ) {
var str = text.text(),
avg = length / str.length,
end = Math.floor(maxw / avg);
str = str.substr(0, end) + '...';
tspan = text.text(str);
}
});
};
// getters / setters
chart.resize = _.debounce(function() {
if (latestData) {
chart.render(latestData);
}
}, 200);
// enable auto-resize
var prevSize;
(function checkSize() {
var size = $elem.width() + ':' + $elem.height();
if (prevSize !== size) {
chart.resize();
}
prevSize = size;
setTimeout(checkSize, 250);
}());
chart.dispatch = dispatch;
chart.margin = function(_) {
if (!arguments.length) { return margin; }
margin.top = typeof _.top !== 'undefined' ? _.top : margin.top;
margin.right = typeof _.right !== 'undefined' ? _.right : margin.right;
margin.bottom = typeof _.bottom !== 'undefined' ? _.bottom : margin.bottom;
margin.left = typeof _.left !== 'undefined' ? _.left : margin.left;
return chart;
};
chart.y = function(_) {
if (!arguments.length) { return getY; }
getY = _;
return chart;
};
chart.x = function(_) {
if (!arguments.length) { return getX; }
getX = _;
return chart;
};
chart.offset = function(_) {
if (!arguments.length) { return offset; }
offset = _;
return chart;
};
chart.width = function(_) {
if (!arguments.length) { return elemWidth; }
elemWidth = _;
return chart;
};
chart.height = function(_) {
if (!arguments.length) { return elemHeight; }
elemHeight = _;
return chart;
};
//chart.color = function(_) {
// if (!arguments.length) { return color; }
// color = d3.scale.linear().range(_);
// return chart;
//};
chart.off = function(event) {
dispatch.on(event, null);
return chart;
};
chart.destroy = function(_) {
/*
Destroys all charts associated with the parent element
if the argument passed is true. By default the argument
is true.
*/
if (!arguments.length || _) {
destroyFlag = _ || true;
// Removing chart and all elements associated with it
d3.select(elem).selectAll('*').remove();
// Cleaning up event listeners
chart.off('click');
chart.off('hover');
chart.off('brush');
d3.select(window).on('resize', null);
}
destroyFlag = _;
return chart;
};
chart.error = function() {
// Removes the legend container
d3.select(elem).selectAll('*').remove();
var errorWrapper = d3.select(elem).append('div')
.attr('class', 'errorWrapper')
.style('height', function() { return $(elem).height() + 'px'; })
.style('text-align', 'center');
errorWrapper.append('p')
.style('font-size', '18px')
.style('margin-top', function() { return $(elem).height() / 3 + 'px'; })
.style('line-height', '18px')
.text('The container is too small for this chart.');
return chart;
};
d3.rebind(chart, dispatch, 'on');
d3.select(window).on('resize', chart.resize);
return chart;
};
});

View file

@ -0,0 +1,135 @@
define(function(require) {
'use strict';
var d3 = require('lib/d3/d3');
function legend (elem, colors, chart) {
var vis = d3.select(elem),
chartWrap = vis.select('.chartwrapper'),
legendWrap = vis.append('div').attr('class', 'legendwrapper'),
allLayers = chartWrap.selectAll('rect'),
header = legendWrap.append('div').attr('class', 'header'),
list,
itm,
allItms;
header
.append('div')
.attr('class', 'column-labels')
.html('<span class="btn btn-xs btn-default legend-toggle"><i class="fa fa-list-ul"></i></span>;');
list = legendWrap.append('ul')
.attr('class', 'legend-ul')
.selectAll('li')
.data(d3.keys(colors))
.enter().append('li')
.attr('class', function (d) {
var label = d !== undefined ?
d.replace(/[.]+|[/]+|[\s]+|[#]+|[*]+|[;]+|[(]+|[)]+|[:]+|[,]+/g, '') : undefined;
return 'legends rl rl-' + label;
})
.html(function(d) {
var str = '<span class="dots" style="background:' + colors[d] + '"></span>' + d + '';
return str;
});
allItms = vis.selectAll('li.legends').style('cursor', 'pointer');
list
.on('mouseover', function (d) {
// chart layer
// Regex to remove ., /, white space, *, ;, (, ), :, , from labels.
var label = d !== undefined ?
d.replace(/[.]+|[/]+|[\s]+|[#]+|[*]+|[;]+|[(]+|[)]+|[:]+|[,]+/g, '') : undefined,
layerClass = '.rl-' + label;
allLayers = vis.selectAll('.rl').style('opacity', 0.3);
vis.selectAll(layerClass).style('opacity', 1);
// legend list
allItms.style('opacity', 0.3);
itm = d3.select(this).style('opacity', 1);
})
.on('mouseout', function () {
allLayers = vis.selectAll('.rl').style('opacity', 1);
allItms.style('opacity', 1);
});
// toggle header
function closeheader(e) {
var vwidth = vis.style('width'),
legwidth = +vwidth.substr(0,vwidth.length-2);
chartWrap.style('width', function() {
return legwidth - 30 + 'px';
});
legendWrap
.classed('legend-open', false)
.style('width', '30px')
.style('height', '30px');
header
.select('.column-labels')
.html('<span class="btn btn-xs btn-default legend-toggle"><i class="fa fa-list-ul"></i></span>&nbsp;');
header
.select('.legend-toggle')
.on( 'click', function (e) {
var sel = d3.select(this)[0].parentNode;
toggleheader(e);
});
}
function openheader(e) {
var vheight = vis.style('height'),
vwidth = vis.style('width'),
legheight = +vheight.substr(0,vheight.length-2) - 68,
legwidth = +vwidth.substr(0,vwidth.length-2);
chartWrap.style('width', function() {
return legwidth - 180 + 'px';
});
legendWrap
.classed('legend-open', true)
.style('width', '170px')
.style('height', legheight + 'px');
header
.select('.column-labels')
.html('<span class="btn btn-xs legend-toggle btn-primary"><i class="fa fa-list-ul"></i></span>');
header
.select('.legend-toggle')
.on( 'click', function (e) {
var sel = d3.select(this)[0].parentNode;
toggleheader(e);
});
}
function toggleheader (e) {
if (chart.headerOpen === undefined || chart.headerOpen === false) {
chart.headerOpen = true;
openheader(e);
} else {
chart.headerOpen = false;
closeheader(e);
}
chart.resize();
}
// check for last state
if ( chart.headerOpen === true ) { openheader(); }
else { closeheader(); }
}
return function (elem, colors, chart) {
return legend (elem, colors, chart);
};
});

View file

@ -0,0 +1,770 @@
define(function(require) {
'use strict';
var d3 = require('lib/d3/d3'),
getSelection = require('src/utils/selection'),
getLegend = require('src/modules/legend'),
getColor = require('src/utils/colorspace');
return function getLineChart(elem, config) {
if (typeof config === 'undefined') { config = {}; }
var chart = {},
/* ***** Chart Options ***** */
addLegend = config.addLegend || false,
addTooltip = config.addTooltip || false,
shareYAxis = config.shareYAxis || false,
/* ************************* */
/* ***** Chart Flags ******* */
destroyFlag = false,
/* ************************* */
/* ***** Chart Globals ******* */
dispatch = d3.dispatch('hover', 'click', 'mouseenter', 'mouseleave', 'mouseout', 'mouseover', 'brush'),
$elem = $(elem), // cached jquery version of element
latestData,
prevSize,
xValue = function(d, i) { return d.x; },
yValue = function(d, i) { return d.y; };
/* ************************* */
/*
Renders the chart to the HTML element
*/
chart.render = function(data) {
try {
if (!data) { throw new Error("No valid data"); }
if (!elem) { throw new Error("No elem provided"); }
// store a copy of the data sent to render, so that it can be resent with .resize()
latestData = data;
// removes elements to redraw the chart on subsequent calls
d3.select(elem).selectAll('*').remove();
var chartWrapper = chart.getChartWrapper(elem)[0][0],
selection = chart.getSelection(chartWrapper, latestData);
return chart.getVisualization(selection);
}
catch(error) {
console.group("chart.render: " + error);
}
};
/*
Creates the d3 visualization
*/
chart.getVisualization = function(selection) {
try {
if (!selection) { throw new Error("No valid selection"); }
if (destroyFlag) { throw new Error('You destroyed the chart and tried to use it again'); }
var colors = chart.getColors(selection);
// Calculates the max Y axis value for all Charts
if (shareYAxis) { var yAxisMax = chart.getYAxisMax(selection); }
// Adds the legend
if (addLegend) { var legend = getLegend(elem, colors, chart); }
// Adds tooltips
if (addTooltip) { var tip = chart.getTooltip(elem); }
return selection.each(function(d, i) {
var that = this;
chart.createLineChart({
'data': d,
'index': i,
'this': that,
'colors': colors,
'tip': tip,
'yAxisMax': yAxisMax
});
});
}
catch(error) {
console.group("chart.getVisualization: " + error);
}
};
chart.getTooltip = function(elem) {
try {
if (!elem) { throw new Error("No valid elem"); }
var tooltipDiv;
tooltipDiv = d3.select(elem)
.append('div')
.attr('class', 'k4tip');
return tooltipDiv;
}
catch(error) {
console.group("chart.getTooltip: " + error);
}
};
chart.getChartWrapper = function(elem) {
try {
if (!elem) { throw new Error("No valid elem"); }
var chartWrapper = d3.select(elem).append('div');
chartWrapper
.attr('class', 'chartwrapper')
.style('height', $(elem).height() + 'px');
return chartWrapper;
}
catch(error) {
console.group("chart.getChartWrapper: " + error);
}
};
chart.getSelection = function(elem, data) {
try {
if (!elem) { throw new Error("No valid elem"); }
if (!data) { throw new Error("No valid data"); }
var selection = d3.selectAll(getSelection(elem, data));
return selection;
}
catch(error) {
console.group("chart.getSelection: " + error);
}
};
chart.getColors = function(selection) {
try {
if (!selection) { throw new Error("No valid selection"); }
var colorDomain = chart.getColorDomain(selection),
lengthOfColorDomain = colorDomain.length,
colorArray = getColor(lengthOfColorDomain),
colorDict;
colorDict = chart.getColorDict(colorDomain, colorArray);
return colorDict;
}
catch(error) {
console.group("chart.getColors: " + error);
}
};
chart.getColorDict = function(colorDomain, colorArray) {
try {
if (!colorDomain) { throw new Error("No valid colorDomain"); }
if (!colorArray) { throw new Error("No valid colorArray"); }
var colorDict;
colorDict = _.zipObject(colorDomain, colorArray);
return colorDict;
}
catch(error) {
console.group("chart.getColorDict" + error);
}
};
/* Color domain */
chart.getColorDomain = function(selection) {
try {
if (!selection) { throw new Error("No valid selection"); }
var items = [];
selection.each(function(d) {
d.series.forEach(function(label) {
if (label.label) { items.push(label.label); }
else { items.push(d.yAxisLabel); }
});
});
items = _.uniq(items);
return items;
}
catch(error) {
console.group("chart.getColorDomain: " + error);
}
};
/* Function for global yAxis */
chart.getYAxisMax = function(selection) {
try {
if (!selection) { throw new Error("No valid selection"); }
var yArray = [];
selection.each(function(d) {
return d3.max(d.series, function(layer) {
return d3.max(layer.values, function(d) {
yArray.push(d.y);
});
});
});
return d3.max(yArray);
}
catch(error) {
console.group("chart.getYAxisMax: " + error);
}
};
chart.getBounds = function(data) {
try {
if (!data) { throw new Error("No valid data"); }
var bounds = [];
data.series.map(function(series) {
series.values.map(function(d, i) {
bounds.push({
x: xValue.call(series, d, i),
y: yValue.call(series, d, i)
});
});
});
return bounds;
}
catch(error) {
console.group("chart.getBounds: " + error);
}
};
chart.createLineChart = function(args) {
try {
if (typeof args === 'undefined') { args = {}; }
var data = args.data,
that = args.this,
colors = args.colors,
tip = args.tip,
yAxisMax = args.yAxisMax,
xAxisLabel = data.xAxisLabel,
yAxisLabel = data.yAxisLabel,
chartLabel = data.label,
xAxisFormatter = data.xAxisFormatter,
yAxisFormatter = data.yAxisFormatter,
tooltipFormatter = data.tooltipFormatter;
var elemWidth = parseInt(d3.select(that).style('width'), 10),
elemHeight = parseInt(d3.select(that).style('height'), 10);
if (!elemWidth) { throw new Error("The visualization element has no width"); }
if (!elemHeight) { throw new Error("The visualization element has no height"); }
var margin = { top: 35, right: 15, bottom: 35, left: 50 },
width = elemWidth - margin.left - margin.right,
height = elemHeight - margin.top - margin.bottom;
var xTickScale = d3.scale.linear()
.clamp(true)
.domain([80, 300, 800])
.range([0, 2, 4]);
var yTickScale = d3.scale.linear()
.clamp(true)
.domain([20, 40, 1000])
.range([0, 1, 10]);
var xTickN = Math.floor(xTickScale(width)),
yTickN = Math.floor(yTickScale(height));
var xScale = d3.time.scale()
.range([0, width]);
var yScale = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(xScale)
.ticks(xTickN)
.tickPadding(5)
.tickFormat(xAxisFormatter)
.orient('bottom');
var yAxis = d3.svg.axis()
.scale(yScale)
.ticks(yTickN)
.tickPadding(4)
.tickFormat(yAxisFormatter)
.orient('left');
var interpolate = 'linear';
var line = d3.svg.line()
.interpolate(interpolate)
.x(X)
.y(Y);
var vis = d3.select(elem),
allLayers = vis.selectAll('path'),
allItms = d3.select('.legendwrapper').selectAll('li.legends'),
scrolltop = document.body.scrollTop,
mousemove;
/* *** Data Manipulation *** */
var seriesData = [];
data.series.map(function(series) {
seriesData.push(series);
});
// adds the label value to each data point
// within the values array for displaying in the tooltip
data.series.forEach(function(d) {
d.values.forEach(function(e) {
e.label = d.label;
});
});
xScale.domain(d3.extent(chart.getBounds(data), function(d) { return d.x; }));
// setting the y scale domain
if (shareYAxis) {
yScale
.domain([0, yAxisMax])
.nice(yTickN);
} else {
yScale
.domain([0, d3.max(chart.getBounds(data), function(d) { return d.y; })])
.nice(yTickN);
}
/* ************************** */
var svg = d3.select(that).append('svg')
.attr('class', 'canvas')
.attr('width', '100%')
.attr('height', '100%');
var g = svg.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
// background rect
g.append('rect')
.attr('class', 'chart-bkgd')
.attr('width', width)
.attr('height', height);
g.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis)
.selectAll('text')
.call(chart.tickText, width)
.on('mouseover', function(d) {
if (addTooltip) {
var hh = tip[0][0].scrollHeight;
mousemove = { left: d3.event.pageX, top: d3.event.pageY};
d3.select(that).style('cursor', 'default');
return tip.datum(d).text(d)
.style('top', mousemove.top - scrolltop - hh/2 + 'px')
.style('left', mousemove.left + 20 + 'px')
.style('visibility', 'visible');
}
})
.on('mouseout', function() {
d3.select(that).classed('hover', false).style('stroke', null);
if (addTooltip) { tip.style('visibility', 'hidden'); }
});
g.append('g')
.attr('class', 'y axis')
.call(yAxis);
// Axis labels
g.append('text')
.attr('class', 'x-axis-label')
.attr('text-anchor', 'middle')
.attr('x', width / 2)
.attr('y', height + 30)
.text(xAxisLabel);
g.append('text')
.attr('class', 'y-axis-label')
.attr('text-anchor', 'middle')
.attr('x', -height / 2)
.attr('y', -40)
.attr('dy', '.75em')
.attr('transform', 'rotate(-90)')
.text(yAxisLabel);
// Chart title
g.append('text')
.attr('class', 'charts-label')
.attr('text-anchor', 'middle')
.attr('x', width / 2)
.attr('y', -10)
.text(chartLabel)
.call(chart.tickText, width)
.on('mouseover', function(d) {
if (addTooltip) {
var hh = tip[0][0].scrollHeight;
mousemove = { left: d3.event.pageX, top: d3.event.pageY};
d3.select(that).style('cursor', 'default');
return tip.text(d.label)
.style('top', mousemove.top - scrolltop - hh/2 + 'px')
.style('left', mousemove.left + 20 + 'px')
.style('visibility', 'visible');
}
})
.on('mouseout', function() {
d3.select(that).classed('hover', false).style('stroke', null);
if (addTooltip) { tip.style('visibility', 'hidden'); }
});
var lines = g.selectAll('.lines')
.data(seriesData)
.enter().append('g')
.attr('class', 'lines');
lines.append('path')
.attr('class', function(d) { return 'rl rl-' + chart.getClassName(d.label, yAxisLabel); })
.attr('d', function(d) { return line(d.values); })
.style('fill', 'none')
.style('stroke', function(d) { return d.label ? colors[d.label] : colors[yAxisLabel]; })
.style('stroke-width', '3px');
var layer = g.selectAll('.layer')
.data(seriesData)
.enter().append('g')
.attr('class', function(d) { return 'rl rl-' + chart.getClassName(d.label, yAxisLabel); })
.attr('stroke', function(d) { return d.label ? colors[d.label] : colors[yAxisLabel]; });
var circle = layer.selectAll('.points')
.data(function(d) { return d.values; })
.enter().append('circle')
.attr('class', 'points')
.attr('cx', function(d) { return xScale(d.x); })
.attr('cy', function(d) { return yScale(d.y); })
.attr('r', 8);
circle
.attr('fill', '#ffffff')
.attr('stroke', function(d) { return d.label ? colors[d.label] : colors[yAxisLabel]; })
.attr('stroke-width', 3.5)
.attr('opacity', 0);
circle
.on('mouseover', function(d, i) {
var point = d3.select(this),
layerClass = '.rl-' + chart.getClassName(d.label, yAxisLabel);
point.attr('opacity', 1)
.classed('hover', true)
.style('cursor', 'pointer');
// highlight chart layer
allLayers = vis.selectAll('path');
allLayers.style('opacity', 0.3);
vis.selectAll(layerClass).style('opacity', 1);
// highlight legend item
if (allItms) {
allItms.style('opacity', 0.3);
var itm = d3.select('.legendwrapper')
.select(layerClass)
.style('opacity', 1);
}
dispatch.hover({
value: yValue(d, i),
point: d,
pointIndex: i,
series: data.series,
config: config,
data: latestData,
e: d3.event
});
d3.event.stopPropagation();
})
.on('mousemove', function(d) {
if (addTooltip) {
var hh = tip[0][0].scrollHeight,
datum;
mousemove = { left: d3.event.pageX, top: d3.event.pageY};
if (typeof d.label !== 'undefined') {
datum = { label: d.label, x: d.x, y: d.y };
} else {
datum = { x: d.x, y: d.y };
}
tip.datum(datum)
.text(tooltipFormatter)
.style('top', mousemove.top - scrolltop - hh/2 + 'px')
.style('left', mousemove.left + 20 + 'px')
.style('visibility', 'visible');
}
})
.on('click', function (d, i) {
dispatch.click({
value: yValue(d, i),
point: d,
pointIndex: i,
series: data.series,
config: config,
data: latestData,
e: d3.event
});
d3.event.stopPropagation();
})
.on('mouseout', function() {
var point = d3.select(this);
point.attr('opacity', 0);
if (addTooltip) { tip.style('visibility', 'hidden'); }
allLayers.style('opacity', 1);
allItms.style('opacity', 1);
});
if (addTooltip) {
// **** hilite series on hover
allLayers = vis.selectAll('path');
lines.on('mouseover', function (d) {
// highlight chart layer
allLayers.style('opacity', 0.3);
var layerClass = '.rl-' + chart.getClassName(d.label, yAxisLabel),
mylayer = vis.selectAll(layerClass).style('opacity', 1);
// stroke this rect
d3.select(this)
.classed('hover', true)
.style('stroke', '#333')
.style('cursor', 'pointer');
// hilite legend item
if (allItms) {
allItms.style('opacity', 0.3);
var itm = d3.select('.legendwrapper')
.select(layerClass)
.style('opacity', 1);
}
});
}
/* Event Selection: BRUSH */
var brush = d3.svg.brush()
.x(xScale)
.on('brushend', brushend);
if (dispatch.on('brush')) {
g.append('g')
.attr('class', 'brush')
.call(brush)
.selectAll('rect')
.attr('height', height);
}
/* ************************** */
lines.on('mouseout', function() {
allLayers.style('opacity', 1);
allItms.style('opacity', 1);
});
function X(d) { return xScale(d.x); }
function Y(d) { return yScale(d.y); }
function brushend() {
var selected,
start,
lastVal,
end,
selectedRange;
// selected is used to determine the range for ordinal scales
selected = xScale.domain().filter(function(d) {
return (brush.extent()[0] <= xScale(d)) && (xScale(d) <= brush.extent()[1]);
});
start = selected[0];
lastVal = selected.length - 1;
end = selected[lastVal];
selectedRange = [start, end];
return brush.extent()[0] instanceof Date ?
dispatch.brush({
range: brush.extent(),
config: config,
e: d3.event,
data: latestData
}) :
dispatch.brush({
range: selectedRange,
config: config,
e: d3.event,
data: latestData
});
}
return svg;
}
catch(error) {
console.group("chart.createLineChart: " + error);
}
};
// getters / setters
chart.resize = _.debounce(function () {
try {
if (!latestData) { throw new Error("No valid data"); }
chart.render(latestData);
}
catch(error) {
console.group("chart.resize: " + error);
}
}, 200);
// enable auto-resize
chart.checkSize = function checkSize() {
try {
var size = $elem.width() + ':' + $elem.height();
if (prevSize !== size) { chart.resize(); }
prevSize = size;
setTimeout(checkSize, 250);
}
catch (error) {
console.group("chart.checkSize: " + error);
}
};
/* Function for truncating x axis tick labels */
chart.tickText = function (text, width) {
try {
if (!text) { throw new Error("No text was given"); }
if (!width) { throw new Error("No width was given"); }
var n = text[0].length,
maxw = width / n * 0.9,
tickn = Math.floor(maxw);
text.each(function () {
var text = d3.select(this),
length = this.getComputedTextLength(),
tspan = text.text();
if ( length > maxw ) {
var str = text.text(),
avg = length / str.length,
end = Math.floor(maxw / avg);
str = str.substr(0, end) + '...';
tspan = text.text(str);
}
});
}
catch(error) {
console.group("chart.tickText: " + error);
}
};
chart.classifyString = function(string) {
try {
return string.replace(/[.]+|[/]+|[\s]+|[*]+|[;]+|[(]+|[)]+|[:]+|[,]+/g, '');
}
catch(error) {
console.group("chart.classifyString: " + error);
}
};
chart.getClassName = function(label, yAxisLabel) {
try {
return label ? chart.classifyString(label) : chart.classifyString(yAxisLabel);
}
catch(error) {
console.group("chart.getClassName: " + error);
}
};
chart.error = function() {
try {
// Removes the legend container
d3.select(elem).selectAll('*').remove();
var errorWrapper = d3.select(elem).append('div')
.attr('class', 'errorWrapper')
.style('height', function() { return $(elem).height() + 'px'; })
.style('text-align', 'center');
errorWrapper.append('p')
.style('font-size', '18px')
.style('margin-top', function() { return $(elem).height() / 3 + 'px'; })
.style('line-height', '18px')
.text('The container is too small for this chart.');
return chart;
}
catch(error) {
console.group("chart.error: " + error);
}
};
chart.off = function(event) {
try {
dispatch.on(event, null);
return chart;
}
catch(error) {
console.group("chart.off: " + error);
}
};
/*
* Destroys all charts associated with the parent element
* if the argument passed is true. By default the argument
* is true.
*/
chart.destroy = function(_) {
try {
if (!arguments.length || _) {
destroyFlag = _ || true;
// Removing chart and all elements associated with it
d3.select(elem).selectAll('*').remove();
// Cleaning up event listeners
chart.off('click');
chart.off('hover');
chart.off('brush');
d3.select(window).on('resize', null);
}
destroyFlag = _;
return chart;
}
catch(error) {
console.group("chart.destroy: " + error);
}
};
chart.dispatch = dispatch;
d3.rebind(chart, dispatch, 'on');
d3.select(window).on('resize', chart.resize);
chart.checkSize();
return chart;
};
});

View file

@ -0,0 +1,203 @@
/* Stylings that will blow your mind! */
.arc path {
stroke: #fff;
/* stroke-width: 3px; */
}
.arc path {
stroke: #fff;
/* stroke-width: 3px; */
}
div.col {
margin: 0;
padding: 0;
display: inline-block;
}
div.rows {
margin: 0;
padding: 0;
}
.row-labels, .column-labels {
margin: 0;
padding: 10;
}
visualize {
margin:0;
padding:0;
}
.chartwrapper {
margin: 0;
padding: 0;
overflow: hidden;
}
/* legends */
.legendwrapper {
margin: 0;
padding: 0;
/*border: 1px solid #ddd;*/
position: absolute;
float: right;
width: 25px;
height: 25px;
top: 34px;
right: 10px;
z-index: 10;
overflow: hidden;
}
.header {
width: 100%;
height: 26px;
margin: 0 0 6px 0;
}
.legend-ul {
list-style: none;
margin: 0;
padding: 0;
width: 150px;
}
.legend-ul li {
display: block;
float: left;
width: 150px;
min-height: 22px;
margin: 0 18px 0 18px;
padding: 4px 0 4px 0;
text-align: left;
word-wrap: break-word;
font-size: 12px;
line-height: 13px;
list-style: none;
color: #666;
}
.dots {
width: 10px;
height: 10px;
border-radius: 50%;
display: block;
float: left;
margin: 2px 0 0 -16px;
padding: 0;
}
.legend-toggle {
position: relative;
float: right;
right: 2px;
top: 1px;
}
.legend-open {
overflow: auto;
}
.column-labels {
color: #777;
font-size: 10pt;
font-weight: normal;
display: block;
margin: 0;
padding: 0;
padding-left: 1.0em;
line-height: 2.0em;
}
/* histogram axis and label styles */
.vis-canvas {
/* background-color: #fff; */
}
.chart-bkgd {
fill: #ffffff;
/*fill: #eff3f4;*/
/*stroke: #ddd;*/
/*shape-rendering: crispEdges;*/
}
/*.rows-label, .columns-label {
font-size: 10pt;
fill: #46525d;
text-align: center;
line-height: 1.5em;
}*/
p.rows-label, p.columns-label {
display: block;
background: #eff3f4;
padding: 0;
margin: 0;
font-size: 9pt;
fill: #46525d;
text-align: center;
line-height: 1.9em;
}
.charts-label {
font-size: 8pt;
fill: #848e96;
}
.x-axis-label, .y-axis-label {
font-size: 7pt;
fill: #848e96;
}
.tick text {
font-size: 7pt;
fill: #848e96;
/*cursor: pointer;*/
}
.axis {
shape-rendering: crispEdges;
stroke-width: 1px;
}
.axis line, .axis path {
stroke: #ddd;
fill: none;
}
.legend-box {
fill: #ffffff;
}
.brush .extent {
stroke: #fff;
fill-opacity: .125;
shape-rendering: crispEdges;
}
.layer .rect:hover {
stroke: 3px;
}
.k4tip {
line-height: 1;
font-size: 12px;
font-weight: normal;
padding: 8px;
background: rgba(70, 82, 93, 0.95);
color: #fff;
border-radius: 4px;
position: fixed;
z-index: 20;
visibility: hidden;
}
.rect {
/*shape-rendering: crispEdges;*/
stroke: transparent;
stroke-width: 2;
}
.rect.hover {
/*shape-rendering: crispEdges;*/
stroke: #333;
}

View file

@ -0,0 +1,45 @@
define(function (require) {
'use strict';
var _ = require('lodash'),
d3 = require('d3');
/* Returns an array of usedColors. The length of usedColors
is trimmed or extended with generated colors to match length of colDom.
*/
// 72 seed colors
var seedColors = [
'#57c17b','#006e8a','#6f87d8','#663db8','#bc52bc','#9e3533','#daa05d','#967b17',
'#a0caae','#73a4b0','#acb5d8','#9b8bbb','#c19fc1','#b88484','#e0cbb2','#bfb282',
'#336c46','#00455c','#394e93','#422c6d','#783678','#6a2424','#936734','#60521f',
'#b3d5bf','#85adb7','#bdc5e0','#aa9dc3','#c9acc9','#c39898','#e8d7c5','#cbc09a',
'#2c593b','#003252','#34457f','#352456','#673267','#591d1d','#7f592f','#443b17',
'#c7e0cf','#a8c5cc','#d2d7ea','#c2b9d4','#dbc7db','#d5b9b9','#f2e9de','#d9d1b5',
'#254b32','#002c47','#2b3969','#30214f','#562956','#491818','#654625','#393114',
'#dbebe0','#bcd2d7','#d0d6eb','#cdc4de','#e8d9e8','#e0cccc','#f5eee5','#e4dec9',
'#20412b','#001f33','#232f57','#281b41','#482348','#3e1414','#563c20','#2e2810'
],
usedColors = [];
return function (colDom) {
// check if more colors needed
var dif = colDom - seedColors.length;
if ( dif > 0 ) {
// generate more colors
usedColors = _.clone(seedColors);
for (var newcol, i = 0; i < dif; i++) {
newcol = d3.rgb( usedColors[i] ).darker(1.3).toString();
usedColors.push(newcol);
}
} else {
// trim to length of colDomain labels
usedColors = _.first(seedColors, colDom);
}
return usedColors;
};
});

View file

@ -0,0 +1,84 @@
define(function(require) {
'use strict';
var d3 = require('lib/d3/d3');
// Dynamically adds css file
require('lib/require-css/css!../css/k4.d3');
// adds an array to another array
function addTo(to, array) {
[].push.apply(to, array);
}
/*
Accepts a DOM element(s) and data.
Returns an array of DOM elements on which charts
will be rendered.
*/
function placeChart(elem, data) {
var $el = elem instanceof Array ? elem : d3.select(elem).datum(data),
charts = [];
if (data.rows) { addTo(charts, split($el, 'height', 'width', data.rows, 'rows')); }
else if (data.columns) { addTo(charts, split($el, 'width', 'height', data.columns, 'columns')); }
else {
if (!data.series || data.series.length === 0) { throw new Error('No valid data'); }
addTo(charts, $el.append('div')
.attr('class', 'chart')
.style('width', '100%')
.style('height', '100%')[0]);
}
return charts;
}
/*
Accepts a DOM element(s), 'width' and 'height', data and class name.
Returns a DOM element array that has been split by class name,
i.e. rows or columns.
*/
function split (elem, by, inherit, data, name) {
var charts = [],
$el = elem instanceof Array ? elem : d3.select(elem),
node = elem instanceof Array ? $(elem[0]) : $(elem),
// need to refactor
size = ($(node).parent()[by]() / data.length) / $(node).parent()[by]() * 100,
inheritedSize = node[inherit]() / node[inherit]() * 100;
if (!size || !inheritedSize || size === 0 || inheritedSize === 0) {
if (!size || size === 0) {
throw new Error('Chart cannot be rendered because ' + by + ' is ' + size + '.');
} else {
throw new Error('Chart cannot be rendered because ' + inherit + ' is ' + inheritedSize + '.');
}
}
$el.selectAll('div')
.data(data)
.enter().append('div')
.attr('class', name)
.style('width', function() { return by === 'width' ? size + '%': inheritedSize + '%'; })
.style('height', function() { return by === 'height' ? size + '%' : inheritedSize + '%'; })
.style('display', function() { return name === 'rows' ? 'block' : 'inline-block'; })
.each(function(d) {
var selection = d3.select(this);
if (!d.series) {
selection.append('div')
.attr('class', name + '-label')
.attr('height', '10%')
.text(d.label);
}
addTo(charts, placeChart(selection, d));
});
return charts;
}
return function(elem, data) {
return placeChart(elem, data);
};
});

View file

@ -0,0 +1,73 @@
define(function(require) {
'use strict';
var _ = require('lib/lodash/dist/lodash');
/* Returns an array of ordered keys for a data series
* or several arrays of data values.
*/
function orderKeys(data) {
var uniqueXObjs = {}, orderedKeys;
data.forEach(function (series) {
series.values.forEach(function (d, i) {
var key = d.x;
uniqueXObjs[key] = uniqueXObjs[key] === void 0 ? i : Math.max(i, uniqueXObjs[key]);
});
});
orderedKeys = _.chain(uniqueXObjs).pairs().sortBy(1).pluck(0).value();
return orderedKeys;
}
/* Returns the indexed position of a value in an array. */
function getIndexOf(val, arr) {
var i, max = arr.length;
for (i = 0; i < max; i++) {
if (val == arr[i].x) {
return i;
}
}
return -1;
}
function createZeroFilledArray(orderedKeys) {
var max = orderedKeys.length,
i, arr = [];
for (i = 0; i < max; i++) {
var val = orderedKeys[i];
arr.push({ x: val, y: 0});
}
return arr;
}
function modifyZeroFilledArray(zeroArray, dataArray) {
var i, max = dataArray.length;
for (i = 0; i < max; i++) {
var val = dataArray[i],
index = getIndexOf(val.x, zeroArray);
zeroArray.splice(index, 1);
zeroArray.splice(index, 0, val);
}
return zeroArray;
}
return function (data) {
var i, max = data.series.length,
orderedKeys = orderKeys(data.series);
for (i = 0; i < max; i ++) {
var zeroArray = createZeroFilledArray(orderedKeys),
dataArray = data.series[i].values;
data.series[i].values = modifyZeroFilledArray(zeroArray, dataArray);
}
return data;
};
});

View file

@ -45,7 +45,6 @@ require.config({
'elasticsearch': ['angular'],
'angular-bootstrap': ['angular'],
'angular-bindonce': ['angular'],
k4d3: ['jquery', 'lodash'],
'angular-ui-ace': ['angular', 'ace'],
inflection: {
exports: 'inflection'

View file

@ -1,5 +1,5 @@
module.exports = function (grunt) {
var notIncludedComponents = '{K4D3,font-awesome,requirejs}';
var notIncludedComponents = '{font-awesome,requirejs}';
return {
build: '<%= build %>',
target: '<%= target %>',
@ -12,14 +12,9 @@ module.exports = function (grunt) {
// remove the contents of K4D3, font-awesome, and requirejs except necessary files
'<%= build %>/bower_components/' + notIncludedComponents + '/*',
'!<%= build %>/bower_components/K4D3/build',
'!<%= build %>/bower_components/requirejs/require.js',
'!<%= build %>/bower_components/font-awesome/fonts',
// remove extra builds from K4D3
'<%= build %>/bower_components/K4D3/build/*',
'!<%= build %>/bower_components/K4D3/build/k4.d3.js',
'<%= build %>/**/_empty_',
'<%= build %>/**/*.less',
'<%= appBuild %>/{css-builder,normalize}.js',

View file

@ -10,7 +10,6 @@ module.exports = function (grunt) {
name: 'kibana',
excludeShallow: [
'../config',
'k4d3'
],
include: [
'controllers/kibana'

View file

@ -3,7 +3,7 @@ module.exports = function (grunt) {
var spawn = require('./utils/spawn');
var installOrUpdateRepo = require('./utils/install_or_update_repo');
// bower update elasticsearch && bower update k4d3 && npm run rebuild-esjs"
// bower update elasticsearch && npm run rebuild-esjs"
grunt.registerTask('update', [
'update-esjs',
]);