diff --git a/config/plugins/visualizations/scatterplot/src/scatterplot.js b/config/plugins/visualizations/scatterplot/src/scatterplot.js
index 0ef9783334c..7eb8cea7cac 100644
--- a/config/plugins/visualizations/scatterplot/src/scatterplot.js
+++ b/config/plugins/visualizations/scatterplot/src/scatterplot.js
@@ -7,168 +7,183 @@ import * as VisualizationModel from "mvc/visualization/visualization-model";
* var plot = new scatterplot( $( 'svg' ).get(0), config, data )
*/
-export function scatterplot( renderTo, config, data ){
+export function scatterplot(renderTo, config, data) {
//console.log( 'scatterplot', config );
- var translateStr = function( x, y ){
- return 'translate(' + x + ',' + y + ')';
+ var translateStr = function(x, y) {
+ return "translate(" + x + "," + y + ")";
},
- rotateStr = function( d, x, y ){
- return 'rotate(' + d + ',' + x + ',' + y + ')';
+ rotateStr = function(d, x, y) {
+ return "rotate(" + d + "," + x + "," + y + ")";
},
- getX = function( d, i ){
+ getX = function(d, i) {
//console.debug( d[ config.xColumn ] );
- return d[ config.xColumn ];
+ return d[config.xColumn];
},
- getY = function( d, i ){
+ getY = function(d, i) {
//console.debug( d[ config.yColumn ] );
- return d[ config.yColumn ];
+ return d[config.yColumn];
};
// .................................................................... scales
var stats = {
- x : { extent: d3.extent( data, getX ) },
- y : { extent: d3.extent( data, getY ) }
- };
+ x: { extent: d3.extent(data, getX) },
+ y: { extent: d3.extent(data, getY) }
+ };
//TODO: set pan/zoom limits
// from http://stackoverflow.com/questions/10422738/limiting-domain-when-zooming-or-panning-in-d3-js
//self.x.domain([Math.max(self.x.domain()[0], self.options.xmin), Math.min(self.x.domain()[1], self.options.xmax)]);
//self.y.domain([Math.max(self.y.domain()[0], self.options.ymin), Math.min(self.y.domain()[1], self.options.ymax)]);
var interpolaterFns = {
- x : d3.scale.linear()
- .domain( stats.x.extent )
- .range([ 0, config.width ]),
- y : d3.scale.linear()
- .domain( stats.y.extent )
- .range([ config.height, 0 ])
+ x: d3.scale
+ .linear()
+ .domain(stats.x.extent)
+ .range([0, config.width]),
+ y: d3.scale
+ .linear()
+ .domain(stats.y.extent)
+ .range([config.height, 0])
};
// .................................................................... main components
- var zoom = d3.behavior.zoom()
- .x( interpolaterFns.x )
- .y( interpolaterFns.y )
- .scaleExtent([ 1, 30 ])
- .scale( config.scale || 1 )
- .translate( config.translate || [ 0, 0 ] );
+ var zoom = d3.behavior
+ .zoom()
+ .x(interpolaterFns.x)
+ .y(interpolaterFns.y)
+ .scaleExtent([1, 30])
+ .scale(config.scale || 1)
+ .translate(config.translate || [0, 0]);
//console.debug( renderTo );
- var svg = d3.select( renderTo )
- .attr( "class", "scatterplot" )
+ var svg = d3
+ .select(renderTo)
+ .attr("class", "scatterplot")
//.attr( "width", config.width + ( config.margin.right + config.margin.left ) )
- .attr( "width", '100%' )
- .attr( "height", config.height + ( config.margin.top + config.margin.bottom ) );
+ .attr("width", "100%")
+ .attr("height", config.height + (config.margin.top + config.margin.bottom));
- var content = svg.append( "g" )
- .attr( "class", "content" )
- .attr( "transform", translateStr( config.margin.left, config.margin.top ) )
- .call( zoom );
+ var content = svg
+ .append("g")
+ .attr("class", "content")
+ .attr("transform", translateStr(config.margin.left, config.margin.top))
+ .call(zoom);
// a BIG gotcha - zoom (or any mouse/touch event in SVG?) requires the pointer to be over an object
// create a transparent rect to be that object here
- content.append( 'rect' )
- .attr( "class", "zoom-rect" )
- .attr( "width", config.width ).attr( "height", config.height )
- .style( "fill", "transparent" );
+ content
+ .append("rect")
+ .attr("class", "zoom-rect")
+ .attr("width", config.width)
+ .attr("height", config.height)
+ .style("fill", "transparent");
//console.log( 'svg:', svg, 'content:', content );
// .................................................................... axes
- var axis = { x : {}, y : {} };
+ var axis = { x: {}, y: {} };
//console.log( 'xTicks:', config.xTicks );
//console.log( 'yTicks:', config.yTicks );
- axis.x.fn = d3.svg.axis()
- .orient( 'bottom' )
- .scale( interpolaterFns.x )
- .ticks( config.xTicks )
+ axis.x.fn = d3.svg
+ .axis()
+ .orient("bottom")
+ .scale(interpolaterFns.x)
+ .ticks(config.xTicks)
// this will convert thousands -> k, millions -> M, etc.
- .tickFormat( d3.format( 's' ) );
+ .tickFormat(d3.format("s"));
- axis.y.fn = d3.svg.axis()
- .orient( 'left' )
- .scale( interpolaterFns.y )
- .ticks( config.yTicks )
- .tickFormat( d3.format( 's' ) );
+ axis.y.fn = d3.svg
+ .axis()
+ .orient("left")
+ .scale(interpolaterFns.y)
+ .ticks(config.yTicks)
+ .tickFormat(d3.format("s"));
- axis.x.g = content.append( 'g' )
- .attr( 'class', 'x axis' )
- .attr( 'transform', translateStr( 0, config.height ) )
- .call( axis.x.fn );
+ axis.x.g = content
+ .append("g")
+ .attr("class", "x axis")
+ .attr("transform", translateStr(0, config.height))
+ .call(axis.x.fn);
//console.log( 'axis.x.g:', axis.x.g );
- axis.y.g = content.append( 'g' )
- .attr( 'class', 'y axis' )
- .call( axis.y.fn );
+ axis.y.g = content
+ .append("g")
+ .attr("class", "y axis")
+ .call(axis.y.fn);
//console.log( 'axis.y.g:', axis.y.g );
// ................................ axis labels
var padding = 6;
// x-axis label
- axis.x.label = svg.append( 'text' )
- .attr( 'id', 'x-axis-label' )
- .attr( 'class', 'axis-label' )
- .text( config.xLabel )
+ axis.x.label = svg
+ .append("text")
+ .attr("id", "x-axis-label")
+ .attr("class", "axis-label")
+ .text(config.xLabel)
// align to the top-middle
- .attr( 'text-anchor', 'middle' )
- .attr( 'dominant-baseline', 'text-after-edge' )
- .attr( 'x', ( config.width / 2 ) + config.margin.left )
+ .attr("text-anchor", "middle")
+ .attr("dominant-baseline", "text-after-edge")
+ .attr("x", config.width / 2 + config.margin.left)
// place 4 pixels below the axis bounds
- .attr( 'y', ( config.height + config.margin.bottom + config.margin.top ) - padding );
+ .attr("y", config.height + config.margin.bottom + config.margin.top - padding);
//console.log( 'axis.x.label:', axis.x.label );
//TODO: anchor to left of x margin/graph
// y-axis label
// place 4 pixels left of the axis.y.g left edge
- axis.y.label = svg.append( 'text' )
- .attr( 'id', 'y-axis-label' )
- .attr( 'class', 'axis-label' )
- .text( config.yLabel )
+ axis.y.label = svg
+ .append("text")
+ .attr("id", "y-axis-label")
+ .attr("class", "axis-label")
+ .text(config.yLabel)
// align to bottom-middle
- .attr( 'text-anchor', 'middle' )
- .attr( 'dominant-baseline', 'text-before-edge' )
- .attr( 'x', padding )
- .attr( 'y', config.height / 2 )
+ .attr("text-anchor", "middle")
+ .attr("dominant-baseline", "text-before-edge")
+ .attr("x", padding)
+ .attr("y", config.height / 2)
// rotate around the alignment point
- .attr( 'transform', rotateStr( -90, padding, config.height / 2 ) );
+ .attr("transform", rotateStr(-90, padding, config.height / 2));
//console.log( 'axis.y.label:', axis.y.label );
- axis.redraw = function _redrawAxis(){
- svg.select( ".x.axis" ).call( axis.x.fn );
- svg.select( ".y.axis" ).call( axis.y.fn );
+ axis.redraw = function _redrawAxis() {
+ svg.select(".x.axis").call(axis.x.fn);
+ svg.select(".y.axis").call(axis.y.fn);
};
// .................................................................... grid
- function renderGrid(){
- var grid = { v : {}, h: {} };
+ function renderGrid() {
+ var grid = { v: {}, h: {} };
// vertical
- grid.v.lines = content.selectAll( 'line.v-grid-line' )
+ grid.v.lines = content
+ .selectAll("line.v-grid-line")
// data are the axis ticks; enter, update, exit
- .data( interpolaterFns.x.ticks( axis.x.fn.ticks()[0] ) );
+ .data(interpolaterFns.x.ticks(axis.x.fn.ticks()[0]));
// enter: append any extra lines needed (more ticks)
- grid.v.lines.enter()
- .append( 'svg:line' )
- .classed( 'grid-line v-grid-line', true );
+ grid.v.lines
+ .enter()
+ .append("svg:line")
+ .classed("grid-line v-grid-line", true);
// update: set coords
grid.v.lines
- .attr( 'x1', interpolaterFns.x )
- .attr( 'x2', interpolaterFns.x )
- .attr( 'y1', 0 )
- .attr( 'y2', config.height );
+ .attr("x1", interpolaterFns.x)
+ .attr("x2", interpolaterFns.x)
+ .attr("y1", 0)
+ .attr("y2", config.height);
// exit: just remove them
grid.v.lines.exit().remove();
//console.log( 'grid.v.lines:', grid.v.lines );
// horizontal
- grid.h.lines = content.selectAll( 'line.h-grid-line' )
- .data( interpolaterFns.y.ticks( axis.y.fn.ticks()[0] ) );
- grid.h.lines.enter()
- .append( 'svg:line' )
- .classed( 'grid-line h-grid-line', true );
+ grid.h.lines = content.selectAll("line.h-grid-line").data(interpolaterFns.y.ticks(axis.y.fn.ticks()[0]));
grid.h.lines
- .attr( 'x1', 0 )
- .attr( 'x2', config.width )
- .attr( 'y1', interpolaterFns.y )
- .attr( 'y2', interpolaterFns.y );
+ .enter()
+ .append("svg:line")
+ .classed("grid-line h-grid-line", true);
+ grid.h.lines
+ .attr("x1", 0)
+ .attr("x2", config.width)
+ .attr("y1", interpolaterFns.y)
+ .attr("y2", interpolaterFns.y);
grid.h.lines.exit().remove();
//console.log( 'grid.h.lines:', grid.h.lines );
return grid;
@@ -176,113 +191,142 @@ export function scatterplot( renderTo, config, data ){
var grid = renderGrid();
//// .................................................................... datapoints
- var datapoints = content.selectAll( '.glyph' ).data( data )
+ var datapoints = content
+ .selectAll(".glyph")
+ .data(data)
// enter - NEW data to be added as glyphs
- .enter().append( 'svg:circle' )
- .classed( "glyph", true )
- .attr( "cx", function( d, i ){ return interpolaterFns.x( getX( d, i ) ); })
- .attr( "cy", function( d, i ){ return interpolaterFns.y( getY( d, i ) ); })
- .attr( "r", 0 );
+ .enter()
+ .append("svg:circle")
+ .classed("glyph", true)
+ .attr("cx", function(d, i) {
+ return interpolaterFns.x(getX(d, i));
+ })
+ .attr("cy", function(d, i) {
+ return interpolaterFns.y(getY(d, i));
+ })
+ .attr("r", 0);
// for all EXISTING glyphs and those that need to be added: transition anim to final state
- datapoints.transition().duration( config.animDuration )
- .attr( "r", config.datapointSize );
+ datapoints
+ .transition()
+ .duration(config.animDuration)
+ .attr("r", config.datapointSize);
//console.log( 'datapoints:', datapoints );
- function _redrawDatapointsClipped(){
- return datapoints
- //TODO: interpolates twice
- .attr( "cx", function( d, i ){ return interpolaterFns.x( getX( d, i ) ); })
- .attr( "cy", function( d, i ){ return interpolaterFns.y( getY( d, i ) ); })
- .style( 'display', 'block' )
- // filter out points now outside the graph content area and hide them
- .filter( function( d, i ){
- var cx = d3.select( this ).attr( "cx" ),
- cy = d3.select( this ).attr( "cy" );
- if( cx < 0 || cx > config.width ){ return true; }
- if( cy < 0 || cy > config.height ){ return true; }
- return false;
- }).style( 'display', 'none' );
+ function _redrawDatapointsClipped() {
+ return (
+ datapoints
+ //TODO: interpolates twice
+ .attr("cx", function(d, i) {
+ return interpolaterFns.x(getX(d, i));
+ })
+ .attr("cy", function(d, i) {
+ return interpolaterFns.y(getY(d, i));
+ })
+ .style("display", "block")
+ // filter out points now outside the graph content area and hide them
+ .filter(function(d, i) {
+ var cx = d3.select(this).attr("cx"),
+ cy = d3.select(this).attr("cy");
+ if (cx < 0 || cx > config.width) {
+ return true;
+ }
+ if (cy < 0 || cy > config.height) {
+ return true;
+ }
+ return false;
+ })
+ .style("display", "none")
+ );
}
_redrawDatapointsClipped();
// .................................................................... behaviors
- function zoomed( scale, translateX, translateY ){
+ function zoomed(scale, translateX, translateY) {
//console.debug( 'zoom', this, zoom.scale(), zoom.translate() );
// re-render axis, grid, and datapoints
- $( '.chart-info-box' ).remove();
+ $(".chart-info-box").remove();
axis.redraw();
_redrawDatapointsClipped();
grid = renderGrid();
- $( svg.node() ).trigger( 'zoom.scatterplot', {
- scale : zoom.scale(),
- translate : zoom.translate()
+ $(svg.node()).trigger("zoom.scatterplot", {
+ scale: zoom.scale(),
+ translate: zoom.translate()
});
}
//TODO: programmatically set zoom/pan and save in config
//TODO: set pan/zoom limits
- zoom.on( "zoom", zoomed );
+ zoom.on("zoom", zoomed);
- function infoBox( top, left, d ){
+ function infoBox(top, left, d) {
// create an abs pos. element containing datapoint data (d) near the point (top, left)
// with added padding to clear the mouse pointer
left += 8;
- return $([
- '
',
- (( config.idColumn !== undefined )?( '
' + d[ config.idColumn ] + '
' ):( '' )),
- '
', getX( d ), '
',
- '
', getY( d ), '
',
- '
'
- ].join( '' ) ).css({ top: top, left: left, 'z-index': 2 });
+ return $(
+ [
+ '',
+ config.idColumn !== undefined ? "
" + d[config.idColumn] + "
" : "",
+ "
",
+ getX(d),
+ "
",
+ "
",
+ getY(d),
+ "
",
+ "
"
+ ].join("")
+ ).css({ top: top, left: left, "z-index": 2 });
}
- datapoints.on( 'mouseover', function( d, i ){
- var datapoint = d3.select( this );
+ datapoints.on("mouseover", function(d, i) {
+ var datapoint = d3.select(this);
datapoint
- .classed( 'highlight', true )
- .style( 'fill', 'red' )
- .style( 'fill-opacity', 1 );
+ .classed("highlight", true)
+ .style("fill", "red")
+ .style("fill-opacity", 1);
// create horiz line to axis
- content.append( 'line' )
- .attr( 'stroke', 'red' )
- .attr( 'stroke-width', 1 )
+ content
+ .append("line")
+ .attr("stroke", "red")
+ .attr("stroke-width", 1)
// start not at center, but at the edge of the circle - to prevent mouseover thrashing
- .attr( 'x1', datapoint.attr( 'cx' ) - config.datapointSize )
- .attr( 'y1', datapoint.attr( 'cy' ) )
- .attr( 'x2', 0 )
- .attr( 'y2', datapoint.attr( 'cy' ) )
- .classed( 'hoverline', true );
+ .attr("x1", datapoint.attr("cx") - config.datapointSize)
+ .attr("y1", datapoint.attr("cy"))
+ .attr("x2", 0)
+ .attr("y2", datapoint.attr("cy"))
+ .classed("hoverline", true);
// create vertical line to axis - if not on the x axis
- if( datapoint.attr( 'cy' ) < config.height ){
- content.append( 'line' )
- .attr( 'stroke', 'red' )
- .attr( 'stroke-width', 1 )
- .attr( 'x1', datapoint.attr( 'cx' ) )
+ if (datapoint.attr("cy") < config.height) {
+ content
+ .append("line")
+ .attr("stroke", "red")
+ .attr("stroke-width", 1)
+ .attr("x1", datapoint.attr("cx"))
// attributes are strings so, (accrd. to js) '3' - 1 = 2 but '3' + 1 = '31': coerce
- .attr( 'y1', +datapoint.attr( 'cy' ) + config.datapointSize )
- .attr( 'x2', datapoint.attr( 'cx' ) )
- .attr( 'y2', config.height )
- .classed( 'hoverline', true );
+ .attr("y1", +datapoint.attr("cy") + config.datapointSize)
+ .attr("x2", datapoint.attr("cx"))
+ .attr("y2", config.height)
+ .classed("hoverline", true);
}
// show the info box and trigger an event
var bbox = this.getBoundingClientRect();
- $( 'body' ).append( infoBox( bbox.top, bbox.right, d ) );
- $( svg.node() ).trigger( 'mouseover-datapoint.scatterplot', [ this, d, i ] );
+ $("body").append(infoBox(bbox.top, bbox.right, d));
+ $(svg.node()).trigger("mouseover-datapoint.scatterplot", [this, d, i]);
});
- datapoints.on( 'mouseout', function(){
+ datapoints.on("mouseout", function() {
// return the point to normal, remove hoverlines and info box
- d3.select( this )
- .classed( 'highlight', false )
- .style( 'fill', 'black' )
- .style( 'fill-opacity', 0.2 );
- content.selectAll( '.hoverline' ).remove();
- $( '.chart-info-box' ).remove();
+ d3
+ .select(this)
+ .classed("highlight", false)
+ .style("fill", "black")
+ .style("fill-opacity", 0.2);
+ content.selectAll(".hoverline").remove();
+ $(".chart-info-box").remove();
});
}
@@ -321,37 +365,37 @@ todo:
var ScatterplotConfigEditor = Backbone.View.extend({
//TODO: !should be a view on a visualization model
//logger : console,
- className : 'scatterplot-control-form',
+ className: "scatterplot-control-form",
/** initialize requires a configuration Object containing a dataset Object */
- initialize : function( attributes ){
- if( !this.model ){
- this.model = new VisualizationModel.Visualization({ type: 'scatterplot' });
+ initialize: function(attributes) {
+ if (!this.model) {
+ this.model = new VisualizationModel.Visualization({ type: "scatterplot" });
}
//this.log( this + '.initialize, attributes:', attributes );
- if( !attributes || !attributes.dataset ){
- throw new Error( "ScatterplotConfigEditor requires a dataset" );
+ if (!attributes || !attributes.dataset) {
+ throw new Error("ScatterplotConfigEditor requires a dataset");
}
this.dataset = attributes.dataset;
//this.log( 'dataset:', this.dataset );
this.display = new ScatterplotDisplay({
- dataset : attributes.dataset,
- model : this.model
+ dataset: attributes.dataset,
+ model: this.model
});
},
// ------------------------------------------------------------------------- CONTROLS RENDERING
- render : function(){
+ render: function() {
//console.log( this + '.render' );
// render the tab controls, areas and loading indicator
- this.$el.empty().append( ScatterplotConfigEditor.templates.mainLayout({}));
- if( this.model.id ){
- this.$el.find( '.copy-btn' ).show();
- this.$el.find( '.save-btn' ).text( 'Update saved' );
+ this.$el.empty().append(ScatterplotConfigEditor.templates.mainLayout({}));
+ if (this.model.id) {
+ this.$el.find(".copy-btn").show();
+ this.$el.find(".save-btn").text("Update saved");
}
- this.$el.find( '[title]' ).tooltip();
+ this.$el.find("[title]").tooltip();
// render the tab content
this._render_dataControl();
@@ -361,8 +405,8 @@ var ScatterplotConfigEditor = Backbone.View.extend({
// set up behaviours
// auto render if given both x, y column choices
- var config = this.model.get( 'config' );
- if( this.model.id && _.isFinite( config.xColumn ) && _.isFinite( config.yColumn ) ){
+ var config = this.model.get("config");
+ if (this.model.id && _.isFinite(config.xColumn) && _.isFinite(config.yColumn)) {
this.renderChart();
}
return this;
@@ -373,22 +417,22 @@ var ScatterplotConfigEditor = Backbone.View.extend({
* column into the appropriate array.
* Used to disable certain columns from being selected for x, y axes.
*/
- _getColumnIndecesByType : function(){
+ _getColumnIndecesByType: function() {
//TODO: not sure these contraints are necc. now
var types = {
- numeric : [],
- text : [],
- all : []
+ numeric: [],
+ text: [],
+ all: []
};
- _.each( this.dataset.metadata_column_types || [], function( type, i ){
- if( type === 'int' || type === 'float' ){
- types.numeric.push( i );
- } else if( type === 'str' || type === 'list' ){
- types.text.push( i );
+ _.each(this.dataset.metadata_column_types || [], function(type, i) {
+ if (type === "int" || type === "float") {
+ types.numeric.push(i);
+ } else if (type === "str" || type === "list") {
+ types.text.push(i);
}
- types.all.push( i );
+ types.all.push(i);
});
- if( types.numeric.length < 2 ){
+ if (types.numeric.length < 2) {
types.numeric = [];
}
//console.log( 'types:', JSON.stringify( types ) );
@@ -396,277 +440,298 @@ var ScatterplotConfigEditor = Backbone.View.extend({
},
/** controls for which columns are used to plot datapoints (and ids/additional info to attach if desired) */
- _render_dataControl : function( $where ){
+ _render_dataControl: function($where) {
$where = $where || this.$el;
- var editor = this,
+ var editor = this,
//column_names = dataset.metadata_column_names || [],
- config = this.model.get( 'config' ),
+ config = this.model.get("config"),
columnTypes = this._getColumnIndecesByType();
// render the html
- var $dataControl = $where.find( '.tab-pane#data-control' );
- $dataControl.html( ScatterplotConfigEditor.templates.dataControl({
- peek : this.dataset.peek
- }));
+ var $dataControl = $where.find(".tab-pane#data-control");
+ $dataControl.html(
+ ScatterplotConfigEditor.templates.dataControl({
+ peek: this.dataset.peek
+ })
+ );
- $dataControl.find( '.peek' ).peekColumnSelector({
- controls : [
- { label: 'X Column', id: 'xColumn', selected: config.xColumn, disabled: columnTypes.text },
- { label: 'Y Column', id: 'yColumn', selected: config.yColumn, disabled: columnTypes.text },
- { label: 'ID Column', id: 'idColumn', selected: config.idColumn }
- ]
- //renameColumns : true
+ $dataControl
+ .find(".peek")
+ .peekColumnSelector({
+ controls: [
+ { label: "X Column", id: "xColumn", selected: config.xColumn, disabled: columnTypes.text },
+ { label: "Y Column", id: "yColumn", selected: config.yColumn, disabled: columnTypes.text },
+ { label: "ID Column", id: "idColumn", selected: config.idColumn }
+ ]
+ //renameColumns : true
+ })
+ .on("peek-column-selector.change", function(ev, data) {
+ //console.info( 'new selection:', data );
+ editor.model.set("config", data);
+ })
+ .on("peek-column-selector.rename", function(ev, data) {
+ //console.info( 'new column names', data );
+ });
- }).on( 'peek-column-selector.change', function( ev, data ){
- //console.info( 'new selection:', data );
- editor.model.set( 'config', data );
-
- }).on( 'peek-column-selector.rename', function( ev, data ){
- //console.info( 'new column names', data );
- });
-
- $dataControl.find( '[title]' ).tooltip();
+ $dataControl.find("[title]").tooltip();
return $dataControl;
},
/** tab content to control how the chart is rendered (data glyph size, chart size, etc.) */
- _render_chartControls : function( $where ){
-//TODO: as controls on actual chart
+ _render_chartControls: function($where) {
+ //TODO: as controls on actual chart
$where = $where || this.$el;
var editor = this,
- config = this.model.get( 'config' ),
- $chartControls = $where.find( '#chart-control' );
+ config = this.model.get("config"),
+ $chartControls = $where.find("#chart-control");
// ---- skeleton/form for controls
- $chartControls.html( ScatterplotConfigEditor.templates.chartControl( config ) );
+ $chartControls.html(ScatterplotConfigEditor.templates.chartControl(config));
//console.debug( '$chartControl:', $chartControls );
// ---- slider controls
// limits for controls (by control/chartConfig id)
var controlRanges = {
- 'datapointSize' : { min: 2, max: 10, step: 1 },
- 'width' : { min: 200, max: 800, step: 20 },
- 'height' : { min: 200, max: 800, step: 20 }
- };
+ datapointSize: { min: 2, max: 10, step: 1 },
+ width: { min: 200, max: 800, step: 20 },
+ height: { min: 200, max: 800, step: 20 }
+ };
- function onSliderChange(){
+ function onSliderChange() {
// set the model config when changed and update the slider output text
- var $this = $( this ),
+ var $this = $(this),
//note: returns a number nicely enough
- newVal = $this.slider( 'value' );
+ newVal = $this.slider("value");
// parent of slide event target has html5 attr data-config-key
- editor.model.set( 'config', _.object([[ $this.parent().data( 'config-key' ), newVal ]]) );
- $this.siblings( '.slider-output' ).text( newVal );
+ editor.model.set("config", _.object([[$this.parent().data("config-key"), newVal]]));
+ $this.siblings(".slider-output").text(newVal);
}
//console.debug( 'numeric sliders:', $chartControls.find( '.numeric-slider-input' ) );
- $chartControls.find( '.numeric-slider-input' ).each( function(){
+ $chartControls.find(".numeric-slider-input").each(function() {
// set up the slider with control ranges, change event; set output text to initial value
- var $this = $( this ),
- configKey = $this.attr( 'data-config-key' ),
- sliderSettings = _.extend( controlRanges[ configKey ], {
- value : config[ configKey ],
- change : onSliderChange,
- slide : onSliderChange
+ var $this = $(this),
+ configKey = $this.attr("data-config-key"),
+ sliderSettings = _.extend(controlRanges[configKey], {
+ value: config[configKey],
+ change: onSliderChange,
+ slide: onSliderChange
});
//console.debug( configKey + ' slider settings:', sliderSettings );
- $this.find( '.slider' ).slider( sliderSettings );
- $this.children( '.slider-output' ).text( config[ configKey ] );
+ $this.find(".slider").slider(sliderSettings);
+ $this.children(".slider-output").text(config[configKey]);
});
// ---- axes labels
var columnNames = this.dataset.metadata_column_names || [];
- var xLabel = config.xLabel || columnNames[ config.xColumn ] || 'X';
- var yLabel = config.yLabel || columnNames[ config.yColumn ] || 'Y';
+ var xLabel = config.xLabel || columnNames[config.xColumn] || "X";
+ var yLabel = config.yLabel || columnNames[config.yColumn] || "Y";
// set label inputs to current x, y metadata_column_names (if any)
- $chartControls.find( 'input[name="X-axis-label"]' ).val( xLabel )
- .on( 'change', function(){
- editor.model.set( 'config', { xLabel: $( this ).val() });
+ $chartControls
+ .find('input[name="X-axis-label"]')
+ .val(xLabel)
+ .on("change", function() {
+ editor.model.set("config", { xLabel: $(this).val() });
});
- $chartControls.find( 'input[name="Y-axis-label"]' ).val( yLabel )
- .on( 'change', function(){
- editor.model.set( 'config', { yLabel: $( this ).val() });
+ $chartControls
+ .find('input[name="Y-axis-label"]')
+ .val(yLabel)
+ .on("change", function() {
+ editor.model.set("config", { yLabel: $(this).val() });
});
//console.debug( '$chartControls:', $chartControls );
- $chartControls.find( '[title]' ).tooltip();
+ $chartControls.find("[title]").tooltip();
return $chartControls;
},
/** render the tab content where the chart is displayed (but not the chart itself) */
- _render_chartDisplay : function( $where ){
+ _render_chartDisplay: function($where) {
$where = $where || this.$el;
- var $chartDisplay = $where.find( '.tab-pane#chart-display' );
- this.display.setElement( $chartDisplay );
+ var $chartDisplay = $where.find(".tab-pane#chart-display");
+ this.display.setElement($chartDisplay);
this.display.render();
- $chartDisplay.find( '[title]' ).tooltip();
+ $chartDisplay.find("[title]").tooltip();
return $chartDisplay;
},
// ------------------------------------------------------------------------- EVENTS
- events : {
- 'change #include-id-checkbox' : 'toggleThirdColumnSelector',
- 'click #data-control .render-button' : 'renderChart',
- 'click #chart-control .render-button' : 'renderChart',
- 'click .save-btn' : 'saveVisualization',
+ events: {
+ "change #include-id-checkbox": "toggleThirdColumnSelector",
+ "click #data-control .render-button": "renderChart",
+ "click #chart-control .render-button": "renderChart",
+ "click .save-btn": "saveVisualization"
//'click .copy-btn' : function(e){ this.model.save(); }
},
- saveVisualization : function(){
+ saveVisualization: function() {
var editor = this;
- this.model.save()
- .fail( function( xhr, status, message ){
- console.error( xhr, status, message );
- editor.trigger( 'save:error', view );
- alert( 'Error loading data:\n' + xhr.responseText );
+ this.model
+ .save()
+ .fail(function(xhr, status, message) {
+ console.error(xhr, status, message);
+ editor.trigger("save:error", view);
+ alert("Error loading data:\n" + xhr.responseText);
})
- .then( function(){
+ .then(function() {
editor.display.render();
});
},
- toggleThirdColumnSelector : function(){
+ toggleThirdColumnSelector: function() {
// show/hide the id selector on the data settings panel
- this.$el.find( 'select[name="idColumn"]' ).parent().toggle();
+ this.$el
+ .find('select[name="idColumn"]')
+ .parent()
+ .toggle();
},
// ------------------------------------------------------------------------- CHART/STATS RENDERING
- renderChart : function(){
+ renderChart: function() {
//console.log( this + '.renderChart' );
// fetch the data, (re-)render the chart
- this.$el.find( '.nav li.disabled' ).removeClass( 'disabled' );
- this.$el.find( 'ul.nav' ).find( 'a[href="#chart-display"]' ).tab( 'show' );
+ this.$el.find(".nav li.disabled").removeClass("disabled");
+ this.$el
+ .find("ul.nav")
+ .find('a[href="#chart-display"]')
+ .tab("show");
this.display.fetchData();
//console.debug( this.display.$el );
},
- toString : function(){
- return 'ScatterplotConfigEditor(' + (( this.dataset )?( this.dataset.id ):( '' )) + ')';
+ toString: function() {
+ return "ScatterplotConfigEditor(" + (this.dataset ? this.dataset.id : "") + ")";
}
});
ScatterplotConfigEditor.templates = {
// tabbed, main layout for the editor (not used for scatterplot-display)
- mainLayout : _.template([
- '',
+ mainLayout: _.template(
+ [
+ '
',
// tab buttons/headers using Bootstrap
'
',
- // start with the data controls as the displayed tab
- '',
- 'Data Controls ',
- ' ',
- '',
- 'Chart Controls ',
- ' ',
- // chart starts as disabled since there's no info yet
- '',
- 'Chart ',
- ' ',
- // button for saving the visualization config on the server
- '',
- 'Save ',
- ' ',
- ' ',
+ // start with the data controls as the displayed tab
+ '
',
+ 'Data Controls ',
+ " ",
+ "
",
+ 'Chart Controls ',
+ " ",
+ // chart starts as disabled since there's no info yet
+ '
',
+ 'Chart ',
+ " ",
+ // button for saving the visualization config on the server
+ '
',
+ 'Save ',
+ " ",
+ "",
// data form, chart config form, chart all get their own tab
'
',
- // tab for data settings form
- '
',
+ // tab for data settings form
+ '
',
- // tab for chart graphics control form
- '
',
+ // tab for chart graphics control form
+ '
',
- // tab for actual chart
- '
',
+ // tab for actual chart
+ '
',
- '
',
- '
',
- ].join('')),
+ "
",
+ ""
+ ].join("")
+ ),
// the controls for data selection (this is mostly done with column selector now)
// TODO: this could be moved to the main template above
// TODO: localize
- dataControl : _.template([
- '',
- 'Use the following control to change which columns are used by the chart. Click any cell ',
- 'from the last three rows of the table to select the column for the appropriate data. ',
- 'Use the \'Draw\' button to render (or re-render) the chart with the current settings. ',
- '
',
+ dataControl: _.template(
+ [
+ '',
+ "Use the following control to change which columns are used by the chart. Click any cell ",
+ "from the last three rows of the table to select the column for the appropriate data. ",
+ "Use the 'Draw' button to render (or re-render) the chart with the current settings. ",
+ "
",
- '',
- 'X Column : which column values will be used for the x axis of the chart. ',
- 'Y Column : which column values will be used for the y axis of the chart. ',
- 'ID Column : an additional column value displayed when the user hovers over a data point. ',
- 'It may be useful to select unique or categorical identifiers here (such as gene ids).',
- ' ',
- ' ',
+ '',
+ "X Column : which column values will be used for the x axis of the chart. ",
+ "Y Column : which column values will be used for the y axis of the chart. ",
+ "ID Column : an additional column value displayed when the user hovers over a data point. ",
+ "It may be useful to select unique or categorical identifiers here (such as gene ids).",
+ " ",
+ " ",
- '',
+ '
',
// the only dynamic thing
'
<%= peek %> ',
- '
',
+ "
",
- '',
- 'Note : If it can be determined from the dataset\'s filetype that a column is not numeric, ',
- 'that column choice may be disabled for either the x or y axis.',
- '
',
+ '',
+ "Note : If it can be determined from the dataset's filetype that a column is not numeric, ",
+ "that column choice may be disabled for either the x or y axis.",
+ "
",
- 'Draw ',
- ].join('')),
+ 'Draw '
+ ].join("")
+ ),
- chartControl : _.template([
- '',
- 'Use the following controls to how the chart is displayed. The slide controls can be moved ',
- 'by the mouse or, if the \'handle\' is in focus, your keyboard\'s arrow keys. ',
- 'Move the focus between controls by using the tab or shift+tab keys on your keyboard. ',
- 'Use the \'Draw\' button to render (or re-render) the chart with the current settings. ',
- '
',
+ chartControl: _.template(
+ [
+ '',
+ "Use the following controls to how the chart is displayed. The slide controls can be moved ",
+ "by the mouse or, if the 'handle' is in focus, your keyboard's arrow keys. ",
+ "Move the focus between controls by using the tab or shift+tab keys on your keyboard. ",
+ "Use the 'Draw' button to render (or re-render) the chart with the current settings. ",
+ "
",
- '",
- '",
- '",
- '',
+ '
',
'
Re-label the X axis: ',
'
',
'
',
- '
',
+ "
",
- '',
+ '
',
'
Re-label the Y axis: ',
'
',
'
',
- '
',
+ "
",
- 'Draw ',
- ].join('')),
+ 'Draw '
+ ].join("")
+ )
// mainLayout : scatterplot.editor,
// dataControl : scatterplot.datacontrol,
@@ -683,247 +748,266 @@ ScatterplotConfigEditor.templates = {
* controls for pagination of data (if needed)
*/
var ScatterplotDisplay = Backbone.View.extend({
-
- initialize : function( attributes ){
- this.data = null,
- this.dataset = attributes.dataset;
+ initialize: function(attributes) {
+ (this.data = null), (this.dataset = attributes.dataset);
this.lineCount = this.dataset.metadata_data_lines || null;
},
- fetchData : function(){
+ fetchData: function() {
this.showLoadingIndicator();
//console.debug( 'currPage', this.config.pagination.currPage );
var view = this,
- config = this.model.get( 'config' ),
+ config = this.model.get("config"),
//TODO: very tied to datasets - should be generalized eventually
- baseUrl = ( window.parent && parent.galaxy_config )? parent.galaxy_config.root : '/',
- xhr = jQuery.getJSON( baseUrl + 'api/datasets/' + this.dataset.id, {
- data_type : 'raw_data',
- provider : 'dataset-column',
- limit : config.pagination.perPage,
- offset : ( config.pagination.currPage * config.pagination.perPage )
+ baseUrl = window.parent && parent.galaxy_config ? parent.galaxy_config.root : "/",
+ xhr = jQuery.getJSON(baseUrl + "api/datasets/" + this.dataset.id, {
+ data_type: "raw_data",
+ provider: "dataset-column",
+ limit: config.pagination.perPage,
+ offset: config.pagination.currPage * config.pagination.perPage
});
- xhr.done( function( data ){
+ xhr.done(function(data) {
// no need to hide loading indicator, line info will write over that
view.data = data.data;
- view.trigger( 'data:fetched', view );
+ view.trigger("data:fetched", view);
view.renderData();
});
- xhr.fail( function( xhr, status, message ){
- console.error( xhr, status, message );
- view.trigger( 'data:error', view );
- alert( 'Error loading data:\n' + xhr.responseText );
+ xhr.fail(function(xhr, status, message) {
+ console.error(xhr, status, message);
+ view.trigger("data:error", view);
+ alert("Error loading data:\n" + xhr.responseText);
});
return xhr;
},
- showLoadingIndicator : function(){
+ showLoadingIndicator: function() {
// display the loading indicator over the tab panels if hidden, update message (if passed)
- this.$el.find( '.scatterplot-data-info' ).html([
- '',
- ' ',
- 'loading... ',
- '
'
- ].join( '' ));
+ this.$el
+ .find(".scatterplot-data-info")
+ .html(
+ [
+ '',
+ ' ',
+ 'loading... ',
+ "
"
+ ].join("")
+ );
},
- template : function(){
+ template: function() {
var html = [
'',
- '
',
- '
',
- '
Stats ',
- '
Redraw ',
- '
',
- '
',
- '
',
- ' ', //TODO: id
+ '',
+ '
',
+ '
Stats ',
+ '
Redraw ',
+ "
",
+ '",
+ "",
+ " ", //TODO: id
'
'
- ].join( '' );
+ ].join("");
return html;
},
- render : function(){
- this.$el.addClass( 'scatterplot-display' ).html( this.template() );
- if( this.data ){
+ render: function() {
+ this.$el.addClass("scatterplot-display").html(this.template());
+ if (this.data) {
this.renderData();
}
return this;
},
- renderData : function(){
+ renderData: function() {
this.renderLeftControls();
this.renderRightControls();
- this.renderPlot( this.data );
+ this.renderPlot(this.data);
this.getStats();
},
- renderLeftControls : function(){
+ renderLeftControls: function() {
var display = this,
- config = this.model.get( 'config' );
+ config = this.model.get("config");
- this.$el.find( '.controls .left .page-control' ).pagination({
- startingPage : config.pagination.currPage,
- perPage : config.pagination.perPage,
- totalDataSize: this.lineCount,
- currDataSize : this.data.length
+ this.$el
+ .find(".controls .left .page-control")
+ .pagination({
+ startingPage: config.pagination.currPage,
+ perPage: config.pagination.perPage,
+ totalDataSize: this.lineCount,
+ currDataSize: this.data.length
- //TODO: move to named function and remove only named
- }).off().on( 'pagination.page-change', function( event, page ){
- //console.debug( 'pagination:page-change', page );
- config.pagination.currPage = page;
- display.model.set( 'config', { pagination: config.pagination });
- //console.debug( pagination, display.model.get( 'config' ).pagination );
- display.resetZoom();
- display.fetchData();
- });
+ //TODO: move to named function and remove only named
+ })
+ .off()
+ .on("pagination.page-change", function(event, page) {
+ //console.debug( 'pagination:page-change', page );
+ config.pagination.currPage = page;
+ display.model.set("config", { pagination: config.pagination });
+ //console.debug( pagination, display.model.get( 'config' ).pagination );
+ display.resetZoom();
+ display.fetchData();
+ });
return this;
},
- renderRightControls : function(){
+ renderRightControls: function() {
var view = this;
- this.setLineInfo( this.data );
+ this.setLineInfo(this.data);
// clear prev. handlers due to closure around data
- this.$el.find( '.stats-toggle-btn' )
- .off().click( function(){
+ this.$el
+ .find(".stats-toggle-btn")
+ .off()
+ .click(function() {
view.toggleStats();
});
- this.$el.find( '.rerender-btn' )
- .off().click( function(){
+ this.$el
+ .find(".rerender-btn")
+ .off()
+ .click(function() {
view.resetZoom();
- view.renderPlot( this.data );
+ view.renderPlot(this.data);
});
},
/** render and show the d3 plot into the svg node of the view */
- renderPlot : function(){
+ renderPlot: function() {
var view = this,
- $svg = this.$el.find( 'svg' );
+ $svg = this.$el.find("svg");
// turn off stats, clear previous svg, and make it visible
- this.toggleStats( false );
- $svg.off().empty().show()
+ this.toggleStats(false);
+ $svg
+ .off()
+ .empty()
+ .show()
// set up listeners for events from plot
- .on( 'zoom.scatterplot', function( ev, zoom ){
+ .on("zoom.scatterplot", function(ev, zoom) {
//TODO: possibly throttle this
//console.debug( 'zoom.scatterplot', zoom.scale, zoom.translate );
- view.model.set( 'config', zoom );
+ view.model.set("config", zoom);
});
//TODO: may not be necessary to off/on this more than the initial on
// call the sep. d3 function to generate the plot
- scatterplot( $svg.get( 0 ), this.model.get( 'config' ), this.data );
+ scatterplot($svg.get(0), this.model.get("config"), this.data);
},
- setLineInfo : function( data, contents ){
- if( data ){
- var config = this.model.get( 'config' ),
- totalLines = this.lineCount || 'an unknown total',
- lineStart = config.pagination.currPage * config.pagination.perPage,
- lineEnd = lineStart + data.length;
- this.$el.find( '.controls p.scatterplot-data-info' )
- .text([ lineStart + 1, 'to', lineEnd, 'of', totalLines ].join( ' ' ));
+ setLineInfo: function(data, contents) {
+ if (data) {
+ var config = this.model.get("config"),
+ totalLines = this.lineCount || "an unknown total",
+ lineStart = config.pagination.currPage * config.pagination.perPage,
+ lineEnd = lineStart + data.length;
+ this.$el
+ .find(".controls p.scatterplot-data-info")
+ .text([lineStart + 1, "to", lineEnd, "of", totalLines].join(" "));
} else {
- this.$el.find( '.controls p.scatterplot-data-info' ).html( contents || '' );
+ this.$el.find(".controls p.scatterplot-data-info").html(contents || "");
}
return this;
},
- resetZoom : function( scale, translate ){
- scale = ( scale !== undefined )?( scale ):( 1 );
- translate = ( translate !== undefined )?( translate ):( [ 0, 0 ] );
- this.model.set( 'config', { scale: scale, translate: translate } );
+ resetZoom: function(scale, translate) {
+ scale = scale !== undefined ? scale : 1;
+ translate = translate !== undefined ? translate : [0, 0];
+ this.model.set("config", { scale: scale, translate: translate });
return this;
},
// ------------------------------------------------------------------------ statistics display
/** create a webworker to calc stats for data given */
- getStats : function(){
- if( !this.data ){ return; }
+ getStats: function() {
+ if (!this.data) {
+ return;
+ }
var view = this,
- config = this.model.get( 'config' ),
- meanWorker = new Worker( '/plugins/visualizations/scatterplot/static/worker-stats.js' );
+ config = this.model.get("config"),
+ meanWorker = new Worker("/plugins/visualizations/scatterplot/static/worker-stats.js");
meanWorker.postMessage({
- data : this.data,
- keys : [ config.xColumn, config.yColumn ]
+ data: this.data,
+ keys: [config.xColumn, config.yColumn]
});
- meanWorker.onerror = function( event ){
+ meanWorker.onerror = function(event) {
meanWorker.terminate();
};
- meanWorker.onmessage = function( event ){
- view.renderStats( event.data );
+ meanWorker.onmessage = function(event) {
+ view.renderStats(event.data);
};
},
- renderStats : function( stats, error ){
+ renderStats: function(stats, error) {
//console.debug( 'renderStats:', stats, error );
//console.debug( JSON.stringify( stats, null, ' ' ) );
- var config = this.model.get( 'config' ),
- $statsTable = this.$el.find( '.stats-display' ),
- xLabel = config.xLabel, yLabel = config.yLabel,
- $table = $( '' ).addClass( 'table' )
- .append([ '', xLabel, ' ', yLabel, ' ' ].join( '' ))
- .append( _.map( stats, function( stat, key ){
- return $([ '', key, ' ', stat[0], ' ', stat[1], ' ' ].join( '' ));
- }));
- $statsTable.empty().append( $table );
+ var config = this.model.get("config"),
+ $statsTable = this.$el.find(".stats-display"),
+ xLabel = config.xLabel,
+ yLabel = config.yLabel,
+ $table = $("")
+ .addClass("table")
+ .append(["", xLabel, " ", yLabel, " "].join(""))
+ .append(
+ _.map(stats, function(stat, key) {
+ return $(["", key, " ", stat[0], " ", stat[1], " "].join(""));
+ })
+ );
+ $statsTable.empty().append($table);
},
- toggleStats : function( showStats ){
- var $statsDisplay = this.$el.find( '.stats-display' );
- showStats = ( showStats === undefined )?( $statsDisplay.is( ':hidden' ) ):( showStats );
- if( showStats ){
- this.$el.find( 'svg' ).hide();
+ toggleStats: function(showStats) {
+ var $statsDisplay = this.$el.find(".stats-display");
+ showStats = showStats === undefined ? $statsDisplay.is(":hidden") : showStats;
+ if (showStats) {
+ this.$el.find("svg").hide();
$statsDisplay.show();
- this.$el.find( '.controls .stats-toggle-btn' ).text( 'Plot' );
+ this.$el.find(".controls .stats-toggle-btn").text("Plot");
} else {
$statsDisplay.hide();
- this.$el.find( 'svg' ).show();
- this.$el.find( '.controls .stats-toggle-btn' ).text( 'Stats' );
+ this.$el.find("svg").show();
+ this.$el.find(".controls .stats-toggle-btn").text("Stats");
}
},
- toString : function(){
- return 'ScatterplotView()';
+ toString: function() {
+ return "ScatterplotView()";
}
});
var ScatterplotModel = VisualizationModel.Visualization.extend({
+ defaults: {
+ type: "scatterplot",
- defaults : {
- type : 'scatterplot',
-
- config : {
+ config: {
// shouldn't be needed for properly saved splots - also often incorrect
//xColumn : 0,
//yColumn : 1,
-
- pagination : {
- currPage : 0,
- perPage : 3000
+
+ pagination: {
+ currPage: 0,
+ perPage: 3000
},
// graph style
- width : 400,
- height : 400,
+ width: 400,
+ height: 400,
- margin : {
- top : 16,
- right : 16,
- bottom : 40,
- left : 54
+ margin: {
+ top: 16,
+ right: 16,
+ bottom: 40,
+ left: 54
},
- xTicks : 10,
- xLabel : 'X',
- yTicks : 10,
- yLabel : 'Y',
+ xTicks: 10,
+ xLabel: "X",
+ yTicks: 10,
+ yLabel: "Y",
- datapointSize : 4,
- animDuration : 500,
+ datapointSize: 4,
+ animDuration: 500,
- scale : 1,
- translate : [ 0, 0 ]
+ scale: 1,
+ translate: [0, 0]
}
}
});
diff --git a/config/plugins/visualizations/scatterplot/webpack.config.js b/config/plugins/visualizations/scatterplot/webpack.config.js
index 37ba2628082..40cab463bec 100644
--- a/config/plugins/visualizations/scatterplot/webpack.config.js
+++ b/config/plugins/visualizations/scatterplot/webpack.config.js
@@ -1,7 +1,7 @@
var path = require("path");
var croot = path.resolve(__dirname, "../../../../");
var webpack = require("webpack");
-var lroot = path.resolve(croot, "client/galaxy/scripts/libs");
+var lroot = path.resolve(croot, "client/galaxy/scripts/libs");
module.exports = {
entry: path.resolve(__dirname, "./src/scatterplot.js"),
@@ -10,10 +10,7 @@ module.exports = {
filename: "scatterplot.bundle.js"
},
resolve: {
- modules: [
- path.resolve(croot, "client/galaxy/scripts"),
- path.resolve(croot, "client/node_modules")
- ],
+ modules: [path.resolve(croot, "client/galaxy/scripts"), path.resolve(croot, "client/node_modules")],
alias: {
jquery: path.join(lroot, "jquery/jquery"),
underscore: path.join(lroot, "underscore.js"),
@@ -33,7 +30,7 @@ module.exports = {
new webpack.ProvidePlugin({
$: "jquery",
_: "underscore",
- Backbone: "backbone",
+ Backbone: "backbone"
})
]
};