diff --git a/.gitignore b/.gitignore index 92f3e46fb40a..d8a90ba458ed 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ .DS_Store node_modules trash -src/bower_components/K4D3 src/bower_components/elasticsearch build -target \ No newline at end of file +target diff --git a/src/kibana/apps/discover/directives/timechart.js b/src/kibana/apps/discover/directives/timechart.js index b96632b259ad..562a55f36712 100644 --- a/src/kibana/apps/discover/directives/timechart.js +++ b/src/kibana/apps/discover/directives/timechart.js @@ -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], { }); diff --git a/src/kibana/apps/visualize/directives/visualize.js b/src/kibana/apps/visualize/directives/visualize.js index d51d17448b5a..0e4742b43b24 100644 --- a/src/kibana/apps/visualize/directives/visualize.js +++ b/src/kibana/apps/visualize/directives/visualize.js @@ -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. diff --git a/src/kibana/components/K4D3 b/src/kibana/components/K4D3 deleted file mode 160000 index 65ca81b5f0f4..000000000000 --- a/src/kibana/components/K4D3 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 65ca81b5f0f4a905918d0a96a9ec554578231c52 diff --git a/src/kibana/components/vislib/core.js b/src/kibana/components/vislib/core.js new file mode 100644 index 000000000000..89482086c476 --- /dev/null +++ b/src/kibana/components/vislib/core.js @@ -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; + }; +}); diff --git a/src/kibana/components/vislib/index.js b/src/kibana/components/vislib/index.js new file mode 100644 index 000000000000..1565409dcaca --- /dev/null +++ b/src/kibana/components/vislib/index.js @@ -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; +}); diff --git a/src/kibana/components/vislib/modules/areaChart.js b/src/kibana/components/vislib/modules/areaChart.js new file mode 100644 index 000000000000..5f753508d252 --- /dev/null +++ b/src/kibana/components/vislib/modules/areaChart.js @@ -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; + }; +}); diff --git a/src/kibana/components/vislib/modules/histogram.js b/src/kibana/components/vislib/modules/histogram.js new file mode 100644 index 000000000000..c2520d79500b --- /dev/null +++ b/src/kibana/components/vislib/modules/histogram.js @@ -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; + }; +}); diff --git a/src/kibana/components/vislib/modules/legend.js b/src/kibana/components/vislib/modules/legend.js new file mode 100644 index 000000000000..977e559d02d1 --- /dev/null +++ b/src/kibana/components/vislib/modules/legend.js @@ -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(';'); + + 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 = '' + 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(' '); + + 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(''); + + 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); + }; +}); \ No newline at end of file diff --git a/src/kibana/components/vislib/modules/lineChart.js b/src/kibana/components/vislib/modules/lineChart.js new file mode 100644 index 000000000000..9291429c7153 --- /dev/null +++ b/src/kibana/components/vislib/modules/lineChart.js @@ -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; + }; +}); \ No newline at end of file diff --git a/src/kibana/components/vislib/styles/k4.d3.css b/src/kibana/components/vislib/styles/k4.d3.css new file mode 100644 index 000000000000..e8b9d914b91c --- /dev/null +++ b/src/kibana/components/vislib/styles/k4.d3.css @@ -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; +} diff --git a/src/kibana/components/vislib/utils/colorspace.js b/src/kibana/components/vislib/utils/colorspace.js new file mode 100644 index 000000000000..18d247e90fa2 --- /dev/null +++ b/src/kibana/components/vislib/utils/colorspace.js @@ -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; + }; + +}); \ No newline at end of file diff --git a/src/kibana/components/vislib/utils/selection.js b/src/kibana/components/vislib/utils/selection.js new file mode 100644 index 000000000000..86dabb03afd6 --- /dev/null +++ b/src/kibana/components/vislib/utils/selection.js @@ -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); + }; +}); diff --git a/src/kibana/components/vislib/utils/zeroInjection.js b/src/kibana/components/vislib/utils/zeroInjection.js new file mode 100644 index 000000000000..7956be48f5f9 --- /dev/null +++ b/src/kibana/components/vislib/utils/zeroInjection.js @@ -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; + }; +}); \ No newline at end of file diff --git a/src/kibana/require.config.js b/src/kibana/require.config.js index e95a5506f76a..4a20e1ca0375 100644 --- a/src/kibana/require.config.js +++ b/src/kibana/require.config.js @@ -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' diff --git a/tasks/config/clean.js b/tasks/config/clean.js index fad9ea446dab..da58c836a433 100644 --- a/tasks/config/clean.js +++ b/tasks/config/clean.js @@ -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', diff --git a/tasks/config/requirejs.js b/tasks/config/requirejs.js index f697ccfabf2a..93cd92dda941 100644 --- a/tasks/config/requirejs.js +++ b/tasks/config/requirejs.js @@ -10,7 +10,6 @@ module.exports = function (grunt) { name: 'kibana', excludeShallow: [ '../config', - 'k4d3' ], include: [ 'controllers/kibana' diff --git a/tasks/update.js b/tasks/update.js index 7bebc7013894..e2ec16560755 100644 --- a/tasks/update.js +++ b/tasks/update.js @@ -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', ]);