UI: add pagination jquery plugin able to handle known/unknown data totals; Scatterplot: use that plugin for pagination, add class for highlighted data points

This commit is contained in:
Carl Eberhard
2014-02-14 10:17:56 -05:00
parent 536d39a98d
commit 5d01f6393c
9 changed files with 383 additions and 206 deletions
@@ -5,7 +5,7 @@ todo:
Better separation of AJAX in scatterplot.js (maybe pass in function?)
Labels should auto fill in chart control when dataset has column_names
Allow column selection/config using the peek output as a base for UI
Allow setting perPage in config
Allow setting perPage in chart controls
Allow option to auto set width/height based on screen real estate avail.
Handle large number of pages better (Known genes hg19)
Use d3.nest to allow grouping, pagination/filtration by group (e.g. chromCol)
@@ -243,7 +243,7 @@ var ScatterplotConfigEditor = Backbone.View.extend( LoggableMixin ).extend({
alert( 'Error loading data:\n' + xhr.responseText );
})
.then( function(){
editor.render();
editor.display.render();
});
},
@@ -11,25 +11,15 @@ var ScatterplotDisplay = Backbone.View.extend({
initialize : function( attributes ){
this.data = null,
this.dataset = attributes.dataset;
this.calcNumPages();
},
calcNumPages : function(){
var config = this.model.get( 'config' );
this.lineCount = this.dataset.metadata_data_lines,
this.numPages = ( this.lineCount )?( Math.ceil( this.lineCount / config.pagination.perPage ) ):( undefined );
if( !this.lineCount || this.numPages === undefined ){
console.warn( 'no data total found' );
}
this.lineCount = this.dataset.metadata_data_lines || null;
},
fetchData : function(){
//TODO: doesn't work bc it's rendered in render()...
this.showLoadingIndicator( 'getting data' );
this.showLoadingIndicator();
//console.debug( 'currPage', this.config.pagination.currPage );
var view = this,
config = this.model.get( 'config' ),
//TODO: very tied to datasets - should be generalized eventually
//TODO: very tied to datasets - should be generalized eventually
xhr = jQuery.getJSON( '/api/datasets/' + this.dataset.id, {
data_type : 'raw_data',
provider : 'dataset-column',
@@ -37,6 +27,7 @@ var ScatterplotDisplay = Backbone.View.extend({
offset : ( config.pagination.currPage * config.pagination.perPage )
});
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.renderData();
@@ -63,6 +54,7 @@ var ScatterplotDisplay = Backbone.View.extend({
var html = [
'<div class="controls clear">',
'<div class="left">',
'<div class="page-control"></div>',
'</div>',
'<div class="right">',
'<p class="scatterplot-data-info"></p>',
@@ -92,11 +84,24 @@ var ScatterplotDisplay = Backbone.View.extend({
},
renderLeftControls : function(){
if( this.lineCount ){
this.$el.find( '.controls .left' ).empty().append( this.renderPagination() );
} else {
this.$el.find( '.controls .left' ).empty().append( this.renderPrevNext() );
}
var display = this,
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
//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;
},
@@ -155,83 +160,6 @@ var ScatterplotDisplay = Backbone.View.extend({
return this;
},
// ------------------------------------------------------------------------ data pagination
//TODO: to pagination control
goToPage : function( page ){
var pagination = this.model.get( 'config' ).pagination;
//console.debug( 'goToPage', page, pagination, this.numPages );
if( page <= 0 ){ page = 0; }
if( this.numPages && page >= this.numPages ){ page = this.numPages - 1; }
if( page === pagination.currPage ){ return this; }
//console.debug( '\t going to page ' + page )
pagination.currPage = page;
this.model.set( 'config', { pagination: pagination });
this.resetZoom();
this.fetchData();
return this;
},
nextPage : function(){
var currPage = this.model.get( 'config' ).pagination.currPage;
return this.goToPage( currPage + 1 );
},
prevPage : function(){
var currPage = this.model.get( 'config' ).pagination.currPage;
return this.goToPage( currPage - 1 );
},
/** render previous and next pagination buttons */
renderPrevNext : function(){
var config = this.model.get( 'config' );
// if there's no data or there's less than one page of data - return null
if( !this.data ){ return null; }
if( config.pagination.currPage === 0 && this.data.length < config.pagination.perPage ){ return null; }
var view = this,
$prev = $( '<li><a href="javascript:void(0);">Prev</a></li>' )
.click( function(){ view.prevPage(); }),
$next = $( '<li><a href="javascript:void(0);">Next</a></li>' )
.click( function(){ view.nextPage(); });
// disable if it either end
if( config.pagination.currPage === 0 ){
$prev.addClass( 'disabled' );
}
if( this.numPages && config.pagination.currPage === ( this.numPages - 1 ) ){
$next.addClass( 'disabled' );
}
return $( '<ul/>' ).addClass( 'pagination data-prev-next' ).append([ $prev, $next ]);
},
/** render page links for each possible page (if we can) */
renderPagination : function(){
var config = this.model.get( 'config' );
// if there's no data, no page count, or there's less than one page of data - return null
if( !this.data ){ return null; }
if( !this.numPages ){ return null; }
if( config.pagination.currPage === 0 && this.data.length < config.pagination.perPage ){ return null; }
var view = this,
$pagesList = $( '<ul/>' ).addClass( 'pagination data-pages' );
pageNumClick = function( ev ){
view.goToPage( $( this ).data( 'page' ) );
};
for( var i=0; i<this.numPages; i+=1 ){
// add html5 data tag 'page' for later click event handler use
var $pageLi = $([ '<li><a href="javascript:void(0);">', i + 1, '</a></li>' ].join( '' ))
.attr( 'data-page', i ).click( pageNumClick );
// highlight the current page
if( i === config.pagination.currPage ){
$pageLi.addClass( 'active' );
}
$pagesList.append( $pageLi );
}
return $pagesList;
},
// ------------------------------------------------------------------------ statistics display
/** create a webworker to calc stats for data given */
getStats : function(){
@@ -237,6 +237,7 @@ function scatterplot( renderTo, config, data ){
datapoints.on( 'mouseover', function( d, i ){
var datapoint = d3.select( this );
datapoint
.classed( 'highlight', true )
.style( 'fill', 'red' )
.style( 'fill-opacity', 1 );
@@ -273,6 +274,7 @@ function scatterplot( renderTo, config, data ){
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();