1 new commit in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/changeset/4eaa644dd478/ changeset: 4eaa644dd478 user: carlfeberhard date: 2012-11-28 16:45:01 summary: scatterplot: add header discovery from first dataset line; fix animation; better hover info; fix column selection; pack scripts affected #: 15 files diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa lib/galaxy/webapps/galaxy/api/datasets.py --- a/lib/galaxy/webapps/galaxy/api/datasets.py +++ b/lib/galaxy/webapps/galaxy/api/datasets.py @@ -187,7 +187,6 @@ when the dataset is not yet indexed and hence using data would be slow because indexes need to be created. """ - # Dataset check. msg = self.check_dataset_state( trans, dataset ) if msg: diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa lib/galaxy/webapps/galaxy/controllers/history.py --- a/lib/galaxy/webapps/galaxy/controllers/history.py +++ b/lib/galaxy/webapps/galaxy/controllers/history.py @@ -1322,4 +1322,3 @@ def get_item( self, trans, id ): return self.get_history( trans, id ) - diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa lib/galaxy/webapps/galaxy/controllers/visualization.py --- a/lib/galaxy/webapps/galaxy/controllers/visualization.py +++ b/lib/galaxy/webapps/galaxy/controllers/visualization.py @@ -793,9 +793,23 @@ hda = self.get_dataset( trans, dataset_id, check_ownership=False, check_accessible=True ) hda_dict = hda.get_api_value() hda_dict[ 'id' ] = dataset_id - if( hda_dict[ 'metadata_column_names' ] == None - and hasattr( hda.datatype, 'column_names' ) ): - hda_dict[ 'metadata_column_names' ] = hda.datatype.column_names + + if( ( hda_dict[ 'metadata_column_names' ] == None ) + and ( hasattr( hda.datatype, 'column_names' ) ) ): + hda_dict[ 'metadata_column_names' ] = hda.datatype.column_names + + # try to get the first line (assuming it's a header) + #TODO: doesn't belong here + try: + with open( hda.file_name ) as infile: + for index, line in enumerate( infile ): + if 'comment_lines' not in hda_dict: + hda_dict[ 'comment_lines' ] = [] + hda_dict[ 'comment_lines' ].append( line ) + if index >= 3: + break + except Exception, exc: + log.error( str( exc ) ) history_id = trans.security.encode_id( hda.history.id ) diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa static/scripts/mvc/visualizations/scatterplotControlForm.js --- a/static/scripts/mvc/visualizations/scatterplotControlForm.js +++ b/static/scripts/mvc/visualizations/scatterplotControlForm.js @@ -67,8 +67,9 @@ //logger : console, className : 'scatterplot-control-form', - dataLoadDelay : 500, - dataLoadSize : 3001, + //NOTE: should include time needed to render + dataLoadDelay : 4000, + dataLoadSize : 5000, loadingIndicatorImage : 'loading_small_white_bg.gif', fetchMsg : 'Fetching data...', @@ -107,6 +108,16 @@ } this.log( '\t dataset:', this.dataset ); + // attempt to get possible headers from the data's first line + if( this.dataset.comment_lines && this.dataset.comment_lines.length ){ + //TODO:?? + var firstLine = this.dataset.comment_lines[0], + possibleHeaders = firstLine.split( '\t' ); + if( possibleHeaders.length === this.dataset.metadata_column_types.length ){ + this.possibleHeaders = possibleHeaders; + } + } + // passed from mako helper //TODO: integrate to galaxyPaths //TODO: ?? seems like data loader section would be better @@ -194,16 +205,24 @@ // controls for which columns are used to plot datapoints (and ids/additional info to attach if desired) var view = this, allColumns = [], - numericColumns = []; - + numericColumns = [], + usePossibleHeaders = ( this.possibleHeaders && this.$dataControl )? + ( this.$dataControl.find( '#first-line-header-checkbox' ).is( ':checked' ) ):( false ); + // gather column indeces (from metadata_column_types) and names (from metadata_columnnames) _.each( this.dataset.metadata_column_types, function( type, index ){ // use a 1 based index in names/values within the form (will be dec. when parsed out) var oneBasedIndex = index + 1, - // label with the name if available (fall back on 'column <index>') + // default name is 'column <index>'... name = 'column ' + oneBasedIndex; + + // ...but label with the name if available... if( view.dataset.metadata_column_names ){ name = view.dataset.metadata_column_names[ index ]; + + // ...or, use the first line as headers if the user wants + } else if( usePossibleHeaders ){ + name = view.possibleHeaders[ index ]; } // cache all columns here @@ -218,11 +237,17 @@ // render the html var $dataControl = this.$el.find( '.tab-pane#data-control' ); - $dataControl.append( ScatterplotControlForm.templates.dataControl({ + $dataControl.html( ScatterplotControlForm.templates.dataControl({ allColumns : allColumns, - numericColumns : numericColumns + numericColumns : numericColumns, + possibleHeaders : ( this.possibleHeaders )?( this.possibleHeaders.join( ', ' ) ):( '' ), + usePossibleHeaders : usePossibleHeaders })); + if( !this.dataset.metadata_column_names && this.possibleHeaders ){ + $dataControl.find( '#first-line-header' ).show(); + } + // preset to column selectors if they were passed in the config in the query string $dataControl.find( '#X-select' ).val( this.chartConfig.xColumn ); $dataControl.find( '#Y-select' ).val( this.chartConfig.yColumn ); @@ -288,6 +313,7 @@ // ------------------------------------------------------------------------- EVENTS events : { 'change #include-id-checkbox' : 'toggleThirdColumnSelector', + 'change #first-line-header-checkbox' : 'rerenderDataControl', 'click #data-control #render-button' : 'renderChart', 'click #chart-control #render-button' : 'changeChartSettings' }, @@ -296,6 +322,10 @@ // show/hide the id selector on the data settings panel this.$el.find( 'select[name="ID"]' ).parent().toggle(); }, + + rerenderDataControl : function(){ + this.$dataControl = this._render_dataControl(); + }, showLoadingIndicator : function( message, callback ){ // display the loading indicator over the tab panels if hidden, update message (if passed) @@ -577,10 +607,8 @@ settings.yLabel = ( chartSettingsYLabel === 'Y' )? ( colSelections.Y.colName ):( chartSettingsYLabel ); - settings.animDuration = 10; - if( this.$chartControl.find( '#animDuration.checkbox-input' ).is( ':checked' ) ){ - settings.animDuration = 500; - } + settings.animDuration = ( this.$chartControl.find( '#animate-chart' ).is( ':checked' ) )? + ( this.chart.defaults.animDuration ):( 0 ); this.log( '\t chartSettings:', settings ); return settings; diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa static/scripts/packed/mvc/visualizations/scatterplotControlForm.js --- a/static/scripts/packed/mvc/visualizations/scatterplotControlForm.js +++ b/static/scripts/packed/mvc/visualizations/scatterplotControlForm.js @@ -1,1 +1,1 @@ -var ScatterplotControlForm=BaseView.extend(LoggableMixin).extend({className:"scatterplot-control-form",dataLoadDelay:500,dataLoadSize:3001,loadingIndicatorImage:"loading_small_white_bg.gif",fetchMsg:"Fetching data...",renderMsg:"Rendering...",initialize:function(a){this.log(this+".initialize, attributes:",a);this.dataset=null;this.chartConfig=null;this.chart=null;this.loader=null;this.$dataControl=null;this.$chartControl=null;this.$statsDisplay=null;this.$chartDisplay=null;this.dataFetch=null;this.initializeFromAttributes(a);this.initializeChart(a);this.initializeDataLoader(a)},initializeFromAttributes:function(a){if(!a||!a.dataset){throw ("ScatterplotView requires a dataset")}else{this.dataset=a.dataset}if(jQuery.type(this.dataset.metadata_column_types)==="string"){this.dataset.metadata_column_types=this.dataset.metadata_column_types.split(", ")}this.log("\t dataset:",this.dataset);if(!a.apiDatasetsURL){throw ("ScatterplotView requires a apiDatasetsURL")}else{this.dataURL=a.apiDatasetsURL+"/"+this.dataset.id+"?"}this.log("\t dataURL:",this.dataURL)},initializeChart:function(a){this.chartConfig=a.chartConfig||{};this.log("\t initial chartConfig:",this.chartConfig);this.chart=new TwoVarScatterplot(this.chartConfig);this.chartConfig=this.chart.config},initializeDataLoader:function(b){var a=this;this.loader=new LazyDataLoader({url:null,start:b.start||0,total:b.total||this.dataset.metadata_data_lines,delay:this.dataLoadDelay,size:this.dataLoadSize,buildUrl:function(d,c){return this.url+"&"+jQuery.param({start_val:d,max_vals:c})}});$(this.loader).bind("error",function(e,c,d){a.log("ERROR:",c,d);alert("ERROR fetching data:\n"+c+"\n"+d);a.hideLoadingIndicator()})},render:function(){this.log(this+".render");this.$el.append(ScatterplotControlForm.templates.mainLayout({loadingIndicatorImagePath:galaxy_paths.get("image_path")+"/"+this.loadingIndicatorImage,message:""}));this.$dataControl=this._render_dataControl();this.$chartControl=this._render_chartControl();this.$statsDisplay=this.$el.find(".tab-pane#stats-display");this.$chartDisplay=this._render_chartDisplay();if(this.chartConfig.xColumn&&this.chartConfig.yColumn){this.renderChart()}this.$el.find(".tooltip").tooltip();return this},_render_dataControl:function(){var b=this,a=[],d=[];_.each(this.dataset.metadata_column_types,function(h,f){var g=f+1,e="column "+g;if(b.dataset.metadata_column_names){e=b.dataset.metadata_column_names[f]}a.push({index:g,name:e});if(h==="int"||h==="float"){d.push({index:g,name:e})}});var c=this.$el.find(".tab-pane#data-control");c.append(ScatterplotControlForm.templates.dataControl({allColumns:a,numericColumns:d}));c.find("#X-select").val(this.chartConfig.xColumn);c.find("#Y-select").val(this.chartConfig.yColumn);if(this.chartConfig.idColumn!==undefined){c.find("#include-id-checkbox").attr("checked",true).trigger("change");c.find("#ID-select").val(this.chartConfig.idColumn)}return c},_render_chartControl:function(){var a=this,b=this.$el.find(".tab-pane#chart-control"),c={datapointSize:{min:2,max:10,step:1},width:{min:200,max:800,step:20},height:{min:200,max:800,step:20}};b.append(ScatterplotControlForm.templates.chartControl(this.chartConfig));b.find(".numeric-slider-input").each(function(){var f=$(this),e=f.find(".slider-output"),g=f.find(".slider"),h=f.attr("id");function d(){var j=$(this),i=j.slider("value");e.text(i)}g.slider(_.extend(c[h],{value:a.chartConfig[h],change:d,slide:d}))});return b},_render_chartDisplay:function(){var a=this.$el.find(".tab-pane#chart-display");a.append(ScatterplotControlForm.templates.chartDisplay(this.chartConfig));return a},events:{"change #include-id-checkbox":"toggleThirdColumnSelector","click #data-control #render-button":"renderChart","click #chart-control #render-button":"changeChartSettings"},toggleThirdColumnSelector:function(){this.$el.find('select[name="ID"]').parent().toggle()},showLoadingIndicator:function(b,c){b=b||"";var a=this.$el.find("div#loading-indicator");messageBox=a.find(".loading-message");if(a.is(":visible")){if(b){messageBox.fadeOut("fast",function(){messageBox.text(b);messageBox.fadeIn("fast",c)})}else{c()}}else{if(b){messageBox.text(b)}a.fadeIn("fast",c)}},hideLoadingIndicator:function(a){this.$el.find("div#loading-indicator").fadeOut("fast",a)},renderChart:function(){this.log(this+".renderChart");this.data=null;this.meta=null;_.extend(this.chartConfig,this.getChartSettings());this.log("\t chartConfig:",this.chartConfig);this.chart.updateConfig(this.chartConfig,false);this.loader.url=this.dataURL+"&"+jQuery.param(this.getDataSettings());this.log("\t loader: total lines:",this.loader.total," url:",this.loader.url);var a=this;$(this.loader).bind("loaded.new",function(c,b){a.log(a+" loaded.new",b);a.postProcessDataFetchResponse(b);a.log("\t postprocessed data:",a.data);a.log("\t postprocessed meta:",a.meta);a.showLoadingIndicator(a.renderMsg,function(){a.chart.render(a.data,a.meta);a.renderStats(a.data,a.meta);a.hideLoadingIndicator()})});$(this.loader).bind("complete",function(b,c){a.log(a+" complete",c);$(a.loader).unbind()});a.showLoadingIndicator(a.fetchMsg,function(){a.$el.find("ul.nav").find('a[href="#chart-display"]').tab("show");a.loader.load()})},renderStats:function(){this.log(this+".renderStats");this.$statsDisplay.html(ScatterplotControlForm.templates.statsDisplay({stats:[{name:"Count",xval:this.meta[0].count,yval:this.meta[1].count},{name:"Min",xval:this.meta[0].min,yval:this.meta[1].min},{name:"Max",xval:this.meta[0].max,yval:this.meta[1].max},{name:"Sum",xval:this.meta[0].sum,yval:this.meta[1].sum},{name:"Mean",xval:this.meta[0].mean,yval:this.meta[1].mean},{name:"Median",xval:this.meta[0].median,yval:this.meta[1].median}]}))},changeChartSettings:function(){var a=this;newChartSettings=this.getChartSettings();_.extend(this.chartConfig,newChartSettings);this.log("this.chartConfig:",this.chartConfig);this.chart.updateConfig(this.chartConfig,false);if(a.data&&a.meta){a.showLoadingIndicator(a.renderMsg,function(){a.$el.find("ul.nav").find('a[href="#chart-display"]').tab("show");a.chart.render(a.data,a.meta);a.hideLoadingIndicator()})}else{this.renderChart()}},postProcessDataFetchResponse:function(a){this.postProcessData(a.data);this.postProcessMeta(a.meta)},postProcessData:function(b){var a=this;if(a.data){_.each(b,function(d,c){a.data[c]=a.data[c].concat(d)})}else{a.data=b}},postProcessMeta:function(c){var a=this,b=this.dataset.metadata_column_types;if(a.meta){_.each(c,function(e,d){var i=a.meta[d],g=b[d];i.count+=(e.count)?(e.count):(0);if((g==="int")||(g==="float")){i.min=Math.min(e.min,i.min);i.max=Math.max(e.max,i.max);i.sum=e.sum+i.sum;i.mean=(i.count)?(i.sum/i.count):(null);var f=a.data[d].slice().sort(),h=Math.floor(f.length/2);if(f.length%2===0){i.median=((f[h]+f[(h+1)])/2)}else{i.median=f[h]}}})}else{a.meta=c}},getDataSettings:function(){var b=this.getColumnSelections(),a=[];this.log("\t columnSelections:",b);a=[b.X.colIndex-1,b.Y.colIndex-1];if(this.$dataControl.find("#include-id-checkbox").attr("checked")){a.push(b.ID.colIndex-1)}var c={data_type:"raw_data",columns:"["+a+"]"};this.log("\t data settings (url params):",c);return c},getColumnSelections:function(){var a={};this.$dataControl.find("div.column-select select").each(function(){var b=$(this),c=b.val();a[b.attr("name")]={colIndex:c,colName:b.children('[value="'+c+'"]').text()}});return a},getChartSettings:function(){var c={},d=this.getColumnSelections();c.datapointSize=this.$chartControl.find("#datapointSize.numeric-slider-input").find(".slider").slider("value");c.width=this.$chartControl.find("#width.numeric-slider-input").find(".slider").slider("value");c.height=this.$chartControl.find("#height.numeric-slider-input").find(".slider").slider("value");var b=this.$chartControl.find("input#X-axis-label").val(),a=this.$chartControl.find("input#Y-axis-label").val();c.xLabel=(b==="X")?(d.X.colName):(b);c.yLabel=(a==="Y")?(d.Y.colName):(a);c.animDuration=10;if(this.$chartControl.find("#animDuration.checkbox-input").is(":checked")){c.animDuration=500}this.log("\t chartSettings:",c);return c},toString:function(){return"ScatterplotControlForm("+((this.dataset)?(this.dataset.id):(""))+")"}});ScatterplotControlForm.templates={mainLayout:Handlebars.templates["template-visualization-scatterplotControlForm"],dataControl:Handlebars.templates["template-visualization-dataControl"],chartControl:Handlebars.templates["template-visualization-chartControl"],statsDisplay:Handlebars.templates["template-visualization-statsDisplay"],chartDisplay:Handlebars.templates["template-visualization-chartDisplay"]}; \ No newline at end of file +var ScatterplotControlForm=BaseView.extend(LoggableMixin).extend({className:"scatterplot-control-form",dataLoadDelay:4000,dataLoadSize:5000,loadingIndicatorImage:"loading_small_white_bg.gif",fetchMsg:"Fetching data...",renderMsg:"Rendering...",initialize:function(a){this.log(this+".initialize, attributes:",a);this.dataset=null;this.chartConfig=null;this.chart=null;this.loader=null;this.$dataControl=null;this.$chartControl=null;this.$statsDisplay=null;this.$chartDisplay=null;this.dataFetch=null;this.initializeFromAttributes(a);this.initializeChart(a);this.initializeDataLoader(a)},initializeFromAttributes:function(a){if(!a||!a.dataset){throw ("ScatterplotView requires a dataset")}else{this.dataset=a.dataset}if(jQuery.type(this.dataset.metadata_column_types)==="string"){this.dataset.metadata_column_types=this.dataset.metadata_column_types.split(", ")}this.log("\t dataset:",this.dataset);if(this.dataset.comment_lines&&this.dataset.comment_lines.length){var b=this.dataset.comment_lines[0],c=b.split("\t");if(c.length===this.dataset.metadata_column_types.length){this.possibleHeaders=c}}if(!a.apiDatasetsURL){throw ("ScatterplotView requires a apiDatasetsURL")}else{this.dataURL=a.apiDatasetsURL+"/"+this.dataset.id+"?"}this.log("\t dataURL:",this.dataURL)},initializeChart:function(a){this.chartConfig=a.chartConfig||{};this.log("\t initial chartConfig:",this.chartConfig);this.chart=new TwoVarScatterplot(this.chartConfig);this.chartConfig=this.chart.config},initializeDataLoader:function(b){var a=this;this.loader=new LazyDataLoader({url:null,start:b.start||0,total:b.total||this.dataset.metadata_data_lines,delay:this.dataLoadDelay,size:this.dataLoadSize,buildUrl:function(d,c){return this.url+"&"+jQuery.param({start_val:d,max_vals:c})}});$(this.loader).bind("error",function(e,c,d){a.log("ERROR:",c,d);alert("ERROR fetching data:\n"+c+"\n"+d);a.hideLoadingIndicator()})},render:function(){this.log(this+".render");this.$el.append(ScatterplotControlForm.templates.mainLayout({loadingIndicatorImagePath:galaxy_paths.get("image_path")+"/"+this.loadingIndicatorImage,message:""}));this.$dataControl=this._render_dataControl();this.$chartControl=this._render_chartControl();this.$statsDisplay=this.$el.find(".tab-pane#stats-display");this.$chartDisplay=this._render_chartDisplay();if(this.chartConfig.xColumn&&this.chartConfig.yColumn){this.renderChart()}this.$el.find(".tooltip").tooltip();return this},_render_dataControl:function(){var b=this,a=[],e=[],c=(this.possibleHeaders&&this.$dataControl)?(this.$dataControl.find("#first-line-header-checkbox").is(":checked")):(false);_.each(this.dataset.metadata_column_types,function(i,g){var h=g+1,f="column "+h;if(b.dataset.metadata_column_names){f=b.dataset.metadata_column_names[g]}else{if(c){f=b.possibleHeaders[g]}}a.push({index:h,name:f});if(i==="int"||i==="float"){e.push({index:h,name:f})}});var d=this.$el.find(".tab-pane#data-control");d.html(ScatterplotControlForm.templates.dataControl({allColumns:a,numericColumns:e,possibleHeaders:(this.possibleHeaders)?(this.possibleHeaders.join(", ")):(""),usePossibleHeaders:c}));if(!this.dataset.metadata_column_names&&this.possibleHeaders){d.find("#first-line-header").show()}d.find("#X-select").val(this.chartConfig.xColumn);d.find("#Y-select").val(this.chartConfig.yColumn);if(this.chartConfig.idColumn!==undefined){d.find("#include-id-checkbox").attr("checked",true).trigger("change");d.find("#ID-select").val(this.chartConfig.idColumn)}return d},_render_chartControl:function(){var a=this,b=this.$el.find(".tab-pane#chart-control"),c={datapointSize:{min:2,max:10,step:1},width:{min:200,max:800,step:20},height:{min:200,max:800,step:20}};b.append(ScatterplotControlForm.templates.chartControl(this.chartConfig));b.find(".numeric-slider-input").each(function(){var f=$(this),e=f.find(".slider-output"),g=f.find(".slider"),h=f.attr("id");function d(){var j=$(this),i=j.slider("value");e.text(i)}g.slider(_.extend(c[h],{value:a.chartConfig[h],change:d,slide:d}))});return b},_render_chartDisplay:function(){var a=this.$el.find(".tab-pane#chart-display");a.append(ScatterplotControlForm.templates.chartDisplay(this.chartConfig));return a},events:{"change #include-id-checkbox":"toggleThirdColumnSelector","change #first-line-header-checkbox":"rerenderDataControl","click #data-control #render-button":"renderChart","click #chart-control #render-button":"changeChartSettings"},toggleThirdColumnSelector:function(){this.$el.find('select[name="ID"]').parent().toggle()},rerenderDataControl:function(){this.$dataControl=this._render_dataControl()},showLoadingIndicator:function(b,c){b=b||"";var a=this.$el.find("div#loading-indicator");messageBox=a.find(".loading-message");if(a.is(":visible")){if(b){messageBox.fadeOut("fast",function(){messageBox.text(b);messageBox.fadeIn("fast",c)})}else{c()}}else{if(b){messageBox.text(b)}a.fadeIn("fast",c)}},hideLoadingIndicator:function(a){this.$el.find("div#loading-indicator").fadeOut("fast",a)},renderChart:function(){this.log(this+".renderChart");this.data=null;this.meta=null;_.extend(this.chartConfig,this.getChartSettings());this.log("\t chartConfig:",this.chartConfig);this.chart.updateConfig(this.chartConfig,false);this.loader.url=this.dataURL+"&"+jQuery.param(this.getDataSettings());this.log("\t loader: total lines:",this.loader.total," url:",this.loader.url);var a=this;$(this.loader).bind("loaded.new",function(c,b){a.log(a+" loaded.new",b);a.postProcessDataFetchResponse(b);a.log("\t postprocessed data:",a.data);a.log("\t postprocessed meta:",a.meta);a.showLoadingIndicator(a.renderMsg,function(){a.chart.render(a.data,a.meta);a.renderStats(a.data,a.meta);a.hideLoadingIndicator()})});$(this.loader).bind("complete",function(b,c){a.log(a+" complete",c);$(a.loader).unbind()});a.showLoadingIndicator(a.fetchMsg,function(){a.$el.find("ul.nav").find('a[href="#chart-display"]').tab("show");a.loader.load()})},renderStats:function(){this.log(this+".renderStats");this.$statsDisplay.html(ScatterplotControlForm.templates.statsDisplay({stats:[{name:"Count",xval:this.meta[0].count,yval:this.meta[1].count},{name:"Min",xval:this.meta[0].min,yval:this.meta[1].min},{name:"Max",xval:this.meta[0].max,yval:this.meta[1].max},{name:"Sum",xval:this.meta[0].sum,yval:this.meta[1].sum},{name:"Mean",xval:this.meta[0].mean,yval:this.meta[1].mean},{name:"Median",xval:this.meta[0].median,yval:this.meta[1].median}]}))},changeChartSettings:function(){var a=this;newChartSettings=this.getChartSettings();_.extend(this.chartConfig,newChartSettings);this.log("this.chartConfig:",this.chartConfig);this.chart.updateConfig(this.chartConfig,false);if(a.data&&a.meta){a.showLoadingIndicator(a.renderMsg,function(){a.$el.find("ul.nav").find('a[href="#chart-display"]').tab("show");a.chart.render(a.data,a.meta);a.hideLoadingIndicator()})}else{this.renderChart()}},postProcessDataFetchResponse:function(a){this.postProcessData(a.data);this.postProcessMeta(a.meta)},postProcessData:function(b){var a=this;if(a.data){_.each(b,function(d,c){a.data[c]=a.data[c].concat(d)})}else{a.data=b}},postProcessMeta:function(c){var a=this,b=this.dataset.metadata_column_types;if(a.meta){_.each(c,function(e,d){var i=a.meta[d],g=b[d];i.count+=(e.count)?(e.count):(0);if((g==="int")||(g==="float")){i.min=Math.min(e.min,i.min);i.max=Math.max(e.max,i.max);i.sum=e.sum+i.sum;i.mean=(i.count)?(i.sum/i.count):(null);var f=a.data[d].slice().sort(),h=Math.floor(f.length/2);if(f.length%2===0){i.median=((f[h]+f[(h+1)])/2)}else{i.median=f[h]}}})}else{a.meta=c}},getDataSettings:function(){var b=this.getColumnSelections(),a=[];this.log("\t columnSelections:",b);a=[b.X.colIndex-1,b.Y.colIndex-1];if(this.$dataControl.find("#include-id-checkbox").attr("checked")){a.push(b.ID.colIndex-1)}var c={data_type:"raw_data",columns:"["+a+"]"};this.log("\t data settings (url params):",c);return c},getColumnSelections:function(){var a={};this.$dataControl.find("div.column-select select").each(function(){var b=$(this),c=b.val();a[b.attr("name")]={colIndex:c,colName:b.children('[value="'+c+'"]').text()}});return a},getChartSettings:function(){var c={},d=this.getColumnSelections();c.datapointSize=this.$chartControl.find("#datapointSize.numeric-slider-input").find(".slider").slider("value");c.width=this.$chartControl.find("#width.numeric-slider-input").find(".slider").slider("value");c.height=this.$chartControl.find("#height.numeric-slider-input").find(".slider").slider("value");var b=this.$chartControl.find("input#X-axis-label").val(),a=this.$chartControl.find("input#Y-axis-label").val();c.xLabel=(b==="X")?(d.X.colName):(b);c.yLabel=(a==="Y")?(d.Y.colName):(a);c.animDuration=(this.$chartControl.find("#animate-chart").is(":checked"))?(this.chart.defaults.animDuration):(0);this.log("\t chartSettings:",c);return c},toString:function(){return"ScatterplotControlForm("+((this.dataset)?(this.dataset.id):(""))+")"}});ScatterplotControlForm.templates={mainLayout:Handlebars.templates["template-visualization-scatterplotControlForm"],dataControl:Handlebars.templates["template-visualization-dataControl"],chartControl:Handlebars.templates["template-visualization-chartControl"],statsDisplay:Handlebars.templates["template-visualization-statsDisplay"],chartDisplay:Handlebars.templates["template-visualization-chartDisplay"]}; \ No newline at end of file diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa static/scripts/packed/templates/compiled/template-visualization-chartControl.js --- a/static/scripts/packed/templates/compiled/template-visualization-chartControl.js +++ b/static/scripts/packed/templates/compiled/template-visualization-chartControl.js @@ -1,1 +1,1 @@ -(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-visualization-chartControl"]=b(function(f,m,e,l,k){e=e||f.helpers;var i="",c,h,g="function",j=this.escapeExpression,n=this;function d(p,o){return' checked="true"'}i+='<p class="help-text">\n Use the following controls to how the chart is displayed.\n The slide controls can be moved by the mouse or, if the \'handle\' is in focus, your keyboard\'s arrow keys.\n Move the focus between controls by using the tab or shift+tab keys on your keyboard.\n Use the \'Draw\' button to render (or re-render) the chart with the current settings.\n </p>\n\n <div id="datapointSize" class="form-input numeric-slider-input">\n <label for="datapointSize">Size of data point: </label>\n <div class="slider-output">';h=e.datapointSize;if(h){c=h.call(m,{hash:{}})}else{c=m.datapointSize;c=typeof c===g?c():c}i+=j(c)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n Size of the graphic representation of each data point\n </p>\n </div>\n\n <div id="animDuration" class="form-input checkbox-input">\n <label for="animated">Animate graph transitions?: </label>\n <input type="checkbox" id="animated"\n class="checkbox control"';c=m.animDuration;c=e["if"].call(m,c,{hash:{},inverse:n.noop,fn:n.program(1,d,k)});if(c||c===0){i+=c}i+=' />\n <p class="form-help help-text-small">\n Uncheck this to disable the animations used on the graph\n </p>\n </div>\n\n <div id="width" class="form-input numeric-slider-input">\n <label for="width">Graph width: </label>\n <div class="slider-output">';h=e.width;if(h){c=h.call(m,{hash:{}})}else{c=m.width;c=typeof c===g?c():c}i+=j(c)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n (not including graph margins and axes)\n </p>\n </div>\n\n <div id="height" class="form-input numeric-slider-input">\n <label for="height">Graph height: </label>\n <div class="slider-output">';h=e.height;if(h){c=h.call(m,{hash:{}})}else{c=m.height;c=typeof c===g?c():c}i+=j(c)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n (not including graph margins and axes)\n </p>\n </div>\n\n <div id="X-axis-label"class="text-input form-input">\n <label for="X-axis-label">Re-label the X axis: </label>\n <input type="text" name="X-axis-label" id="X-axis-label" value="';h=e.xLabel;if(h){c=h.call(m,{hash:{}})}else{c=m.xLabel;c=typeof c===g?c():c}i+=j(c)+'" />\n <p class="form-help help-text-small"></p>\n </div>\n\n <div id="Y-axis-label" class="text-input form-input">\n <label for="Y-axis-label">Re-label the Y axis: </label>\n <input type="text" name="Y-axis-label" id="Y-axis-label" value="';h=e.yLabel;if(h){c=h.call(m,{hash:{}})}else{c=m.yLabel;c=typeof c===g?c():c}i+=j(c)+'" />\n <p class="form-help help-text-small"></p>\n </div>\n\n <input id="render-button" type="button" value="Draw" />';return i})})(); \ No newline at end of file +(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-visualization-chartControl"]=b(function(f,m,e,l,k){e=e||f.helpers;var i="",c,h,g="function",j=this.escapeExpression,n=this;function d(p,o){return' checked="true"'}i+='<p class="help-text">\n Use the following controls to how the chart is displayed.\n The slide controls can be moved by the mouse or, if the \'handle\' is in focus, your keyboard\'s arrow keys.\n Move the focus between controls by using the tab or shift+tab keys on your keyboard.\n Use the \'Draw\' button to render (or re-render) the chart with the current settings.\n </p>\n\n <div id="datapointSize" class="form-input numeric-slider-input">\n <label for="datapointSize">Size of data point: </label>\n <div class="slider-output">';h=e.datapointSize;if(h){c=h.call(m,{hash:{}})}else{c=m.datapointSize;c=typeof c===g?c():c}i+=j(c)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n Size of the graphic representation of each data point\n </p>\n </div>\n\n <div id="animDuration" class="form-input checkbox-input">\n <label for="animate-chart">Animate chart transitions?: </label>\n <input type="checkbox" id="animate-chart"\n class="checkbox control"';c=m.animDuration;c=e["if"].call(m,c,{hash:{},inverse:n.noop,fn:n.program(1,d,k)});if(c||c===0){i+=c}i+=' />\n <p class="form-help help-text-small">\n Uncheck this to disable the animations used on the chart\n </p>\n </div>\n\n <div id="width" class="form-input numeric-slider-input">\n <label for="width">Chart width: </label>\n <div class="slider-output">';h=e.width;if(h){c=h.call(m,{hash:{}})}else{c=m.width;c=typeof c===g?c():c}i+=j(c)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n (not including chart margins and axes)\n </p>\n </div>\n\n <div id="height" class="form-input numeric-slider-input">\n <label for="height">Chart height: </label>\n <div class="slider-output">';h=e.height;if(h){c=h.call(m,{hash:{}})}else{c=m.height;c=typeof c===g?c():c}i+=j(c)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n (not including chart margins and axes)\n </p>\n </div>\n\n <div id="X-axis-label"class="text-input form-input">\n <label for="X-axis-label">Re-label the X axis: </label>\n <input type="text" name="X-axis-label" id="X-axis-label" value="';h=e.xLabel;if(h){c=h.call(m,{hash:{}})}else{c=m.xLabel;c=typeof c===g?c():c}i+=j(c)+'" />\n <p class="form-help help-text-small"></p>\n </div>\n\n <div id="Y-axis-label" class="text-input form-input">\n <label for="Y-axis-label">Re-label the Y axis: </label>\n <input type="text" name="Y-axis-label" id="Y-axis-label" value="';h=e.yLabel;if(h){c=h.call(m,{hash:{}})}else{c=m.yLabel;c=typeof c===g?c():c}i+=j(c)+'" />\n <p class="form-help help-text-small"></p>\n </div>\n\n <input id="render-button" type="button" value="Draw" />';return i})})(); \ No newline at end of file diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa static/scripts/packed/templates/compiled/template-visualization-dataControl.js --- a/static/scripts/packed/templates/compiled/template-visualization-dataControl.js +++ b/static/scripts/packed/templates/compiled/template-visualization-dataControl.js @@ -1,1 +1,1 @@ -(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-visualization-dataControl"]=b(function(g,m,f,l,k){f=f||g.helpers;var i="",d,h="function",j=this.escapeExpression,o=this;function e(t,s){var q="",r,p;q+='\n <option value="';p=f.index;if(p){r=p.call(t,{hash:{}})}else{r=t.index;r=typeof r===h?r():r}q+=j(r)+'">';p=f.name;if(p){r=p.call(t,{hash:{}})}else{r=t.name;r=typeof r===h?r():r}q+=j(r)+"</option>\n ";return q}function c(t,s){var q="",r,p;q+='\n <option value="';p=f.index;if(p){r=p.call(t,{hash:{}})}else{r=t.index;r=typeof r===h?r():r}q+=j(r)+'">';p=f.name;if(p){r=p.call(t,{hash:{}})}else{r=t.name;r=typeof r===h?r():r}q+=j(r)+"</option>\n ";return q}function n(t,s){var q="",r,p;q+='\n <option value="';p=f.index;if(p){r=p.call(t,{hash:{}})}else{r=t.index;r=typeof r===h?r():r}q+=j(r)+'">';p=f.name;if(p){r=p.call(t,{hash:{}})}else{r=t.name;r=typeof r===h?r():r}q+=j(r)+"</option>\n ";return q}i+="<p class=\"help-text\">\n Use the following controls to change the data used by the chart.\n Use the 'Draw' button to render (or re-render) the chart with the current settings.\n </p>\n\n ";i+='\n <div class="column-select">\n <label for="X-select">Data column for X: </label>\n <select name="X" id="X-select">\n ';d=m.numericColumns;d=f.each.call(m,d,{hash:{},inverse:o.noop,fn:o.program(1,e,k)});if(d||d===0){i+=d}i+='\n </select>\n </div>\n <div class="column-select">\n <label for="Y-select">Data column for Y: </label>\n <select name="Y" id="Y-select">\n ';d=m.numericColumns;d=f.each.call(m,d,{hash:{},inverse:o.noop,fn:o.program(3,c,k)});if(d||d===0){i+=d}i+="\n </select>\n </div>\n\n ";i+='\n <div id="include-id">\n <label for="include-id-checkbox">Include a third column as data point IDs?</label>\n <input type="checkbox" name="include-id" id="include-id-checkbox" />\n <p class="help-text-small">\n These will be displayed (along with the x and y values) when you hover over\n a data point.\n </p>\n </div>\n <div class="column-select" style="display: none">\n <label for="ID-select">Data column for IDs: </label>\n <select name="ID" id="ID-select">\n ';d=m.allColumns;d=f.each.call(m,d,{hash:{},inverse:o.noop,fn:o.program(5,n,k)});if(d||d===0){i+=d}i+='\n </select>\n </div>\n\n <input id="render-button" type="button" value="Draw" />\n <div class="clear"></div>';return i})})(); \ No newline at end of file +(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-visualization-dataControl"]=b(function(g,n,f,m,l){f=f||g.helpers;var j="",d,i,h="function",k=this.escapeExpression,q=this;function e(v,u){var s="",t,r;s+='\n <option value="';r=f.index;if(r){t=r.call(v,{hash:{}})}else{t=v.index;t=typeof t===h?t():t}s+=k(t)+'">';r=f.name;if(r){t=r.call(v,{hash:{}})}else{t=v.name;t=typeof t===h?t():t}s+=k(t)+"</option>\n ";return s}function c(v,u){var s="",t,r;s+='\n <option value="';r=f.index;if(r){t=r.call(v,{hash:{}})}else{t=v.index;t=typeof t===h?t():t}s+=k(t)+'">';r=f.name;if(r){t=r.call(v,{hash:{}})}else{t=v.name;t=typeof t===h?t():t}s+=k(t)+"</option>\n ";return s}function p(v,u){var s="",t,r;s+='\n <option value="';r=f.index;if(r){t=r.call(v,{hash:{}})}else{t=v.index;t=typeof t===h?t():t}s+=k(t)+'">';r=f.name;if(r){t=r.call(v,{hash:{}})}else{t=v.name;t=typeof t===h?t():t}s+=k(t)+"</option>\n ";return s}function o(s,r){return'checked="true"'}j+="<p class=\"help-text\">\n Use the following controls to change the data used by the chart.\n Use the 'Draw' button to render (or re-render) the chart with the current settings.\n </p>\n\n ";j+='\n <div class="column-select">\n <label for="X-select">Data column for X: </label>\n <select name="X" id="X-select">\n ';d=n.numericColumns;d=f.each.call(n,d,{hash:{},inverse:q.noop,fn:q.program(1,e,l)});if(d||d===0){j+=d}j+='\n </select>\n </div>\n <div class="column-select">\n <label for="Y-select">Data column for Y: </label>\n <select name="Y" id="Y-select">\n ';d=n.numericColumns;d=f.each.call(n,d,{hash:{},inverse:q.noop,fn:q.program(3,c,l)});if(d||d===0){j+=d}j+="\n </select>\n </div>\n\n ";j+='\n <div id="include-id">\n <label for="include-id-checkbox">Include a third column as data point IDs?</label>\n <input type="checkbox" name="include-id" id="include-id-checkbox" />\n <p class="help-text-small">\n These will be displayed (along with the x and y values) when you hover over\n a data point.\n </p>\n </div>\n <div class="column-select" style="display: none">\n <label for="ID-select">Data column for IDs: </label>\n <select name="ID" id="ID-select">\n ';d=n.allColumns;d=f.each.call(n,d,{hash:{},inverse:q.noop,fn:q.program(5,p,l)});if(d||d===0){j+=d}j+="\n </select>\n </div>\n\n ";j+='\n <div id="first-line-header" style="display: none;">\n <p>Possible headers: ';i=f.possibleHeaders;if(i){d=i.call(n,{hash:{}})}else{d=n.possibleHeaders;d=typeof d===h?d():d}j+=k(d)+'\n </p>\n <label for="first-line-header-checkbox">Use the above as column headers?</label>\n <input type="checkbox" name="include-id" id="first-line-header-checkbox"\n ';d=n.usePossibleHeaders;d=f["if"].call(n,d,{hash:{},inverse:q.noop,fn:q.program(7,o,l)});if(d||d===0){j+=d}j+='/>\n <p class="help-text-small">\n It looks like Galaxy couldn\'t get proper column headers for this data.\n Would you like to use the column headers above as column names to select columns?\n </p>\n </div>\n\n <input id="render-button" type="button" value="Draw" />\n <div class="clear"></div>';return j})})(); \ No newline at end of file diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa static/scripts/packed/utils/LazyDataLoader.js --- a/static/scripts/packed/utils/LazyDataLoader.js +++ b/static/scripts/packed/utils/LazyDataLoader.js @@ -1,1 +1,1 @@ -function LazyDataLoader(c){var a=this,d="loaded.new",b="complete";ERROR_EVENT="error";jQuery.extend(a,LoggableMixin);jQuery.extend(a,{total:undefined,url:undefined,currentIntervalId:undefined,data:[],delay:500,start:0,size:1000,initialize:function(e){jQuery.extend(a,e);if(e.hasOwnProperty("initialize")){e.initialize.call(a,e)}this.log(this+" initialized:",a)},buildUrl:function(f,e){return this.url+"&"+jQuery.param({start_val:f,max_vals:e})},ajaxErrorFn:function(g,e,f){},load:function(h){this.log(this+".load");if(!a.url){throw (a+" requires a url")}if(this.total===null){this.log("\t total is null (will load all)")}else{this.log("\t total:",this.total)}var g=a.size;if((a.total!==null)&&(a.total<a.size)){g=a.total}a.log(a+"\t beginning recursion");f(a.start,g);function f(k,j){a.log(a+".loadHelper, start:",k,"size:",j);var i=a.buildUrl(k,j);a.log("\t url:",i);jQuery.ajax({url:a.buildUrl(k,j),dataType:"json",error:function(n,l,m){a.log("\t ajax error, status:",l,"error:",m);if(a.currentIntervalId){clearInterval(a.currentIntervalId)}$(a).trigger(ERROR_EVENT,[l,m]);a.ajaxErrorFn(n,l,m)},success:function(l){a.log("\t ajax success, response:",l,"next:",m,"remainder:",n);if(l!==null){a.data.push(l);$(a).trigger(d,[l,k,j]);var m=k+j,n=a.size;if(a.total!==null){n=Math.min(a.total-m,a.size)}a.log("\t next recursion, start:",m,"size:",n);if(a.total===null||n>0){a.currentIntervalId=setTimeout(function(){f(m,n)},a.delay);a.log("\t currentIntervalId:",a.currentIntervalId)}else{e()}}else{e()}}})}function e(){a.log(a+".loadHelper, has finished:",a.data);$(a).trigger(b,[a.data,a.total]);if(h){h(a.data)}}},toString:function(){return"LazyDataLoader"}});a.initialize(c);return a}; \ No newline at end of file +function LazyDataLoader(c){var a=this,d="loaded.new",b="complete";ERROR_EVENT="error";jQuery.extend(a,LoggableMixin);jQuery.extend(a,{total:undefined,url:undefined,currentIntervalId:undefined,data:[],delay:4000,start:0,size:4000,initialize:function(e){jQuery.extend(a,e);if(e.hasOwnProperty("initialize")){e.initialize.call(a,e)}this.log(this+" initialized:",a)},buildUrl:function(f,e){return this.url+"&"+jQuery.param({start_val:f,max_vals:e})},ajaxErrorFn:function(g,e,f){},load:function(h){this.log(this+".load");if(!a.url){throw (a+" requires a url")}if(this.total===null){this.log("\t total is null (will load all)")}else{this.log("\t total:",this.total)}var g=a.size;if((a.total!==null)&&(a.total<a.size)){g=a.total}a.log(a+"\t beginning recursion");f(a.start,g);function f(k,j){a.log(a+".loadHelper, start:",k,"size:",j);var i=a.buildUrl(k,j);a.log("\t url:",i);jQuery.ajax({url:a.buildUrl(k,j),dataType:"json",error:function(n,l,m){a.log("\t ajax error, status:",l,"error:",m);if(a.currentIntervalId){clearInterval(a.currentIntervalId)}$(a).trigger(ERROR_EVENT,[l,m]);a.ajaxErrorFn(n,l,m)},success:function(l){a.log("\t ajax success, response:",l,"next:",m,"remainder:",n);if(l!==null){a.data.push(l);$(a).trigger(d,[l,k,j]);var m=k+j,n=a.size;if(a.total!==null){n=Math.min(a.total-m,a.size)}a.log("\t next recursion, start:",m,"size:",n);if(a.total===null||n>0){a.currentIntervalId=setTimeout(function(){f(m,n)},a.delay);a.log("\t currentIntervalId:",a.currentIntervalId)}else{e()}}else{e()}}})}function e(){a.log(a+".loadHelper, has finished:",a.data);$(a).trigger(b,[a.data,a.total]);if(h){h(a.data)}}},toString:function(){return"LazyDataLoader"}});a.initialize(c);return a}; \ No newline at end of file diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa static/scripts/packed/viz/scatterplot.js --- a/static/scripts/packed/viz/scatterplot.js +++ b/static/scripts/packed/viz/scatterplot.js @@ -1,1 +1,1 @@ -function TwoVarScatterplot(d){var b=10,f=7,e=10,c=8,a=5;this.log=function(){if(this.debugging&&console&&console.debug){var g=Array.prototype.slice.call(arguments);g.unshift(this.toString());console.debug.apply(console,g)}};this.log("new TwoVarScatterplot:",d);this.defaults={id:"TwoVarScatterplot",containerSelector:"body",maxDataPoints:30000,datapointSize:4,animDuration:500,xNumTicks:10,yNumTicks:10,xAxisLabelBumpY:40,yAxisLabelBumpX:-35,width:400,height:400,marginTop:50,marginRight:50,marginBottom:50,marginLeft:50,xMin:null,xMax:null,yMin:null,yMax:null,xLabel:"X",yLabel:"Y"};this.config=_.extend({},this.defaults,d);this.log("intial config:",this.config);this.updateConfig=function(g,h){_.extend(this.config,g);this.log(this+".updateConfig:",this.config)};this.toString=function(){return this.config.id};this.translateStr=function(g,h){return"translate("+g+","+h+")"};this.rotateStr=function(h,g,i){return"rotate("+h+","+g+","+i+")"};this.adjustChartDimensions=function(j,h,g,i){j=j||0;h=h||0;g=g||0;i=i||0;this.svg.attr("width",this.config.width+(this.config.marginRight+h)+(this.config.marginLeft+i)).attr("height",this.config.height+(this.config.marginTop+j)+(this.config.marginBottom+g)).style("display","block");this.content=this.svg.select("g.content").attr("transform",this.translateStr(this.config.marginLeft+i,this.config.marginTop+j))};this.preprocessData=function(i,h,g){return(i.length>this.config.maxDataPoints)?(i.slice(0,this.config.maxDataPoints)):(i)};this.findMinMaxes=function(g,i,h){this.xMin=this.config.xMin||(h)?(h[0].min):(d3.min(g));this.xMax=this.config.xMax||(h)?(h[0].max):(d3.max(g));this.yMin=this.config.yMin||(h)?(h[1].min):(d3.min(i));this.yMax=this.config.yMax||(h)?(h[1].max):(d3.max(i))};this.setUpScales=function(){this.xScale=d3.scale.linear().domain([this.xMin,this.xMax]).range([0,this.config.width]),this.yScale=d3.scale.linear().domain([this.yMin,this.yMax]).range([this.config.height,0])};this.setUpXAxis=function(){this.xAxisFn=d3.svg.axis().scale(this.xScale).ticks(this.config.xNumTicks).orient("bottom");this.xAxis.attr("transform",this.translateStr(0,this.config.height)).call(this.xAxisFn);var g=d3.max(_.map([this.xMin,this.xMax],function(h){return(String(h)).length}));if(g>=a){this.xAxis.selectAll("g").filter(":nth-child(odd)").style("display","none")}this.log("this.config.xLabel:",this.config.xLabel);this.xAxisLabel.attr("x",this.config.width/2).attr("y",this.config.xAxisLabelBumpY).attr("text-anchor","middle").text(this.config.xLabel);this.log("xAxisLabel:",this.xAxisLabel)};this.setUpYAxis=function(){this.yAxisFn=d3.svg.axis().scale(this.yScale).ticks(this.config.yNumTicks).orient("left");this.yAxis.call(this.yAxisFn);var g=this.yAxis.selectAll("text").filter(function(k,j){return j!==0});this.log("yTickLabels:",g);this.yLongestLabel=d3.max(g[0].map(function(k,j){return(d3.select(k).text()).length}))||0;var h=b+(this.yLongestLabel*f)+c+e;this.config.yAxisLabelBumpX=-(h-e);if(this.config.marginLeft<h){var i=(h)-this.config.marginLeft;i=(i<0)?(0):(i);this.adjustChartDimensions(0,0,0,i)}this.yAxisLabel.attr("x",this.config.yAxisLabelBumpX).attr("y",this.config.height/2).attr("text-anchor","middle").attr("transform",this.rotateStr(-90,this.config.yAxisLabelBumpX,this.config.height/2)).text(this.config.yLabel)};this.renderGrid=function(){this.vGridLines=this.content.selectAll("line.v-grid-line").data(this.xScale.ticks(this.xAxisFn.ticks()[0]));this.vGridLines.enter().append("svg:line").classed("grid-line v-grid-line",true);this.vGridLines.attr("x1",this.xScale).attr("y1",0).attr("x2",this.xScale).attr("y2",this.config.height);this.vGridLines.exit().remove();this.hGridLines=this.content.selectAll("line.h-grid-line").data(this.yScale.ticks(this.yAxisFn.ticks()[0]));this.hGridLines.enter().append("svg:line").classed("grid-line h-grid-line",true);this.hGridLines.attr("x1",0).attr("y1",this.yScale).attr("x2",this.config.width).attr("y2",this.yScale);this.hGridLines.exit().remove()};this.glyphEnterState=function(g){};this.glyphFinalState=function(g){};this.glyphExitState=function(g){};this.renderDatapoints=function(g,j,h){this.log(this+".renderDatapoints",arguments);var i=0;this.datapoints=this.addDatapoints(g,j,h,".glyph");this.datapoints.exit().each(function(){i+=1}).transition().duration(this.config.animDuration).attr("cy",this.config.height).attr("r",0).remove();this.log(i," glyphs removed")};this.addDatapoints=function(k,i,g,j){this.log(this+".addDatapoints",arguments);var n=this,m=0,o=function(q,p){return n.xScale(k[p])},h=function(q,p){return n.yScale(i[p])};var l=this.content.selectAll(j);this.log("existing datapoints:",l);l=l.data(k);m=0;l.enter().append("svg:circle").each(function(){m+=1}).classed("glyph",true).attr("cx",o).attr("cy",h).attr("r",0);this.log(m," new glyphs created");m=0;l.transition().duration(this.config.animDuration).each(function(){m+=1}).attr("cx",o).attr("cy",h).attr("r",n.config.datapointSize);this.log(m," existing glyphs transitioned");if(g){l.attr("data",function(q,p){return(g[p])})}l.attr("svg:title",function(q,p){return((g)?(g[p]+": "):(""))+k[p]+", "+i[p]});l.on("mouseover",function(r,p){var q=d3.select(this);q.style("fill","red").style("fill-opacity",1);n.content.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",q.attr("cx")).attr("y1",q.attr("cy")).attr("x2",0).attr("y2",q.attr("cy")).classed("hoverline",true);n.content.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",q.attr("cx")).attr("y1",q.attr("cy")).attr("x2",q.attr("cx")).attr("y2",n.config.height).classed("hoverline",true)}).on("mouseout",function(){d3.select(this).style("fill","black").style("fill-opacity",0.2);d3.selectAll(".hoverline").remove()});return l};this.render=function(i,j){this.log(this+".render",arguments);this.log("\t config:",this.config);var g=i[0],k=i[1],h=(i.length>2)?(i[2]):(undefined);g=this.preprocessData(g);k=this.preprocessData(k);this.log("xCol len",g.length,"yCol len",k.length);this.findMinMaxes(g,k,j);this.setUpScales();if(!this.svg){this.svg=d3.select("svg").attr("class","chart")}if(!this.content){this.content=this.svg.append("svg:g").attr("class","content").attr("id",this.config.id)}this.adjustChartDimensions();if(!this.xAxis){this.xAxis=this.content.append("g").attr("class","axis").attr("id","x-axis")}if(!this.xAxisLabel){this.xAxisLabel=this.xAxis.append("text").attr("class","axis-label").attr("id","x-axis-label")}if(!this.yAxis){this.yAxis=this.content.append("g").attr("class","axis").attr("id","y-axis")}if(!this.yAxisLabel){this.yAxisLabel=this.yAxis.append("text").attr("class","axis-label").attr("id","y-axis-label")}this.setUpXAxis();this.setUpYAxis();this.renderGrid();this.renderDatapoints(g,k,h)}}; \ No newline at end of file +function TwoVarScatterplot(d){var b=10,f=7,e=10,c=8,a=5;this.log=function(){if(this.debugging&&console&&console.debug){var g=Array.prototype.slice.call(arguments);g.unshift(this.toString());console.debug.apply(console,g)}};this.log("new TwoVarScatterplot:",d);this.defaults={id:"TwoVarScatterplot",containerSelector:"body",maxDataPoints:30000,datapointSize:4,animDuration:500,xNumTicks:10,yNumTicks:10,xAxisLabelBumpY:40,yAxisLabelBumpX:-40,width:400,height:400,marginTop:50,marginRight:50,marginBottom:50,marginLeft:50,xMin:null,xMax:null,yMin:null,yMax:null,xLabel:"X",yLabel:"Y"};this.config=_.extend({},this.defaults,d);this.log("intial config:",this.config);this.updateConfig=function(g,h){_.extend(this.config,g);this.log(this+".updateConfig:",this.config)};this.toString=function(){return this.config.id};this.translateStr=function(g,h){return"translate("+g+","+h+")"};this.rotateStr=function(h,g,i){return"rotate("+h+","+g+","+i+")"};this.adjustChartDimensions=function(j,h,g,i){j=j||0;h=h||0;g=g||0;i=i||0;this.svg.attr("width",this.config.width+(this.config.marginRight+h)+(this.config.marginLeft+i)).attr("height",this.config.height+(this.config.marginTop+j)+(this.config.marginBottom+g)).style("display","block");this.content=this.svg.select("g.content").attr("transform",this.translateStr(this.config.marginLeft+i,this.config.marginTop+j))};this.preprocessData=function(i,h,g){return(i.length>this.config.maxDataPoints)?(i.slice(0,this.config.maxDataPoints)):(i)};this.findMinMaxes=function(g,i,h){this.xMin=this.config.xMin||(h)?(h[0].min):(d3.min(g));this.xMax=this.config.xMax||(h)?(h[0].max):(d3.max(g));this.yMin=this.config.yMin||(h)?(h[1].min):(d3.min(i));this.yMax=this.config.yMax||(h)?(h[1].max):(d3.max(i))};this.setUpScales=function(){this.xScale=d3.scale.linear().domain([this.xMin,this.xMax]).range([0,this.config.width]),this.yScale=d3.scale.linear().domain([this.yMin,this.yMax]).range([this.config.height,0])};this.setUpXAxis=function(){this.xAxisFn=d3.svg.axis().scale(this.xScale).ticks(this.config.xNumTicks).orient("bottom");this.xAxis.attr("transform",this.translateStr(0,this.config.height)).call(this.xAxisFn);var g=d3.max(_.map([this.xMin,this.xMax],function(h){return(String(h)).length}));if(g>=a){this.xAxis.selectAll("g").filter(":nth-child(odd)").style("display","none")}this.log("this.config.xLabel:",this.config.xLabel);this.xAxisLabel.attr("x",this.config.width/2).attr("y",this.config.xAxisLabelBumpY).attr("text-anchor","middle").text(this.config.xLabel);this.log("xAxisLabel:",this.xAxisLabel)};this.setUpYAxis=function(){this.yAxisFn=d3.svg.axis().scale(this.yScale).ticks(this.config.yNumTicks).orient("left");this.yAxis.call(this.yAxisFn);var g=this.yAxis.selectAll("text").filter(function(k,j){return j!==0});this.log("yTickLabels:",g);this.yLongestLabel=d3.max(g[0].map(function(k,j){return(d3.select(k).text()).length}))||0;var h=b+(this.yLongestLabel*f)+c+e;this.config.yAxisLabelBumpX=-(h-e);if(this.config.marginLeft<h){var i=(h)-this.config.marginLeft;i=(i<0)?(0):(i);this.adjustChartDimensions(0,0,0,i)}this.yAxisLabel.attr("x",this.config.yAxisLabelBumpX).attr("y",this.config.height/2).attr("text-anchor","middle").attr("transform",this.rotateStr(-90,this.config.yAxisLabelBumpX,this.config.height/2)).text(this.config.yLabel)};this.renderGrid=function(){this.vGridLines=this.content.selectAll("line.v-grid-line").data(this.xScale.ticks(this.xAxisFn.ticks()[0]));this.vGridLines.enter().append("svg:line").classed("grid-line v-grid-line",true);this.vGridLines.attr("x1",this.xScale).attr("y1",0).attr("x2",this.xScale).attr("y2",this.config.height);this.vGridLines.exit().remove();this.hGridLines=this.content.selectAll("line.h-grid-line").data(this.yScale.ticks(this.yAxisFn.ticks()[0]));this.hGridLines.enter().append("svg:line").classed("grid-line h-grid-line",true);this.hGridLines.attr("x1",0).attr("y1",this.yScale).attr("x2",this.config.width).attr("y2",this.yScale);this.hGridLines.exit().remove()};this.renderDatapoints=function(g,n,j){this.log(this+".renderDatapoints",arguments);var k=0,m=this,i=function(p,o){return m.xScale(g[o])},h=function(p,o){return m.yScale(n[o])};var l=this.content.selectAll(".glyph").data(g);k=0;l.enter().append("svg:circle").each(function(){k+=1}).classed("glyph",true).attr("cx",0).attr("cy",this.config.height).attr("r",0);this.log(k," new glyphs created");k=0;l.transition().duration(this.config.animDuration).each(function(){k+=1}).attr("cx",i).attr("cy",h).attr("r",m.config.datapointSize);this.log(k," existing glyphs transitioned");l.exit().each(function(){k+=1}).transition().duration(this.config.animDuration).attr("cy",this.config.height).attr("r",0).remove();this.log(k," glyphs removed");this._addDatapointEventhandlers(l,g,n,j)};this._addDatapointEventhandlers=function(j,g,k,h){var i=this;j.on("mouseover",function(o,l){var n=d3.select(this);n.style("fill","red").style("fill-opacity",1);i.content.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",n.attr("cx")-i.config.datapointSize).attr("y1",n.attr("cy")).attr("x2",0).attr("y2",n.attr("cy")).classed("hoverline",true);if(n.attr("cy")<i.config.height){i.content.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",n.attr("cx")).attr("y1",n.attr("cy")+i.config.datapointSize).attr("x2",n.attr("cx")).attr("y2",i.config.height).classed("hoverline",true)}var m=$(this).offset();i.datapointInfoBox=i.infoBox(m.top,m.left,i.infoHtml(g[l],k[l],(h)?(h[l]):(undefined)));$("body").append(i.datapointInfoBox)}).on("mouseout",function(){d3.select(this).style("fill","black").style("fill-opacity",0.2);i.content.selectAll(".hoverline").remove();if(i.datapointInfoBox){i.datapointInfoBox.remove()}})},this.render=function(i,j){this.log(this+".render",arguments);this.log("\t config:",this.config);var g=i[0],k=i[1],h=(i.length>2)?(i[2]):(undefined);g=this.preprocessData(g);k=this.preprocessData(k);this.log("xCol len",g.length,"yCol len",k.length);this.findMinMaxes(g,k,j);this.setUpScales();if(!this.svg){this.svg=d3.select("svg").attr("class","chart")}if(!this.content){this.content=this.svg.append("svg:g").attr("class","content").attr("id",this.config.id)}this.adjustChartDimensions();if(!this.xAxis){this.xAxis=this.content.append("g").attr("class","axis").attr("id","x-axis")}if(!this.xAxisLabel){this.xAxisLabel=this.xAxis.append("text").attr("class","axis-label").attr("id","x-axis-label")}if(!this.yAxis){this.yAxis=this.content.append("g").attr("class","axis").attr("id","y-axis")}if(!this.yAxisLabel){this.yAxisLabel=this.yAxis.append("text").attr("class","axis-label").attr("id","y-axis-label")}this.setUpXAxis();this.setUpYAxis();this.renderGrid();this.renderDatapoints(g,k,h)};this.infoHtml=function(g,j,i){var h=$("<div/>");if(i){$("<div/>").text(i).css("font-weight","bold").appendTo(h)}$("<div/>").text(g).appendTo(h);$("<div/>").text(j).appendTo(h);return h.html()};this.infoBox=function(l,k,i,h,g){h=h||0;g=g||20;var j=$("<div />").addClass("chart-info-box").css({position:"absolute",top:l+h,left:k+g});j.html(i);return j}}; \ No newline at end of file diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa static/scripts/templates/compiled/template-visualization-chartControl.js --- a/static/scripts/templates/compiled/template-visualization-chartControl.js +++ b/static/scripts/templates/compiled/template-visualization-chartControl.js @@ -13,19 +13,19 @@ foundHelper = helpers.datapointSize; if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); } else { stack1 = depth0.datapointSize; stack1 = typeof stack1 === functionType ? stack1() : stack1; } - buffer += escapeExpression(stack1) + "</div>\n <div class=\"slider\"></div>\n <p class=\"form-help help-text-small\">\n Size of the graphic representation of each data point\n </p>\n </div>\n\n <div id=\"animDuration\" class=\"form-input checkbox-input\">\n <label for=\"animated\">Animate graph transitions?: </label>\n <input type=\"checkbox\" id=\"animated\"\n class=\"checkbox control\""; + buffer += escapeExpression(stack1) + "</div>\n <div class=\"slider\"></div>\n <p class=\"form-help help-text-small\">\n Size of the graphic representation of each data point\n </p>\n </div>\n\n <div id=\"animDuration\" class=\"form-input checkbox-input\">\n <label for=\"animate-chart\">Animate chart transitions?: </label>\n <input type=\"checkbox\" id=\"animate-chart\"\n class=\"checkbox control\""; stack1 = depth0.animDuration; stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(1, program1, data)}); if(stack1 || stack1 === 0) { buffer += stack1; } - buffer += " />\n <p class=\"form-help help-text-small\">\n Uncheck this to disable the animations used on the graph\n </p>\n </div>\n\n <div id=\"width\" class=\"form-input numeric-slider-input\">\n <label for=\"width\">Graph width: </label>\n <div class=\"slider-output\">"; + buffer += " />\n <p class=\"form-help help-text-small\">\n Uncheck this to disable the animations used on the chart\n </p>\n </div>\n\n <div id=\"width\" class=\"form-input numeric-slider-input\">\n <label for=\"width\">Chart width: </label>\n <div class=\"slider-output\">"; foundHelper = helpers.width; if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); } else { stack1 = depth0.width; stack1 = typeof stack1 === functionType ? stack1() : stack1; } - buffer += escapeExpression(stack1) + "</div>\n <div class=\"slider\"></div>\n <p class=\"form-help help-text-small\">\n (not including graph margins and axes)\n </p>\n </div>\n\n <div id=\"height\" class=\"form-input numeric-slider-input\">\n <label for=\"height\">Graph height: </label>\n <div class=\"slider-output\">"; + buffer += escapeExpression(stack1) + "</div>\n <div class=\"slider\"></div>\n <p class=\"form-help help-text-small\">\n (not including chart margins and axes)\n </p>\n </div>\n\n <div id=\"height\" class=\"form-input numeric-slider-input\">\n <label for=\"height\">Chart height: </label>\n <div class=\"slider-output\">"; foundHelper = helpers.height; if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); } else { stack1 = depth0.height; stack1 = typeof stack1 === functionType ? stack1() : stack1; } - buffer += escapeExpression(stack1) + "</div>\n <div class=\"slider\"></div>\n <p class=\"form-help help-text-small\">\n (not including graph margins and axes)\n </p>\n </div>\n\n <div id=\"X-axis-label\"class=\"text-input form-input\">\n <label for=\"X-axis-label\">Re-label the X axis: </label>\n <input type=\"text\" name=\"X-axis-label\" id=\"X-axis-label\" value=\""; + buffer += escapeExpression(stack1) + "</div>\n <div class=\"slider\"></div>\n <p class=\"form-help help-text-small\">\n (not including chart margins and axes)\n </p>\n </div>\n\n <div id=\"X-axis-label\"class=\"text-input form-input\">\n <label for=\"X-axis-label\">Re-label the X axis: </label>\n <input type=\"text\" name=\"X-axis-label\" id=\"X-axis-label\" value=\""; foundHelper = helpers.xLabel; if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); } else { stack1 = depth0.xLabel; stack1 = typeof stack1 === functionType ? stack1() : stack1; } diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa static/scripts/templates/compiled/template-visualization-dataControl.js --- a/static/scripts/templates/compiled/template-visualization-dataControl.js +++ b/static/scripts/templates/compiled/template-visualization-dataControl.js @@ -2,7 +2,7 @@ var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {}; templates['template-visualization-dataControl'] = template(function (Handlebars,depth0,helpers,partials,data) { helpers = helpers || Handlebars.helpers; - var buffer = "", stack1, functionType="function", escapeExpression=this.escapeExpression, self=this; + var buffer = "", stack1, foundHelper, functionType="function", escapeExpression=this.escapeExpression, self=this; function program1(depth0,data) { @@ -46,6 +46,11 @@ buffer += escapeExpression(stack1) + "</option>\n "; return buffer;} +function program7(depth0,data) { + + + return "checked=\"true\"";} + buffer += "<p class=\"help-text\">\n Use the following controls to change the data used by the chart.\n Use the 'Draw' button to render (or re-render) the chart with the current settings.\n </p>\n\n "; buffer += "\n <div class=\"column-select\">\n <label for=\"X-select\">Data column for X: </label>\n <select name=\"X\" id=\"X-select\">\n "; stack1 = depth0.numericColumns; @@ -60,6 +65,15 @@ stack1 = depth0.allColumns; stack1 = helpers.each.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(5, program5, data)}); if(stack1 || stack1 === 0) { buffer += stack1; } - buffer += "\n </select>\n </div>\n\n <input id=\"render-button\" type=\"button\" value=\"Draw\" />\n <div class=\"clear\"></div>"; + buffer += "\n </select>\n </div>\n\n "; + buffer += "\n <div id=\"first-line-header\" style=\"display: none;\">\n <p>Possible headers: "; + foundHelper = helpers.possibleHeaders; + if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); } + else { stack1 = depth0.possibleHeaders; stack1 = typeof stack1 === functionType ? stack1() : stack1; } + buffer += escapeExpression(stack1) + "\n </p>\n <label for=\"first-line-header-checkbox\">Use the above as column headers?</label>\n <input type=\"checkbox\" name=\"include-id\" id=\"first-line-header-checkbox\"\n "; + stack1 = depth0.usePossibleHeaders; + stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(7, program7, data)}); + if(stack1 || stack1 === 0) { buffer += stack1; } + buffer += "/>\n <p class=\"help-text-small\">\n It looks like Galaxy couldn't get proper column headers for this data.\n Would you like to use the column headers above as column names to select columns?\n </p>\n </div>\n\n <input id=\"render-button\" type=\"button\" value=\"Draw\" />\n <div class=\"clear\"></div>"; return buffer;}); })(); \ No newline at end of file diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa static/scripts/templates/visualization-templates.html --- a/static/scripts/templates/visualization-templates.html +++ b/static/scripts/templates/visualization-templates.html @@ -90,6 +90,19 @@ </select></div> + {{! if we're using generic column selection names ('column 1') - allow the user to use the first line }} + <div id="first-line-header" style="display: none;"> + <p>Possible headers: {{ possibleHeaders }} + </p> + <label for="first-line-header-checkbox">Use the above as column headers?</label> + <input type="checkbox" name="include-id" id="first-line-header-checkbox" + {{#if usePossibleHeaders }}checked="true"{{/if}}/> + <p class="help-text-small"> + It looks like Galaxy couldn't get proper column headers for this data. + Would you like to use the column headers above as column names to select columns? + </p> + </div> + <input id="render-button" type="button" value="Draw" /><div class="clear"></div></script> @@ -112,29 +125,29 @@ </div><div id="animDuration" class="form-input checkbox-input"> - <label for="animated">Animate graph transitions?: </label> - <input type="checkbox" id="animated" + <label for="animate-chart">Animate chart transitions?: </label> + <input type="checkbox" id="animate-chart" class="checkbox control"{{#if animDuration}} checked="true"{{/if}} /><p class="form-help help-text-small"> - Uncheck this to disable the animations used on the graph + Uncheck this to disable the animations used on the chart </p></div><div id="width" class="form-input numeric-slider-input"> - <label for="width">Graph width: </label> + <label for="width">Chart width: </label><div class="slider-output">{{width}}</div><div class="slider"></div><p class="form-help help-text-small"> - (not including graph margins and axes) + (not including chart margins and axes) </p></div><div id="height" class="form-input numeric-slider-input"> - <label for="height">Graph height: </label> + <label for="height">Chart height: </label><div class="slider-output">{{height}}</div><div class="slider"></div><p class="form-help help-text-small"> - (not including graph margins and axes) + (not including chart margins and axes) </p></div> diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa static/scripts/utils/LazyDataLoader.js --- a/static/scripts/utils/LazyDataLoader.js +++ b/static/scripts/utils/LazyDataLoader.js @@ -81,11 +81,11 @@ // it's the responsibility of the code using this to combine them properly data : [], // ms btwn recursive loads - delay : 500, + delay : 4000, // starting line, element, whatever start : 0, // size to fetch per load - size : 1000, + size : 4000, // loader init func: extends loader with config and calls config.init if there //@param {object} config : object containing variables to override (or additional) diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa static/scripts/viz/scatterplot.js --- a/static/scripts/viz/scatterplot.js +++ b/static/scripts/viz/scatterplot.js @@ -90,7 +90,7 @@ xNumTicks : 10, yNumTicks : 10, xAxisLabelBumpY : 40, - yAxisLabelBumpX : -35, + yAxisLabelBumpX : -40, width : 400, height : 400, //TODO: anyway to make this a sub-obj? @@ -304,75 +304,37 @@ }; // ........................................................ data points - //TODO: these to config ...somehow - //TODO: use these in renderDatapoints ...somehow - this.glyphEnterState = function( d3Elem ){ - - }; - this.glyphFinalState = function( d3Elem ){ - - }; - this.glyphExitState = function( d3Elem ){ - - }; - - // initial render or complete re-render (REPLACE datapoints) this.renderDatapoints = function( xCol, yCol, ids ){ this.log( this + '.renderDatapoints', arguments ); - var count = 0; - - this.datapoints = this.addDatapoints( xCol, yCol, ids, ".glyph" ); - - // glyphs that need to be removed: transition to from normal state to 'exit' state, remove from DOM - this.datapoints.exit() - .each( function(){ count += 1; } ) - .transition().duration( this.config.animDuration ) - .attr( "cy", this.config.height ) - .attr( "r", 0 ) - .remove(); - this.log( count, ' glyphs removed' ); - - //this.log( this.datapoints.length, ' glyphs in the graph' ); - }; - - // adding points to existing - this.addDatapoints = function( newXCol, newYCol, ids, selectorForExisting ){ - this.log( this + '.addDatapoints', arguments ); - // ADD datapoints to plot that's already rendered - // if selectorForExisting === undefined (as in not passed), addDatapoints won't update existing - // pass in the class ( '.glyph' ) to update exising datapoints - var plot = this, - count = 0, + var count = 0, + plot = this, xPosFn = function( d, i ){ //if( d ){ this.log( 'x.data:', newXCol[ i ], 'plotted:', plot.xScale( newXCol[ i ] ) ); } - return plot.xScale( newXCol[ i ] ); + return plot.xScale( xCol[ i ] ); }, yPosFn = function( d, i ){ //if( d ){ this.log( 'y.data:', newYCol[ i ], 'plotted:', plot.yScale( newYCol[ i ] ) ); } - return plot.yScale( newYCol[ i ] ); + return plot.yScale( yCol[ i ] ); }; - - // select all existing glyphs and compare to incoming data - // enter() will yield those glyphs that need to be added - var newDatapoints = this.content.selectAll( selectorForExisting ); - this.log( 'existing datapoints:', newDatapoints ); - newDatapoints = newDatapoints.data( newXCol ); - - // enter - new data to be added as glyphs: give them a 'entry' position and style + + //this.datapoints = this.addDatapoints( xCol, yCol, ids, ".glyph" ); + var datapoints = this.content.selectAll( '.glyph' ).data( xCol ); + + // enter - NEW data to be added as glyphs: give them a 'entry' position and style count = 0; - newDatapoints.enter() + datapoints.enter() .append( 'svg:circle' ) .each( function(){ count += 1; } ) .classed( "glyph", true ) - .attr( "cx", xPosFn ) - .attr( "cy", yPosFn ) + .attr( "cx", 0 ) + .attr( "cy", this.config.height ) // start all bubbles small... .attr( "r", 0 ); this.log( count, ' new glyphs created' ); - // for all existing glyphs and those that need to be added: transition anim to final state + // for all EXISTING glyphs and those that need to be added: transition anim to final state count = 0; - newDatapoints + datapoints // ...animate to final position .transition().duration( this.config.animDuration ) .each( function(){ count += 1; } ) @@ -381,18 +343,22 @@ .attr( "r", plot.config.datapointSize ); this.log( count, ' existing glyphs transitioned' ); - // attach ids - if( ids ){ - newDatapoints.attr( 'data', function( d, i ){ return ( ids[ i ] ); } ); - } + // events + // glyphs that need to be removed: transition to from normal state to 'exit' state, remove from DOM + datapoints.exit() + .each( function(){ count += 1; } ) + .transition().duration( this.config.animDuration ) + .attr( "cy", this.config.height ) + .attr( "r", 0 ) + .remove(); + this.log( count, ' glyphs removed' ); - // titles - newDatapoints.attr( 'svg:title', function( d, i ){ - return (( ids )?( ids[ i ] + ': ' ):( '' )) + newXCol[ i ] + ', ' + newYCol[ i ]; - }); - - // events - newDatapoints + this._addDatapointEventhandlers( datapoints, xCol, yCol, ids ); + }; + + this._addDatapointEventhandlers = function( datapoints, xCol, yCol, ids ){ + var plot = this; + datapoints //TODO: remove magic numbers .on( 'mouseover', function( d, i ){ var datapoint = d3.select( this ); @@ -404,36 +370,42 @@ plot.content.append( 'line' ) .attr( 'stroke', 'red' ) .attr( 'stroke-width', 1 ) - .attr( 'x1', datapoint.attr( 'cx' ) ).attr( 'y1', datapoint.attr( 'cy' ) ) - .attr( 'x2', 0 ).attr( 'y2', datapoint.attr( 'cy' ) ) - .classed( 'hoverline', true ); - plot.content.append( 'line' ) - .attr( 'stroke', 'red' ) - .attr( 'stroke-width', 1 ) - .attr( 'x1', datapoint.attr( 'cx' ) ).attr( 'y1', datapoint.attr( 'cy' ) ) - .attr( 'x2', datapoint.attr( 'cx' ) ).attr( 'y2', plot.config.height ) + // start not at center, but at the edge of the circle - to prevent mouseover thrashing + .attr( 'x1', datapoint.attr( 'cx' ) - plot.config.datapointSize ) + .attr( 'y1', datapoint.attr( 'cy' ) ) + .attr( 'x2', 0 ) + .attr( 'y2', datapoint.attr( 'cy' ) ) .classed( 'hoverline', true ); - //var datapointWindowPos = $( this ).position(); - //var datapointWindowPos = $( this ).offset(); - //window.dot = this; - ////this.popup = make_abs_box( datapointWindowPos.top, datapointWindowPos.left + datapoint.attr( 'r' ), - //this.popup = make_abs_box( datapointWindowPos.top, datapointWindowPos.left, - // newXCol[ i ], newYCol[ i ], ( ids )?( ids[ i ] ):( undefined ) ); - //$( 'body' ).append( this.popup ); + // if the vertical hoverline + if( datapoint.attr( 'cy' ) < plot.config.height ){ + plot.content.append( 'line' ) + .attr( 'stroke', 'red' ) + .attr( 'stroke-width', 1 ) + .attr( 'x1', datapoint.attr( 'cx' ) ) + .attr( 'y1', datapoint.attr( 'cy' ) + plot.config.datapointSize ) + .attr( 'x2', datapoint.attr( 'cx' ) ) + .attr( 'y2', plot.config.height ) + .classed( 'hoverline', true ); + } + + var datapointWindowPos = $( this ).offset(); + plot.datapointInfoBox = plot.infoBox( + datapointWindowPos.top, datapointWindowPos.left, + plot.infoHtml( xCol[ i ], yCol[ i ], ( ids )?( ids[ i ] ):( undefined ) ) + ); + $( 'body' ).append( plot.datapointInfoBox ); }) .on( 'mouseout', function(){ d3.select( this ) .style( 'fill', 'black' ) .style( 'fill-opacity', 0.2 ); - d3.selectAll( '.hoverline' ).remove(); - //if( this.popup ){ - // this.popup.remove(); - //} + plot.content.selectAll( '.hoverline' ).remove(); + if( plot.datapointInfoBox ){ + plot.datapointInfoBox.remove(); + } }); - - return newDatapoints; - }; + }, this.render = function( columnData, meta ){ this.log( this + '.render', arguments ); @@ -457,7 +429,7 @@ //this.log( 'xMin, xMax, yMin, yMax:', this.xMin, this.xMax, this.yMin, this.yMax ); this.setUpScales(); - // build the svg dom infrastructure + // find (or build if it doesn't exist) the svg dom infrastructure if( !this.svg ){ this.svg = d3.select( 'svg' ).attr( "class", "chart" ); } if( !this.content ){ this.content = this.svg.append( "svg:g" ).attr( "class", "content" ).attr( 'id', this.config.id ); @@ -485,6 +457,32 @@ this.renderGrid(); this.renderDatapoints( xCol, yCol, ids ); }; + + this.infoHtml = function( x, y, id ){ + var retDiv = $( '<div/>' ); + if( id ){ + $( '<div/>' ).text( id ).css( 'font-weight', 'bold' ).appendTo( retDiv ); + } + $( '<div/>' ).text( x ).appendTo( retDiv ); + $( '<div/>' ).text( y ).appendTo( retDiv ); + return retDiv.html(); + }; + + //TODO: html for now + this.infoBox = function( top, left, html, adjTop, adjLeft ){ + adjTop = adjTop || 0; + adjLeft = adjLeft || 20; + var infoBox = $( '<div />' ) + .addClass( 'chart-info-box' ) + .css({ + 'position' : 'absolute', + 'top' : top + adjTop, + 'left' : left + adjLeft + }); + infoBox.html( html ); + return infoBox; + }; + } //============================================================================== diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa templates/visualization/scatterplot.mako --- a/templates/visualization/scatterplot.mako +++ b/templates/visualization/scatterplot.mako @@ -26,6 +26,7 @@ padding : 8px; background-color: #ebd9b2; margin-bottom: 16px; + overflow: auto; } #chart-header .subtitle { @@ -182,12 +183,11 @@ } /* -------------------------------------------- info box */ -.zero-dimensions { - width: 0; - height: 0; - border-top: 8px solid transparent; - border-bottom: 8px solid transparent; - border-right: 8px solid grey; +.chart-info-box { + border-radius: 4px; + padding: 4px; + background-color: white; + border: 1px solid black; } </style> @@ -255,63 +255,3 @@ </div><div id="scatterplot" class="scatterplot-control-form"></div></%def> - - -<script type="text/javascript"> -function make_abs_box( top, left, x, y, id ){ - var ARROW_SIZE = 8, - ARROW_COLOR = 'grey', - DIST_TO_POINT = 4, - halfArrowSize = ARROW_SIZE / 2; - - var boxContainer = $( '<div />' ) - .attr( 'id', 'abs-box-container' ) - // top left arrow - .css({ - 'position' : 'absolute', - 'top' : top - halfArrowSize, - 'left' : left + ARROW_SIZE + DIST_TO_POINT, - 'background-color': 'transparent', - }); - window.boxContainer = boxContainer; - - var arrowLeft = $( '<div />' ) - .attr( 'id', 'abs-box-arrow' ) - .addClass( 'zero-dimensions' ) - .css({ - 'border-top' : ARROW_SIZE + 'px solid transparent', - 'border-bottom' : ARROW_SIZE + 'px solid transparent', - 'border-right' : ARROW_SIZE + 'px solid ' + ARROW_COLOR, - }); - boxContainer.append( arrowLeft ); - window.arrow = arrowLeft; - - console.debug( 'arrow height:', arrowLeft.height() ); - var boxInfo = $( '<div />' ) - .attr( 'id', 'abs-box' ) - .css({ - 'position' : 'relative', - //TODO: 4 here is the border-radius - 'top' : -( 2 * ARROW_SIZE + 6 ), - 'left' : ARROW_SIZE, - 'border' : '2px solid grey', - 'border-radius' : '4px', - 'padding' : '4px', - 'background-color': 'white', - 'box-shadow' : '4px 4px 4px black' - }); - - // remove - if( id ){ - $( '<div />' ).addClass( 'abs-box-id' ).css( 'font-weight', 'bold' ).text( id ).appendTo( boxInfo ); - } - $( '<div />' ).addClass( 'abs-box-x' ).text( x ).appendTo( boxInfo ); - $( '<div />' ).addClass( 'abs-box-y' ).text( y ).appendTo( boxInfo ); - boxContainer.append( boxInfo ); - boxContainer.append( '<div style="clear:both"></div>' ); - window.boxInfo = boxContainer; - - //console.debug( boxContainer ); - return boxContainer; -} -</script> Repository URL: https://bitbucket.org/galaxy/galaxy-central/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.