From 67d5a4577ff0e78e74577b01d8c16803edff1b94 Mon Sep 17 00:00:00 2001 From: Zachary Tong Date: Thu, 18 Apr 2013 13:54:24 -0400 Subject: [PATCH 1/4] Update spacing --- panels/parallelcoordinates/module.js | 772 +++++++++++++-------------- 1 file changed, 386 insertions(+), 386 deletions(-) diff --git a/panels/parallelcoordinates/module.js b/panels/parallelcoordinates/module.js index 54d539ca6fe..1ac0228ce02 100644 --- a/panels/parallelcoordinates/module.js +++ b/panels/parallelcoordinates/module.js @@ -1,443 +1,443 @@ angular.module('kibana.parallelcoordinates', []) - .controller('parallelcoordinates', function ($scope, eventBus) { - - - $scope.activeDocs = []; - - // Set and populate defaults - var _d = { - query : "*", - size : 100, // Per page - pages : 5, // Pages available - offset : 0, - sort : ['@timestamp','desc'], - group : "default", - style : {'font-size': '9pt'}, - fields : [], - sortable: true, - spyable: true + .controller('parallelcoordinates', function ($scope, eventBus) { + + + $scope.activeDocs = []; + + // Set and populate defaults + var _d = { + query : "*", + size : 100, // Per page + pages : 5, // Pages available + offset : 0, + sort : ['@timestamp','desc'], + group : "default", + style : {'font-size': '9pt'}, + fields : [], + sortable: true, + spyable: true + } + + _.defaults($scope.panel, _d) + + $scope.init = function () { + + $scope.set_listeners($scope.panel.group); + // Now that we're all setup, request the time from our group + eventBus.broadcast($scope.$id,$scope.panel.group,"get_time") + + //and get the currently selected fields + eventBus.broadcast($scope.$id,$scope.panel.group,"get_fields") + }; + + $scope.set_listeners = function(group) { + eventBus.register($scope,'time',function(event,time) { + $scope.panel.offset = 0; + set_time(time) + }); + eventBus.register($scope,'query',function(event,query) { + $scope.panel.offset = 0; + $scope.panel.query = _.isArray(query) ? query[0] : query; + $scope.get_data(); + }); + eventBus.register($scope,'sort', function(event,sort){ + $scope.panel.sort = _.clone(sort); + $scope.get_data(); + }); + eventBus.register($scope,'selected_fields', function(event, fields) { + $scope.panel.fields = _.clone(fields) + $scope.$emit('render'); + }); + }; + + + $scope.get_data = function (segment,query_id) { + + // Make sure we have everything for the request to complete + if (_.isUndefined($scope.panel.index) || _.isUndefined($scope.time)) + return; + + var _segment = _.isUndefined(segment) ? 0 : segment + $scope.segment = _segment; + + $scope.panel.loading = true; + var request = $scope.ejs.Request().indices($scope.panel.index[_segment]) + .query(ejs.FilteredQuery( + ejs.QueryStringQuery($scope.panel.query || '*'), + ejs.RangeFilter($scope.time.field) + .from($scope.time.from) + .to($scope.time.to) + ) + ) + .size($scope.panel.size*$scope.panel.pages) + .sort($scope.panel.sort[0],$scope.panel.sort[1]); + + $scope.populate_modal(request); + + var results = request.doSearch(); + + + // Populate scope when we have results + results.then(function (results) { + $scope.panel.loading = false; + if(_segment === 0) { + $scope.hits = 0; + $scope.data = []; + query_id = $scope.query_id = new Date().getTime() } - _.defaults($scope.panel, _d) + // Check for error and abort if found + if(!(_.isUndefined(results.error))) { + $scope.panel.error = $scope.parse_error(results.error); + return; + } - $scope.init = function () { + // Check that we're still on the same query, if not stop + if($scope.query_id === query_id) { + $scope.data= $scope.data.concat(_.map(results.hits.hits, function(hit) { + return flatten_json(hit['_source']); + })); - $scope.set_listeners($scope.panel.group); - // Now that we're all setup, request the time from our group - eventBus.broadcast($scope.$id,$scope.panel.group,"get_time") + $scope.hits += results.hits.total; - //and get the currently selected fields - eventBus.broadcast($scope.$id,$scope.panel.group,"get_fields") - }; + // Sort the data + $scope.data = _.sortBy($scope.data, function(v){ + return v[$scope.panel.sort[0]] + }); - $scope.set_listeners = function(group) { - eventBus.register($scope,'time',function(event,time) { - $scope.panel.offset = 0; - set_time(time) - }); - eventBus.register($scope,'query',function(event,query) { - $scope.panel.offset = 0; - $scope.panel.query = _.isArray(query) ? query[0] : query; - $scope.get_data(); - }); - eventBus.register($scope,'sort', function(event,sort){ - $scope.panel.sort = _.clone(sort); - $scope.get_data(); - }); - eventBus.register($scope,'selected_fields', function(event, fields) { - $scope.panel.fields = _.clone(fields) - $scope.$emit('render'); - }); + // Reverse if needed + if($scope.panel.sort[1] == 'desc') + $scope.data.reverse(); + + // Keep only what we need for the set + $scope.data = $scope.data.slice(0,$scope.panel.size * $scope.panel.pages) + + } else { + return; + } + $scope.$emit('render') + }); + + + + }; + + // I really don't like this function, too much dom manip. Break out into directive? + $scope.populate_modal = function (request) { + $scope.modal = { + title: "Inspector", + body: "
Last Elasticsearch Query
" + 'curl -XGET ' + config.elasticsearch + '/' + $scope.panel.index + "/_search?pretty -d'\n" + angular.toJson(JSON.parse(request.toString()), true) + "'
" + } + }; + + function set_time(time) { + $scope.time = time; + $scope.panel.index = _.isUndefined(time.index) ? $scope.panel.index : time.index + $scope.get_data(); + } + + + $scope.$watch('activeDocs', function(v) { + eventBus.broadcast($scope.$id,$scope.panel.group,"table_documents", + {query:$scope.panel.query,docs:$scope.activeDocs}); + }); + + }) + .directive('parallelcoordinates', function () { + return { + restrict: 'A', + link: function (scope, elem, attrs) { + + scope.initializing = false; + + + /** + * Initialize the panels if new, or render existing panels + */ + scope.init_or_render = function() { + if (typeof scope.svg === 'undefined') { + + //prevent duplicate initialization steps, if render is called again + //before the svg is setup + if (!scope.initializing) { + init_panel(); + } + } else { + render_panel(); + } }; - $scope.get_data = function (segment,query_id) { + /** + * Receive render events + */ + scope.$on('render', function () { + scope.init_or_render(); + }); - // Make sure we have everything for the request to complete - if (_.isUndefined($scope.panel.index) || _.isUndefined($scope.time)) - return; + /** + * On window resize, re-render the panel + */ + angular.element(window).bind('resize', function () { + scope.init_or_render(); + }); - var _segment = _.isUndefined(segment) ? 0 : segment - $scope.segment = _segment; - $scope.panel.loading = true; - var request = $scope.ejs.Request().indices($scope.panel.index[_segment]) - .query(ejs.FilteredQuery( - ejs.QueryStringQuery($scope.panel.query || '*'), - ejs.RangeFilter($scope.time.field) - .from($scope.time.from) - .to($scope.time.to) - ) - ) - .size($scope.panel.size*$scope.panel.pages) - .sort($scope.panel.sort[0],$scope.panel.sort[1]); + /** + * Load the various panel-specific scripts then initialize + * the svg and set appropriate D3 settings + */ + function init_panel() { - $scope.populate_modal(request); + scope.m = [80, 100, 80, 100]; + scope.w = $(elem[0]).width() - scope.m[1] - scope.m[3]; + scope.h = $(elem[0]).height() - scope.m[0] - scope.m[2]; - var results = request.doSearch(); + scope.initializing = true; + // Using LABjs, wait until all scripts are loaded before rendering panel + var scripts = $LAB.script("common/lib/d3.v3.min.js?rand="+Math.floor(Math.random()*10000)); - // Populate scope when we have results - results.then(function (results) { - $scope.panel.loading = false; - if(_segment === 0) { - $scope.hits = 0; - $scope.data = []; - query_id = $scope.query_id = new Date().getTime() - } + scripts.wait(function () { - // Check for error and abort if found - if(!(_.isUndefined(results.error))) { - $scope.panel.error = $scope.parse_error(results.error); - return; - } - // Check that we're still on the same query, if not stop - if($scope.query_id === query_id) { - $scope.data= $scope.data.concat(_.map(results.hits.hits, function(hit) { - return flatten_json(hit['_source']); - })); + scope.x = d3.scale.ordinal().domain(scope.panel.fields).rangePoints([0, scope.w]); + scope.y = {}; - $scope.hits += results.hits.total; + scope.line = d3.svg.line().interpolate('cardinal'); + scope.axis = d3.svg.axis().orient("left"); - // Sort the data - $scope.data = _.sortBy($scope.data, function(v){ - return v[$scope.panel.sort[0]] - }); + scope.svg = d3.select(elem[0]).append("svg") + .attr("width", "100%") + .attr("height", "100%") + .attr("viewbox", "0 0 " + (scope.w + scope.m[1] + scope.m[3]) + " " + (scope.h + scope.m[0] + scope.m[2])) + .append("svg:g") + .attr("transform", "translate(" + scope.m[3] + "," + scope.m[0] + ")"); - // Reverse if needed - if($scope.panel.sort[1] == 'desc') - $scope.data.reverse(); + // Add foreground lines. + scope.foreground = scope.svg.append("svg:g") + .attr("class", "foreground"); - // Keep only what we need for the set - $scope.data = $scope.data.slice(0,$scope.panel.size * $scope.panel.pages) + scope.initializing = false; + render_panel(); + }); - } else { - return; - } - $scope.$emit('render') - }); + } + // Returns the path for a given data point. + function path(d) { + return scope.line(scope.panel.fields.map(function(p) { return [scope.x(p), scope.y[p](d[p])]; })); + } - }; + // Handles a brush event, toggling the display of foreground lines. + function brush() { + var actives = scope.panel.fields.filter(function(p) { return !scope.y[p].brush.empty(); }), + extents = actives.map(function(p) { return scope.y[p].brush.extent(); }); - // I really don't like this function, too much dom manip. Break out into directive? - $scope.populate_modal = function (request) { - $scope.modal = { - title: "Inspector", - body: "
Last Elasticsearch Query
" + 'curl -XGET ' + config.elasticsearch + '/' + $scope.panel.index + "/_search?pretty -d'\n" + angular.toJson(JSON.parse(request.toString()), true) + "'
" - } - }; + //.fade class hides the "inactive" lines, helps speed up rendering significantly + scope.foregroundLines.classed("fade", function(d) { + return !actives.every(function(p, i) { + var inside = extents[i][0] <= d[p] && d[p] <= extents[i][1]; + return inside; + }); + }); + + //activeDocs contains the actual doc records for selected lines. + //will be broadcast out to the table + var activeDocs = _.filter(scope.data, function(v) { + return actives.every(function(p,i) { + var inside = extents[i][0] <= v[p] && v[p] <= extents[i][1]; + return inside; + }); + }) - function set_time(time) { - $scope.time = time; - $scope.panel.index = _.isUndefined(time.index) ? $scope.panel.index : time.index - $scope.get_data(); + scope.$apply(function() { + scope.activeDocs = activeDocs; + }); } - $scope.$watch('activeDocs', function(v) { - eventBus.broadcast($scope.$id,$scope.panel.group,"table_documents", - {query:$scope.panel.query,docs:$scope.activeDocs}); - }); + //Drag functions are used for dragging the axis aroud + function dragstart(d) { + scope.i = scope.panel.fields.indexOf(d); + } - }) - .directive('parallelcoordinates', function () { - return { - restrict: 'A', - link: function (scope, elem, attrs) { + function drag(d) { + scope.x.range()[scope.i] = d3.event.x; + scope.panel.fields.sort(function(a, b) { return scope.x(a) - scope.x(b); }); + scope.foregroundLines.attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); + scope.traits.attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); + scope.brushes.attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); + scope.axisLines.attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); + scope.foregroundLines.attr("d", path); + } - scope.initializing = false; - - - /** - * Initialize the panels if new, or render existing panels - */ - scope.init_or_render = function() { - if (typeof scope.svg === 'undefined') { - - //prevent duplicate initialization steps, if render is called again - //before the svg is setup - if (!scope.initializing) { - init_panel(); - } - } else { - render_panel(); - } - }; - - - /** - * Receive render events - */ - scope.$on('render', function () { - scope.init_or_render(); - }); - - /** - * On window resize, re-render the panel - */ - angular.element(window).bind('resize', function () { - scope.init_or_render(); - }); - - - /** - * Load the various panel-specific scripts then initialize - * the svg and set appropriate D3 settings - */ - function init_panel() { - - scope.m = [80, 100, 80, 100]; - scope.w = $(elem[0]).width() - scope.m[1] - scope.m[3]; - scope.h = $(elem[0]).height() - scope.m[0] - scope.m[2]; - - - scope.initializing = true; - // Using LABjs, wait until all scripts are loaded before rendering panel - var scripts = $LAB.script("common/lib/d3.v3.min.js?rand="+Math.floor(Math.random()*10000)); + function dragend(d) { + scope.x.domain(scope.panel.fields).rangePoints([0, scope.w]); + var t = d3.transition().duration(500); + t.selectAll(".trait").attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); + t.selectAll(".axis").attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); + t.selectAll(".brush").attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); + t.selectAll(".foregroundlines").attr("d", path); + } - scripts.wait(function () { - scope.x = d3.scale.ordinal().domain(scope.panel.fields).rangePoints([0, scope.w]); - scope.y = {}; - scope.line = d3.svg.line().interpolate('cardinal'); - scope.axis = d3.svg.axis().orient("left"); + /** + * Render updates to the SVG. Typically happens when the data changes (time, query) + * or when new options are selected + */ + function render_panel() { - scope.svg = d3.select(elem[0]).append("svg") - .attr("width", "100%") - .attr("height", "100%") - .attr("viewbox", "0 0 " + (scope.w + scope.m[1] + scope.m[3]) + " " + (scope.h + scope.m[0] + scope.m[2])) - .append("svg:g") - .attr("transform", "translate(" + scope.m[3] + "," + scope.m[0] + ")"); - // Add foreground lines. - scope.foreground = scope.svg.append("svg:g") - .attr("class", "foreground"); + //update the svg if the size has changed + scope.w = $(elem[0]).width() - scope.m[1] - scope.m[3]; + scope.h = $(elem[0]).height() - scope.m[0] - scope.m[2]; + scope.svg.attr("viewbox", "0 0 " + (scope.w + scope.m[1] + scope.m[3]) + " " + (scope.h + scope.m[0] + scope.m[2])); - scope.initializing = false; - render_panel(); - }); + scope.x = d3.scale.ordinal().domain(scope.panel.fields).rangePoints([0, scope.w]); + scope.y = {}; - } + scope.line = d3.svg.line().interpolate('cardinal'); + scope.axis = d3.svg.axis().orient("left"); - // Returns the path for a given data point. - function path(d) { - return scope.line(scope.panel.fields.map(function(p) { return [scope.x(p), scope.y[p](d[p])]; })); - } - // Handles a brush event, toggling the display of foreground lines. - function brush() { - var actives = scope.panel.fields.filter(function(p) { return !scope.y[p].brush.empty(); }), - extents = actives.map(function(p) { return scope.y[p].brush.extent(); }); + var colorExtent = d3.extent(scope.data, function(p) { return +p[scope.panel.fields[0]]; }); - //.fade class hides the "inactive" lines, helps speed up rendering significantly - scope.foregroundLines.classed("fade", function(d) { - return !actives.every(function(p, i) { - var inside = extents[i][0] <= d[p] && d[p] <= extents[i][1]; - return inside; - }); - }); + scope.colors = d3.scale.linear() + .domain([colorExtent[0],colorExtent[1]]) + .range(["#4580FF", "#FF9245"]); - //activeDocs contains the actual doc records for selected lines. - //will be broadcast out to the table - var activeDocs = _.filter(scope.data, function(v) { - return actives.every(function(p,i) { - var inside = extents[i][0] <= v[p] && v[p] <= extents[i][1]; - return inside; - }); - }) - scope.$apply(function() { - scope.activeDocs = activeDocs; - }); - } + scope.panel.fields.forEach(function(d) { + //If it is a string, setup an ordinal scale. + //Otherwise, use a linear scale for numbers + if (_.isString(scope.data[0][d])) { - //Drag functions are used for dragging the axis aroud - function dragstart(d) { - scope.i = scope.panel.fields.indexOf(d); - } + var value = function(v) { return v[d]; }; + var values = _.map(_.uniq(scope.data, value),value); - function drag(d) { - scope.x.range()[scope.i] = d3.event.x; - scope.panel.fields.sort(function(a, b) { return scope.x(a) - scope.x(b); }); - scope.foregroundLines.attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); - scope.traits.attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); - scope.brushes.attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); - scope.axisLines.attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); - scope.foregroundLines.attr("d", path); - } + scope.y[d] = d3.scale.ordinal() + .domain(values) + .rangeBands([scope.h, 0]); + } else if (_.isNumber(scope.data[0][d])) { + scope.y[d] = d3.scale.linear() + .domain(d3.extent(scope.data, function(p) { return +p[d]; })) + .range([scope.h, 0]); + } - function dragend(d) { - scope.x.domain(scope.panel.fields).rangePoints([0, scope.w]); - var t = d3.transition().duration(500); - t.selectAll(".trait").attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); - t.selectAll(".axis").attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); - t.selectAll(".brush").attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); - t.selectAll(".foregroundlines").attr("d", path); - } + scope.y[d].brush = d3.svg.brush() + .y(scope.y[d]) + .on("brush", brush); + }); - /** - * Render updates to the SVG. Typically happens when the data changes (time, query) - * or when new options are selected - */ - function render_panel() { - - - //update the svg if the size has changed - scope.w = $(elem[0]).width() - scope.m[1] - scope.m[3]; - scope.h = $(elem[0]).height() - scope.m[0] - scope.m[2]; - scope.svg.attr("viewbox", "0 0 " + (scope.w + scope.m[1] + scope.m[3]) + " " + (scope.h + scope.m[0] + scope.m[2])); - - - scope.x = d3.scale.ordinal().domain(scope.panel.fields).rangePoints([0, scope.w]); - scope.y = {}; - - scope.line = d3.svg.line().interpolate('cardinal'); - scope.axis = d3.svg.axis().orient("left"); - - - var colorExtent = d3.extent(scope.data, function(p) { return +p[scope.panel.fields[0]]; }); - - scope.colors = d3.scale.linear() - .domain([colorExtent[0],colorExtent[1]]) - .range(["#4580FF", "#FF9245"]); - - - scope.panel.fields.forEach(function(d) { - - //If it is a string, setup an ordinal scale. - //Otherwise, use a linear scale for numbers - if (_.isString(scope.data[0][d])) { - - var value = function(v) { return v[d]; }; - var values = _.map(_.uniq(scope.data, value),value); - - scope.y[d] = d3.scale.ordinal() - .domain(values) - .rangeBands([scope.h, 0]); - } else if (_.isNumber(scope.data[0][d])) { - scope.y[d] = d3.scale.linear() - .domain(d3.extent(scope.data, function(p) { return +p[d]; })) - .range([scope.h, 0]); - } - - - - scope.y[d].brush = d3.svg.brush() - .y(scope.y[d]) - .on("brush", brush); - }); - - - //pull out the actively selected columns for rendering the axis/lines - var activeData = _.map(scope.data, function(d) { - var t = {}; - _.each(scope.panel.fields, function(f) { - t[f] = d[f]; - }); - return t; - }); - - - //Lines - scope.foregroundLines = scope.foreground - .selectAll(".foregroundlines") - .data(activeData, function(d, i){ - var id = ""; - _.each(d, function(v) { - id += i + "_" + v; - }); - return id; - }); - scope.foregroundLines - .enter().append("svg:path") - .attr("d", path) - .attr("class", "foregroundlines") - .attr("style", function(d) { - return "stroke:" + scope.colors(d[scope.panel.fields[0]]) + ";"; - }); - scope.foregroundLines.exit().remove(); - - - - //Axis group - scope.traits = scope.svg.selectAll(".trait") - .data(scope.panel.fields, String); - scope.traits - .enter().append("svg:g") - .attr("class", "trait") - .attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); - scope.traits - .exit().remove(); - - - //brushes used to select lines - scope.brushes = scope.svg.selectAll(".brush") - .data(scope.panel.fields, String); - scope.brushes - .enter() - .append("svg:g") - .attr("class", "brush") - .each(function(d) { - d3.select(this) - .call(scope.y[d].brush) - .attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); - }) - .selectAll("rect") - .attr("x", -8) - .attr("width", 16); - - //this section is repeated because enter() only works on "new" data, but we always need to - //update the brushes if things change. This just calls the brushing function, so it doesn't - //affect currently active rects - scope.brushes - .each(function(d) { - d3.select(this) - .call(scope.y[d].brush) - .attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); - }); - scope.brushes - .exit().remove(); - - - //vertical axis and labels - scope.axisLines = scope.svg.selectAll(".axis") - .data(scope.panel.fields, String); - scope.axisLines - .enter() - .append("svg:g") - .attr("class", "axis") - .each(function(d) { - d3.select(this) - .call(scope.axis.scale(scope.y[d])) - .attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); - }).call(d3.behavior.drag() - .origin(function(d) { return {x: scope.x(d)}; }) - .on("dragstart", dragstart) - .on("drag", drag) - .on("dragend", dragend)) - - .append("svg:text") - .attr("text-anchor", "middle") - .attr("y", -9) - .text(String); - scope.axisLines - .exit().remove(); - - //Simulate a dragend in case there is new data and we need to rearrange - dragend(); - - } + //pull out the actively selected columns for rendering the axis/lines + var activeData = _.map(scope.data, function(d) { + var t = {}; + _.each(scope.panel.fields, function(f) { + t[f] = d[f]; + }); + return t; + }); + + + //Lines + scope.foregroundLines = scope.foreground + .selectAll(".foregroundlines") + .data(activeData, function(d, i){ + var id = ""; + _.each(d, function(v) { + id += i + "_" + v; + }); + return id; + }); + scope.foregroundLines + .enter().append("svg:path") + .attr("d", path) + .attr("class", "foregroundlines") + .attr("style", function(d) { + return "stroke:" + scope.colors(d[scope.panel.fields[0]]) + ";"; + }); + scope.foregroundLines.exit().remove(); + + + + //Axis group + scope.traits = scope.svg.selectAll(".trait") + .data(scope.panel.fields, String); + scope.traits + .enter().append("svg:g") + .attr("class", "trait") + .attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); + scope.traits + .exit().remove(); + + + //brushes used to select lines + scope.brushes = scope.svg.selectAll(".brush") + .data(scope.panel.fields, String); + scope.brushes + .enter() + .append("svg:g") + .attr("class", "brush") + .each(function(d) { + d3.select(this) + .call(scope.y[d].brush) + .attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); + }) + .selectAll("rect") + .attr("x", -8) + .attr("width", 16); + + //this section is repeated because enter() only works on "new" data, but we always need to + //update the brushes if things change. This just calls the brushing function, so it doesn't + //affect currently active rects + scope.brushes + .each(function(d) { + d3.select(this) + .call(scope.y[d].brush) + .attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); + }); + scope.brushes + .exit().remove(); + + + //vertical axis and labels + scope.axisLines = scope.svg.selectAll(".axis") + .data(scope.panel.fields, String); + scope.axisLines + .enter() + .append("svg:g") + .attr("class", "axis") + .each(function(d) { + d3.select(this) + .call(scope.axis.scale(scope.y[d])) + .attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); + }).call(d3.behavior.drag() + .origin(function(d) { return {x: scope.x(d)}; }) + .on("dragstart", dragstart) + .on("drag", drag) + .on("dragend", dragend)) + + .append("svg:text") + .attr("text-anchor", "middle") + .attr("y", -9) + .text(String); + scope.axisLines + .exit().remove(); + + //Simulate a dragend in case there is new data and we need to rearrange + dragend(); - } - }; - }); \ No newline at end of file + } + + } + }; + }); \ No newline at end of file From 915a0e379c4d6be1713edd14a33be1a27dd461b9 Mon Sep 17 00:00:00 2001 From: Zachary Tong Date: Thu, 18 Apr 2013 14:07:37 -0400 Subject: [PATCH 2/4] Move directive-level variables out of scope --- panels/parallelcoordinates/module.js | 135 ++++++++++++++------------- 1 file changed, 68 insertions(+), 67 deletions(-) diff --git a/panels/parallelcoordinates/module.js b/panels/parallelcoordinates/module.js index 1ac0228ce02..1ad21cf57cd 100644 --- a/panels/parallelcoordinates/module.js +++ b/panels/parallelcoordinates/module.js @@ -148,6 +148,9 @@ angular.module('kibana.parallelcoordinates', []) restrict: 'A', link: function (scope, elem, attrs) { + //used to store a variety of directive-level variables + var directive = {}; + scope.initializing = false; @@ -155,7 +158,7 @@ angular.module('kibana.parallelcoordinates', []) * Initialize the panels if new, or render existing panels */ scope.init_or_render = function() { - if (typeof scope.svg === 'undefined') { + if (typeof directive.svg === 'undefined') { //prevent duplicate initialization steps, if render is called again //before the svg is setup @@ -189,9 +192,9 @@ angular.module('kibana.parallelcoordinates', []) */ function init_panel() { - scope.m = [80, 100, 80, 100]; - scope.w = $(elem[0]).width() - scope.m[1] - scope.m[3]; - scope.h = $(elem[0]).height() - scope.m[0] - scope.m[2]; + directive.m = [80, 100, 80, 100]; + directive.w = $(elem[0]).width() - directive.m[1] - directive.m[3]; + directive.h = $(elem[0]).height() - directive.m[0] - directive.m[2]; scope.initializing = true; @@ -200,22 +203,22 @@ angular.module('kibana.parallelcoordinates', []) scripts.wait(function () { + directive.x = d3.scale.ordinal().domain(scope.panel.fields).rangePoints([0, directive.w]); + directive.y = {}; - scope.x = d3.scale.ordinal().domain(scope.panel.fields).rangePoints([0, scope.w]); - scope.y = {}; - - scope.line = d3.svg.line().interpolate('cardinal'); - scope.axis = d3.svg.axis().orient("left"); + directive.line = d3.svg.line().interpolate('cardinal'); + directive.axis = d3.svg.axis().orient("left"); - scope.svg = d3.select(elem[0]).append("svg") + var viewbox = "0 0 " + (directive.w + directive.m[1] + directive.m[3]) + " " + (directive.h + directive.m[0] + directive.m[2]); + directive.svg = d3.select(elem[0]).append("svg") .attr("width", "100%") .attr("height", "100%") - .attr("viewbox", "0 0 " + (scope.w + scope.m[1] + scope.m[3]) + " " + (scope.h + scope.m[0] + scope.m[2])) + .attr("viewbox", viewbox) .append("svg:g") - .attr("transform", "translate(" + scope.m[3] + "," + scope.m[0] + ")"); + .attr("transform", "translate(" + directive.m[3] + "," + directive.m[0] + ")"); // Add foreground lines. - scope.foreground = scope.svg.append("svg:g") + directive.foreground = directive.svg.append("svg:g") .attr("class", "foreground"); scope.initializing = false; @@ -227,16 +230,16 @@ angular.module('kibana.parallelcoordinates', []) // Returns the path for a given data point. function path(d) { - return scope.line(scope.panel.fields.map(function(p) { return [scope.x(p), scope.y[p](d[p])]; })); + return directive.line(scope.panel.fields.map(function(p) { return [directive.x(p), directive.y[p](d[p])]; })); } // Handles a brush event, toggling the display of foreground lines. function brush() { - var actives = scope.panel.fields.filter(function(p) { return !scope.y[p].brush.empty(); }), - extents = actives.map(function(p) { return scope.y[p].brush.extent(); }); + var actives = scope.panel.fields.filter(function(p) { return !directive.y[p].brush.empty(); }), + extents = actives.map(function(p) { return directive.y[p].brush.extent(); }); //.fade class hides the "inactive" lines, helps speed up rendering significantly - scope.foregroundLines.classed("fade", function(d) { + directive.foregroundLines.classed("fade", function(d) { return !actives.every(function(p, i) { var inside = extents[i][0] <= d[p] && d[p] <= extents[i][1]; return inside; @@ -260,25 +263,25 @@ angular.module('kibana.parallelcoordinates', []) //Drag functions are used for dragging the axis aroud function dragstart(d) { - scope.i = scope.panel.fields.indexOf(d); + directive.i = scope.panel.fields.indexOf(d); } function drag(d) { - scope.x.range()[scope.i] = d3.event.x; - scope.panel.fields.sort(function(a, b) { return scope.x(a) - scope.x(b); }); - scope.foregroundLines.attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); - scope.traits.attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); - scope.brushes.attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); - scope.axisLines.attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); - scope.foregroundLines.attr("d", path); + directive.x.range()[directive.i] = d3.event.x; + scope.panel.fields.sort(function(a, b) { return directive.x(a) - directive.x(b); }); + directive.foregroundLines.attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; }); + directive.traits.attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; }); + directive.brushes.attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; }); + directive.axisLines.attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; }); + directive.foregroundLines.attr("d", path); } function dragend(d) { - scope.x.domain(scope.panel.fields).rangePoints([0, scope.w]); + directive.x.domain(scope.panel.fields).rangePoints([0, directive.w]); var t = d3.transition().duration(500); - t.selectAll(".trait").attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); - t.selectAll(".axis").attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); - t.selectAll(".brush").attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); + t.selectAll(".trait").attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; }); + t.selectAll(".axis").attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; }); + t.selectAll(".brush").attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; }); t.selectAll(".foregroundlines").attr("d", path); } @@ -293,21 +296,21 @@ angular.module('kibana.parallelcoordinates', []) //update the svg if the size has changed - scope.w = $(elem[0]).width() - scope.m[1] - scope.m[3]; - scope.h = $(elem[0]).height() - scope.m[0] - scope.m[2]; - scope.svg.attr("viewbox", "0 0 " + (scope.w + scope.m[1] + scope.m[3]) + " " + (scope.h + scope.m[0] + scope.m[2])); + directive.w = $(elem[0]).width() - directive.m[1] - directive.m[3]; + directive.h = $(elem[0]).height() - directive.m[0] - directive.m[2]; + directive.svg.attr("viewbox", "0 0 " + (directive.w + directive.m[1] + directive.m[3]) + " " + (directive.h + directive.m[0] + directive.m[2])); - scope.x = d3.scale.ordinal().domain(scope.panel.fields).rangePoints([0, scope.w]); - scope.y = {}; + directive.x = d3.scale.ordinal().domain(scope.panel.fields).rangePoints([0, directive.w]); + directive.y = {}; - scope.line = d3.svg.line().interpolate('cardinal'); - scope.axis = d3.svg.axis().orient("left"); + directive.line = d3.svg.line().interpolate('cardinal'); + directive.axis = d3.svg.axis().orient("left"); var colorExtent = d3.extent(scope.data, function(p) { return +p[scope.panel.fields[0]]; }); - scope.colors = d3.scale.linear() + directive.colors = d3.scale.linear() .domain([colorExtent[0],colorExtent[1]]) .range(["#4580FF", "#FF9245"]); @@ -321,19 +324,17 @@ angular.module('kibana.parallelcoordinates', []) var value = function(v) { return v[d]; }; var values = _.map(_.uniq(scope.data, value),value); - scope.y[d] = d3.scale.ordinal() + directive.y[d] = d3.scale.ordinal() .domain(values) - .rangeBands([scope.h, 0]); + .rangeBands([directive.h, 0]); } else if (_.isNumber(scope.data[0][d])) { - scope.y[d] = d3.scale.linear() + directive.y[d] = d3.scale.linear() .domain(d3.extent(scope.data, function(p) { return +p[d]; })) - .range([scope.h, 0]); + .range([directive.h, 0]); } - - - scope.y[d].brush = d3.svg.brush() - .y(scope.y[d]) + directive.y[d].brush = d3.svg.brush() + .y(directive.y[d]) .on("brush", brush); }); @@ -349,7 +350,7 @@ angular.module('kibana.parallelcoordinates', []) //Lines - scope.foregroundLines = scope.foreground + directive.foregroundLines = directive.foreground .selectAll(".foregroundlines") .data(activeData, function(d, i){ var id = ""; @@ -358,39 +359,39 @@ angular.module('kibana.parallelcoordinates', []) }); return id; }); - scope.foregroundLines + directive.foregroundLines .enter().append("svg:path") .attr("d", path) .attr("class", "foregroundlines") .attr("style", function(d) { - return "stroke:" + scope.colors(d[scope.panel.fields[0]]) + ";"; + return "stroke:" + directive.colors(d[scope.panel.fields[0]]) + ";"; }); - scope.foregroundLines.exit().remove(); + directive.foregroundLines.exit().remove(); //Axis group - scope.traits = scope.svg.selectAll(".trait") + directive.traits = directive.svg.selectAll(".trait") .data(scope.panel.fields, String); - scope.traits + directive.traits .enter().append("svg:g") .attr("class", "trait") - .attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); - scope.traits + .attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; }); + directive.traits .exit().remove(); //brushes used to select lines - scope.brushes = scope.svg.selectAll(".brush") + directive.brushes = directive.svg.selectAll(".brush") .data(scope.panel.fields, String); - scope.brushes + directive.brushes .enter() .append("svg:g") .attr("class", "brush") .each(function(d) { d3.select(this) - .call(scope.y[d].brush) - .attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); + .call(directive.y[d].brush) + .attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; }); }) .selectAll("rect") .attr("x", -8) @@ -399,29 +400,29 @@ angular.module('kibana.parallelcoordinates', []) //this section is repeated because enter() only works on "new" data, but we always need to //update the brushes if things change. This just calls the brushing function, so it doesn't //affect currently active rects - scope.brushes + directive.brushes .each(function(d) { d3.select(this) - .call(scope.y[d].brush) - .attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); + .call(directive.y[d].brush) + .attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; }); }); - scope.brushes + directive.brushes .exit().remove(); //vertical axis and labels - scope.axisLines = scope.svg.selectAll(".axis") + directive.axisLines = directive.svg.selectAll(".axis") .data(scope.panel.fields, String); - scope.axisLines + directive.axisLines .enter() .append("svg:g") .attr("class", "axis") .each(function(d) { d3.select(this) - .call(scope.axis.scale(scope.y[d])) - .attr("transform", function(d) { return "translate(" + scope.x(d) + ")"; }); + .call(directive.axis.scale(directive.y[d])) + .attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; }); }).call(d3.behavior.drag() - .origin(function(d) { return {x: scope.x(d)}; }) + .origin(function(d) { return {x: directive.x(d)}; }) .on("dragstart", dragstart) .on("drag", drag) .on("dragend", dragend)) @@ -430,7 +431,7 @@ angular.module('kibana.parallelcoordinates', []) .attr("text-anchor", "middle") .attr("y", -9) .text(String); - scope.axisLines + directive.axisLines .exit().remove(); //Simulate a dragend in case there is new data and we need to rearrange From 2706c737902447e73986dbe03bdf8e4f2d3fc68a Mon Sep 17 00:00:00 2001 From: Zachary Tong Date: Thu, 18 Apr 2013 14:09:51 -0400 Subject: [PATCH 3/4] Remove unused settings --- panels/parallelcoordinates/editor.html | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/panels/parallelcoordinates/editor.html b/panels/parallelcoordinates/editor.html index 2b55cd30c88..7141eeae846 100644 --- a/panels/parallelcoordinates/editor.html +++ b/panels/parallelcoordinates/editor.html @@ -7,19 +7,6 @@ -
-
-
-
Add field
- - -
-
-
-
Selected fields Click to remove
- {{field}} -
-
Panel Spy
From 5d72a6337d2f5186bac349c8ac99c9d96a391b7e Mon Sep 17 00:00:00 2001 From: Zachary Tong Date: Thu, 18 Apr 2013 14:15:17 -0400 Subject: [PATCH 4/4] Remove unused css --- panels/parallelcoordinates/module.html | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/panels/parallelcoordinates/module.html b/panels/parallelcoordinates/module.html index d80efbc3ea1..1401501ab74 100644 --- a/panels/parallelcoordinates/module.html +++ b/panels/parallelcoordinates/module.html @@ -13,27 +13,10 @@ display:none } - .legend { - font-size: 18px; - font-style: oblique; - } - .legend line { stroke-width: 2px; } - .setosa { - stroke: #800; - } - - .versicolor { - stroke: #080; - } - - .virginica { - stroke: #008; - } - .brush .extent { fill-opacity: .3; stroke: #fff;