galaxy-commits
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
February 2014
- 1 participants
- 192 discussions
commit/galaxy-central: guerler: Upload: Fix style for FF
by commits-noreply@bitbucket.org 07 Feb '14
by commits-noreply@bitbucket.org 07 Feb '14
07 Feb '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/4228b5439785/
Changeset: 4228b5439785
User: guerler
Date: 2014-02-08 04:39:51
Summary: Upload: Fix style for FF
Affected #: 2 files
diff -r 2a62cf94bb5bb3a094f179652aaf17e4cdc2d8ce -r 4228b5439785c10aaf638db4e18be65dfd6b9275 static/scripts/mvc/upload/upload-ftp.js
--- a/static/scripts/mvc/upload/upload-ftp.js
+++ b/static/scripts/mvc/upload/upload-ftp.js
@@ -137,7 +137,7 @@
'<span class="upload-icon fa fa-hdd-o"/>' +
'<span id="upload-ftp-disk"/>' +
'</span>' +
- '<table class="grid">' +
+ '<table class="grid" style="float: left;">' +
'<thead>' +
'<tr>' +
'<th></th>' +
diff -r 2a62cf94bb5bb3a094f179652aaf17e4cdc2d8ce -r 4228b5439785c10aaf638db4e18be65dfd6b9275 static/scripts/packed/mvc/upload/upload-ftp.js
--- a/static/scripts/packed/mvc/upload/upload-ftp.js
+++ b/static/scripts/packed/mvc/upload/upload-ftp.js
@@ -1,1 +1,1 @@
-define(["utils/utils"],function(a){return Backbone.View.extend({options:{class_add:"upload-icon-button fa fa-square-o",class_remove:"upload-icon-button fa fa-check-square-o"},initialize:function(c){this.app=c;var b=this;this.setElement(this._template());a.jsonFromUrl(galaxy_config.root+"api/ftp_files",function(d){b._fill(d)})},events:{mousedown:function(b){b.preventDefault()}},_fill:function(c){if(c.length>0){this.$el.find("#upload-ftp-content").html($(this._template_table()));var b=0;for(key in c){this.add(c[key]);b+=c[key].size}this.$el.find("#upload-ftp-number").html(c.length+" files");this.$el.find("#upload-ftp-disk").html(a.bytesToString(b,true))}else{this.$el.find("#upload-ftp-content").html($(this._template_info()))}this.$el.find("#upload-ftp-wait").hide()},add:function(e){var d=$(this._template_row(e));$(this.el).find("tbody").append(d);var c="";if(this._find(e)){c=this.options.class_remove}else{c=this.options.class_add}d.find("#upload-ftp-add").addClass(c);var b=this;d.find("#upload-ftp-add").on("click",function(){var f=b._find(e);$(this).removeClass();if(!f){b.app.uploadbox.add([{mode:"ftp",name:e.path,size:e.size,path:e.path}]);$(this).addClass(b.options.class_remove)}else{b.app.collection.remove(f);$(this).addClass(b.options.class_add)}})},_find:function(f){var c=this.app.collection.where({file_path:f.path});var b=null;for(var d in c){var e=c[d];if(e.get("status")=="init"&&e.get("file_mode")=="ftp"){b=e.get("id")}}return b},_template_row:function(b){return'<tr><td><div id="upload-ftp-add"/></td><td>'+b.path+'</td><td style="white-space: nowrap;">'+a.bytesToString(b.size)+'</td><td style="white-space: nowrap;">'+b.ctime+"</td></tr>"},_template_table:function(){return'<span style="whitespace: nowrap; float: left;">Available files: </span><span style="whitespace: nowrap; float: right;"><span class="upload-icon fa fa-file-text-o"/><span id="upload-ftp-number"/> <span class="upload-icon fa fa-hdd-o"/><span id="upload-ftp-disk"/></span><table class="grid"><thead><tr><th></th><th>Name</th><th>Size</th><th>Created</th></tr></thead><tbody></tbody></table>'},_template_info:function(){return'<div class="upload-ftp-warning warningmessage">Your FTP directory does not contain any files.</div>'},_template:function(){return'<div class="upload-ftp"><div id="upload-ftp-wait" class="upload-ftp-wait fa fa-spinner fa-spin"/><div class="upload-ftp-help">This Galaxy server allows you to upload files via FTP. To upload some files, log in to the FTP server at <strong>'+this.app.options.ftp_upload_site+'</strong> using your Galaxy credentials (email address and password).</div><div id="upload-ftp-content"></div><div>'}})});
\ No newline at end of file
+define(["utils/utils"],function(a){return Backbone.View.extend({options:{class_add:"upload-icon-button fa fa-square-o",class_remove:"upload-icon-button fa fa-check-square-o"},initialize:function(c){this.app=c;var b=this;this.setElement(this._template());a.jsonFromUrl(galaxy_config.root+"api/ftp_files",function(d){b._fill(d)})},events:{mousedown:function(b){b.preventDefault()}},_fill:function(c){if(c.length>0){this.$el.find("#upload-ftp-content").html($(this._template_table()));var b=0;for(key in c){this.add(c[key]);b+=c[key].size}this.$el.find("#upload-ftp-number").html(c.length+" files");this.$el.find("#upload-ftp-disk").html(a.bytesToString(b,true))}else{this.$el.find("#upload-ftp-content").html($(this._template_info()))}this.$el.find("#upload-ftp-wait").hide()},add:function(e){var d=$(this._template_row(e));$(this.el).find("tbody").append(d);var c="";if(this._find(e)){c=this.options.class_remove}else{c=this.options.class_add}d.find("#upload-ftp-add").addClass(c);var b=this;d.find("#upload-ftp-add").on("click",function(){var f=b._find(e);$(this).removeClass();if(!f){b.app.uploadbox.add([{mode:"ftp",name:e.path,size:e.size,path:e.path}]);$(this).addClass(b.options.class_remove)}else{b.app.collection.remove(f);$(this).addClass(b.options.class_add)}})},_find:function(f){var c=this.app.collection.where({file_path:f.path});var b=null;for(var d in c){var e=c[d];if(e.get("status")=="init"&&e.get("file_mode")=="ftp"){b=e.get("id")}}return b},_template_row:function(b){return'<tr><td><div id="upload-ftp-add"/></td><td>'+b.path+'</td><td style="white-space: nowrap;">'+a.bytesToString(b.size)+'</td><td style="white-space: nowrap;">'+b.ctime+"</td></tr>"},_template_table:function(){return'<span style="whitespace: nowrap; float: left;">Available files: </span><span style="whitespace: nowrap; float: right;"><span class="upload-icon fa fa-file-text-o"/><span id="upload-ftp-number"/> <span class="upload-icon fa fa-hdd-o"/><span id="upload-ftp-disk"/></span><table class="grid" style="float: left;"><thead><tr><th></th><th>Name</th><th>Size</th><th>Created</th></tr></thead><tbody></tbody></table>'},_template_info:function(){return'<div class="upload-ftp-warning warningmessage">Your FTP directory does not contain any files.</div>'},_template:function(){return'<div class="upload-ftp"><div id="upload-ftp-wait" class="upload-ftp-wait fa fa-spinner fa-spin"/><div class="upload-ftp-help">This Galaxy server allows you to upload files via FTP. To upload some files, log in to the FTP server at <strong>'+this.app.options.ftp_upload_site+'</strong> using your Galaxy credentials (email address and password).</div><div id="upload-ftp-content"></div><div>'}})});
\ No newline at end of file
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.
1
0
commit/galaxy-central: guerler: Upload: Remove console output
by commits-noreply@bitbucket.org 07 Feb '14
by commits-noreply@bitbucket.org 07 Feb '14
07 Feb '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/2a62cf94bb5b/
Changeset: 2a62cf94bb5b
User: guerler
Date: 2014-02-08 01:41:28
Summary: Upload: Remove console output
Affected #: 2 files
diff -r c3671911ad886fda9cd85c1bf398ac6f10325022 -r 2a62cf94bb5bb3a094f179652aaf17e4cdc2d8ce static/scripts/mvc/upload/upload-button.js
--- a/static/scripts/mvc/upload/upload-button.js
+++ b/static/scripts/mvc/upload/upload-button.js
@@ -67,7 +67,6 @@
$el.addClass('progress-bar-notransition');
if (value != '') {
$el.addClass('progress-bar-' + value);
- console.log($el);
}
},
diff -r c3671911ad886fda9cd85c1bf398ac6f10325022 -r 2a62cf94bb5bb3a094f179652aaf17e4cdc2d8ce static/scripts/packed/mvc/upload/upload-button.js
--- a/static/scripts/packed/mvc/upload/upload-button.js
+++ b/static/scripts/packed/mvc/upload/upload-button.js
@@ -1,1 +1,1 @@
-define([],function(){var b=Backbone.Model.extend({defaults:{percentage:0,icon:"fa-circle",label:"",status:""}});var a=Backbone.View.extend({model:null,initialize:function(d){var c=this;this.model=d;this.options=this.model.attributes;this.setElement(this._template(this.options));$(this.el).on("click",this.options.onclick);if(this.options.tooltip){$(this.el).tooltip({title:this.options.tooltip,placement:"bottom"})}this.model.on("change:percentage",function(){c._percentage(c.model.get("percentage"))});this.model.on("change:status",function(){c._status(c.model.get("status"))});var c=this;$(window).on("beforeunload",function(){var e="";if(c.options.onunload){e=c.options.onunload()}if(e!=""){return e}})},_status:function(d){var c=this.$el.find(".progress-bar");c.removeClass();c.addClass("progress-bar");c.addClass("progress-bar-notransition");if(d!=""){c.addClass("progress-bar-"+d);console.log(c)}},_percentage:function(d){var c=this.$el.find(".progress-bar");c.css({width:d+"%"})},_template:function(c){return'<div style="float: right"><div class="upload-button"><div class="progress"><div class="progress-bar"></div></div><div id="label" class="label"><a class="panel-header-button" href="javascript:void(0)"><span class="fa fa-upload"></span></a></div></div></div>'}});return{Model:b,View:a}});
\ No newline at end of file
+define([],function(){var b=Backbone.Model.extend({defaults:{percentage:0,icon:"fa-circle",label:"",status:""}});var a=Backbone.View.extend({model:null,initialize:function(d){var c=this;this.model=d;this.options=this.model.attributes;this.setElement(this._template(this.options));$(this.el).on("click",this.options.onclick);if(this.options.tooltip){$(this.el).tooltip({title:this.options.tooltip,placement:"bottom"})}this.model.on("change:percentage",function(){c._percentage(c.model.get("percentage"))});this.model.on("change:status",function(){c._status(c.model.get("status"))});var c=this;$(window).on("beforeunload",function(){var e="";if(c.options.onunload){e=c.options.onunload()}if(e!=""){return e}})},_status:function(d){var c=this.$el.find(".progress-bar");c.removeClass();c.addClass("progress-bar");c.addClass("progress-bar-notransition");if(d!=""){c.addClass("progress-bar-"+d)}},_percentage:function(d){var c=this.$el.find(".progress-bar");c.css({width:d+"%"})},_template:function(c){return'<div style="float: right"><div class="upload-button"><div class="progress"><div class="progress-bar"></div></div><div id="label" class="label"><a class="panel-header-button" href="javascript:void(0)"><span class="fa fa-upload"></span></a></div></div></div>'}});return{Model:b,View:a}});
\ No newline at end of file
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.
1
0
commit/galaxy-central: guerler: Upload: Only add ftp file path if available
by commits-noreply@bitbucket.org 07 Feb '14
by commits-noreply@bitbucket.org 07 Feb '14
07 Feb '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/c3671911ad88/
Changeset: c3671911ad88
User: guerler
Date: 2014-02-08 01:05:36
Summary: Upload: Only add ftp file path if available
Affected #: 2 files
diff -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 -r c3671911ad886fda9cd85c1bf398ac6f10325022 static/scripts/mvc/upload/upload-view.js
--- a/static/scripts/mvc/upload/upload-view.js
+++ b/static/scripts/mvc/upload/upload-view.js
@@ -309,10 +309,14 @@
tool_input['file_type'] = extension;
tool_input['files_0|type'] = 'upload_dataset';
tool_input['files_0|url_paste'] = url_paste;
- tool_input['files_0|ftp_files'] = file_path;
tool_input['space_to_tabs'] = space_to_tabs;
tool_input['to_posix_lines'] = to_posix_lines;
+ // add ftp file path
+ if (file_mode == 'ftp') {
+ tool_input['files_0|ftp_files'] = file_path;
+ }
+
// setup data
data = {};
data['history_id'] = this.current_history;
diff -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 -r c3671911ad886fda9cd85c1bf398ac6f10325022 static/scripts/packed/mvc/upload/upload-view.js
--- a/static/scripts/packed/mvc/upload/upload-view.js
+++ b/static/scripts/packed/mvc/upload/upload-view.js
@@ -1,1 +1,1 @@
-define(["galaxy.modal","utils/utils","mvc/upload/upload-button","mvc/upload/upload-model","mvc/upload/upload-row","mvc/upload/upload-ftp","mvc/ui.popover","mvc/ui","utils/uploadbox"],function(a,f,e,c,b,g,d){return Backbone.View.extend({options:{nginx_upload_path:""},modal:null,ui_button:null,uploadbox:null,current_history:null,upload_size:0,select_extension:[["Auto-detect","auto"]],select_genome:[["Unspecified (?)","?"]],collection:new c.Collection(),ftp:null,counter:{announce:0,success:0,error:0,running:0,reset:function(){this.announce=this.success=this.error=this.running=0}},initialize:function(i){var h=this;if(!Galaxy.currHistoryPanel||!Galaxy.currHistoryPanel.model){window.setTimeout(function(){h.initialize()},500);return}if(!Galaxy.currUser.get("id")){return}this.ui_button=new e.Model({icon:"fa-upload",tooltip:"Download from URL or upload files from disk",label:"Load Data",onclick:function(j){if(j){h._eventShow(j)}},onunload:function(){if(h.counter.running>0){return"Several uploads are still processing."}}});$("#left .unified-panel-header-inner").append((new e.View(this.ui_button)).$el);var h=this;f.jsonFromUrl(galaxy_config.root+"api/datatypes",function(j){for(key in j){h.select_extension.push([j[key],j[key]])}});f.jsonFromUrl(galaxy_config.root+"api/genomes",function(j){var k=h.select_genome[0];h.select_genome=[];for(key in j){if(j[key].length>1){if(j[key][1]!==k[1]){h.select_genome.push(j[key])}}}h.select_genome.sort(function(m,l){return m[0]>l[0]?1:m[0]<l[0]?-1:0});h.select_genome.unshift(k)});if(i){this.options=_.defaults(i,this.options)}this.collection.on("remove",function(j){h._eventRemove(j)});this.collection.on("change:genome",function(k){var j=k.get("genome");h.collection.each(function(l){if(l.get("status")=="init"&&l.get("genome")=="?"){l.set("genome",j)}})})},_eventShow:function(j){j.preventDefault();if(!this.modal){var h=this;this.modal=new a.GalaxyModal({title:"Download data directly from web or upload files from your disk",body:this._template("upload-box","upload-info"),buttons:{"Choose local file":function(){h.uploadbox.select()},"Choose FTP file":function(){h._eventFtp()},"Create new file":function(){h._eventCreate()},Start:function(){h._eventStart()},Pause:function(){h._eventStop()},Reset:function(){h._eventReset()},Close:function(){h.modal.hide()},},height:"400",width:"900",closing_events:true});this.setElement("#upload-box");var h=this;this.uploadbox=this.$el.uploadbox({announce:function(k,l,m){h._eventAnnounce(k,l,m)},initialize:function(k,l,m){return h._eventInitialize(k,l,m)},progress:function(k,l,m){h._eventProgress(k,l,m)},success:function(k,l,m){h._eventSuccess(k,l,m)},error:function(k,l,m){h._eventError(k,l,m)},complete:function(){h._eventComplete()}});this._updateScreen();if(this.options.ftp_upload_dir&&this.options.ftp_upload_site){var i=this.modal.getButton("Choose FTP file");this.ftp=new d.View({title:"FTP files",container:i})}else{this.modal.hideButton("Choose FTP file")}}this.modal.show()},_eventRemove:function(i){var h=i.get("status");if(h=="success"){this.counter.success--}else{if(h=="error"){this.counter.error--}else{this.counter.announce--}}this._updateScreen();this.uploadbox.remove(i.id)},_eventAnnounce:function(h,i,k){this.counter.announce++;this._updateScreen();var j=new b(this,{id:h,file_name:i.name,file_size:i.size,file_mode:i.mode,file_path:i.path});this.collection.add(j.model);$(this.el).find("tbody:first").append(j.$el);j.render()},_eventInitialize:function(m,j,s){var k=this.collection.get(m);k.set("status","running");var o=k.get("file_name");var n=k.get("file_path");var h=k.get("file_mode");var p=k.get("extension");var r=k.get("genome");var q=k.get("url_paste");var l=k.get("space_to_tabs");var i=k.get("to_posix_lines");if(!q&&!(j.size>0)){return null}this.uploadbox.configure({url:this.options.nginx_upload_path});if(h=="ftp"){this.uploadbox.configure({paramname:""})}else{this.uploadbox.configure({paramname:"files_0|file_data"})}tool_input={};tool_input.dbkey=r;tool_input.file_type=p;tool_input["files_0|type"]="upload_dataset";tool_input["files_0|url_paste"]=q;tool_input["files_0|ftp_files"]=n;tool_input.space_to_tabs=l;tool_input.to_posix_lines=i;data={};data.history_id=this.current_history;data.tool_id="upload1";data.inputs=JSON.stringify(tool_input);return data},_eventProgress:function(i,j,h){var k=this.collection.get(i);k.set("percentage",h);this.ui_button.set("percentage",this._upload_percentage(h,j.size))},_eventSuccess:function(i,j,l){var k=this.collection.get(i);k.set("status","success");var h=k.get("file_size");this.ui_button.set("percentage",this._upload_percentage(100,h));this.upload_completed+=h*100;this.counter.announce--;this.counter.success++;this._updateScreen();Galaxy.currHistoryPanel.refreshHdas()},_eventError:function(h,i,k){var j=this.collection.get(h);j.set("status","error");j.set("info",k);this.ui_button.set("percentage",this._upload_percentage(100,i.size));this.ui_button.set("status","danger");this.upload_completed+=i.size*100;this.counter.announce--;this.counter.error++;this._updateScreen()},_eventComplete:function(){this.collection.each(function(h){if(h.get("status")=="queued"){h.set("status","init")}});this.counter.running=0;this._updateScreen()},_eventFtp:function(){if(!this.ftp.visible){this.ftp.empty();this.ftp.append((new g(this)).$el);this.ftp.show()}else{this.ftp.hide()}},_eventCreate:function(){this.uploadbox.add([{name:"New File",size:0,mode:"new"}])},_eventStart:function(){if(this.counter.announce==0||this.counter.running>0){return}var h=this;this.upload_size=0;this.upload_completed=0;this.collection.each(function(i){if(i.get("status")=="init"){i.set("status","queued");h.upload_size+=i.get("file_size")}});this.ui_button.set("percentage",0);this.ui_button.set("status","success");this.current_history=Galaxy.currHistoryPanel.model.get("id");this.counter.running=this.counter.announce;this._updateScreen();this.uploadbox.start()},_eventStop:function(){if(this.counter.running==0){return}this.ui_button.set("status","info");this.uploadbox.stop();$("#upload-info").html("Queue will pause after completing the current file...")},_eventReset:function(){if(this.counter.running==0){this.collection.reset();this.counter.reset();this._updateScreen();this.uploadbox.reset();this.ui_button.set("percentage",0)}},_updateScreen:function(){if(this.counter.announce==0){if(this.uploadbox.compatible()){message="You can Drag & Drop files into this box."}else{message="Unfortunately, your browser does not support multiple file uploads or drag&drop.<br>Some supported browsers are: Firefox 4+, Chrome 7+, IE 10+, Opera 12+ or Safari 6+."}}else{if(this.counter.running==0){message="You added "+this.counter.announce+" file(s) to the queue. Add more files or click 'Start' to proceed."}else{message="Please wait..."+this.counter.announce+" out of "+this.counter.running+" remaining."}}$("#upload-info").html(message);if(this.counter.running==0&&this.counter.announce+this.counter.success+this.counter.error>0){this.modal.enableButton("Reset")}else{this.modal.disableButton("Reset")}if(this.counter.running==0&&this.counter.announce>0){this.modal.enableButton("Start")}else{this.modal.disableButton("Start")}if(this.counter.running>0){this.modal.enableButton("Pause")}else{this.modal.disableButton("Pause")}if(this.counter.running==0){this.modal.enableButton("Choose local file");this.modal.enableButton("Choose FTP file");this.modal.enableButton("Create new file")}else{this.modal.disableButton("Choose local file");this.modal.disableButton("Choose FTP file");this.modal.disableButton("Create new file")}if(this.counter.announce+this.counter.success+this.counter.error>0){$(this.el).find("#upload-table").show()}else{$(this.el).find("#upload-table").hide()}},_upload_percentage:function(h,i){return(this.upload_completed+(h*i))/this.upload_size},_template:function(i,h){return'<div id="'+i+'" class="upload-box"><table id="upload-table" class="table table-striped" style="display: none;"><thead><tr><th>Name</th><th>Size</th><th>Type</th><th>Genome</th><th>Settings</th><th>Status</th><th></th></tr></thead><tbody></tbody></table></div><h6 id="'+h+'" class="upload-info"></h6>'}})});
\ No newline at end of file
+define(["galaxy.modal","utils/utils","mvc/upload/upload-button","mvc/upload/upload-model","mvc/upload/upload-row","mvc/upload/upload-ftp","mvc/ui.popover","mvc/ui","utils/uploadbox"],function(a,f,e,c,b,g,d){return Backbone.View.extend({options:{nginx_upload_path:""},modal:null,ui_button:null,uploadbox:null,current_history:null,upload_size:0,select_extension:[["Auto-detect","auto"]],select_genome:[["Unspecified (?)","?"]],collection:new c.Collection(),ftp:null,counter:{announce:0,success:0,error:0,running:0,reset:function(){this.announce=this.success=this.error=this.running=0}},initialize:function(i){var h=this;if(!Galaxy.currHistoryPanel||!Galaxy.currHistoryPanel.model){window.setTimeout(function(){h.initialize()},500);return}if(!Galaxy.currUser.get("id")){return}this.ui_button=new e.Model({icon:"fa-upload",tooltip:"Download from URL or upload files from disk",label:"Load Data",onclick:function(j){if(j){h._eventShow(j)}},onunload:function(){if(h.counter.running>0){return"Several uploads are still processing."}}});$("#left .unified-panel-header-inner").append((new e.View(this.ui_button)).$el);var h=this;f.jsonFromUrl(galaxy_config.root+"api/datatypes",function(j){for(key in j){h.select_extension.push([j[key],j[key]])}});f.jsonFromUrl(galaxy_config.root+"api/genomes",function(j){var k=h.select_genome[0];h.select_genome=[];for(key in j){if(j[key].length>1){if(j[key][1]!==k[1]){h.select_genome.push(j[key])}}}h.select_genome.sort(function(m,l){return m[0]>l[0]?1:m[0]<l[0]?-1:0});h.select_genome.unshift(k)});if(i){this.options=_.defaults(i,this.options)}this.collection.on("remove",function(j){h._eventRemove(j)});this.collection.on("change:genome",function(k){var j=k.get("genome");h.collection.each(function(l){if(l.get("status")=="init"&&l.get("genome")=="?"){l.set("genome",j)}})})},_eventShow:function(j){j.preventDefault();if(!this.modal){var h=this;this.modal=new a.GalaxyModal({title:"Download data directly from web or upload files from your disk",body:this._template("upload-box","upload-info"),buttons:{"Choose local file":function(){h.uploadbox.select()},"Choose FTP file":function(){h._eventFtp()},"Create new file":function(){h._eventCreate()},Start:function(){h._eventStart()},Pause:function(){h._eventStop()},Reset:function(){h._eventReset()},Close:function(){h.modal.hide()},},height:"400",width:"900",closing_events:true});this.setElement("#upload-box");var h=this;this.uploadbox=this.$el.uploadbox({announce:function(k,l,m){h._eventAnnounce(k,l,m)},initialize:function(k,l,m){return h._eventInitialize(k,l,m)},progress:function(k,l,m){h._eventProgress(k,l,m)},success:function(k,l,m){h._eventSuccess(k,l,m)},error:function(k,l,m){h._eventError(k,l,m)},complete:function(){h._eventComplete()}});this._updateScreen();if(this.options.ftp_upload_dir&&this.options.ftp_upload_site){var i=this.modal.getButton("Choose FTP file");this.ftp=new d.View({title:"FTP files",container:i})}else{this.modal.hideButton("Choose FTP file")}}this.modal.show()},_eventRemove:function(i){var h=i.get("status");if(h=="success"){this.counter.success--}else{if(h=="error"){this.counter.error--}else{this.counter.announce--}}this._updateScreen();this.uploadbox.remove(i.id)},_eventAnnounce:function(h,i,k){this.counter.announce++;this._updateScreen();var j=new b(this,{id:h,file_name:i.name,file_size:i.size,file_mode:i.mode,file_path:i.path});this.collection.add(j.model);$(this.el).find("tbody:first").append(j.$el);j.render()},_eventInitialize:function(m,j,s){var k=this.collection.get(m);k.set("status","running");var o=k.get("file_name");var n=k.get("file_path");var h=k.get("file_mode");var p=k.get("extension");var r=k.get("genome");var q=k.get("url_paste");var l=k.get("space_to_tabs");var i=k.get("to_posix_lines");if(!q&&!(j.size>0)){return null}this.uploadbox.configure({url:this.options.nginx_upload_path});if(h=="ftp"){this.uploadbox.configure({paramname:""})}else{this.uploadbox.configure({paramname:"files_0|file_data"})}tool_input={};tool_input.dbkey=r;tool_input.file_type=p;tool_input["files_0|type"]="upload_dataset";tool_input["files_0|url_paste"]=q;tool_input.space_to_tabs=l;tool_input.to_posix_lines=i;if(h=="ftp"){tool_input["files_0|ftp_files"]=n}data={};data.history_id=this.current_history;data.tool_id="upload1";data.inputs=JSON.stringify(tool_input);return data},_eventProgress:function(i,j,h){var k=this.collection.get(i);k.set("percentage",h);this.ui_button.set("percentage",this._upload_percentage(h,j.size))},_eventSuccess:function(i,j,l){var k=this.collection.get(i);k.set("status","success");var h=k.get("file_size");this.ui_button.set("percentage",this._upload_percentage(100,h));this.upload_completed+=h*100;this.counter.announce--;this.counter.success++;this._updateScreen();Galaxy.currHistoryPanel.refreshHdas()},_eventError:function(h,i,k){var j=this.collection.get(h);j.set("status","error");j.set("info",k);this.ui_button.set("percentage",this._upload_percentage(100,i.size));this.ui_button.set("status","danger");this.upload_completed+=i.size*100;this.counter.announce--;this.counter.error++;this._updateScreen()},_eventComplete:function(){this.collection.each(function(h){if(h.get("status")=="queued"){h.set("status","init")}});this.counter.running=0;this._updateScreen()},_eventFtp:function(){if(!this.ftp.visible){this.ftp.empty();this.ftp.append((new g(this)).$el);this.ftp.show()}else{this.ftp.hide()}},_eventCreate:function(){this.uploadbox.add([{name:"New File",size:0,mode:"new"}])},_eventStart:function(){if(this.counter.announce==0||this.counter.running>0){return}var h=this;this.upload_size=0;this.upload_completed=0;this.collection.each(function(i){if(i.get("status")=="init"){i.set("status","queued");h.upload_size+=i.get("file_size")}});this.ui_button.set("percentage",0);this.ui_button.set("status","success");this.current_history=Galaxy.currHistoryPanel.model.get("id");this.counter.running=this.counter.announce;this._updateScreen();this.uploadbox.start()},_eventStop:function(){if(this.counter.running==0){return}this.ui_button.set("status","info");this.uploadbox.stop();$("#upload-info").html("Queue will pause after completing the current file...")},_eventReset:function(){if(this.counter.running==0){this.collection.reset();this.counter.reset();this._updateScreen();this.uploadbox.reset();this.ui_button.set("percentage",0)}},_updateScreen:function(){if(this.counter.announce==0){if(this.uploadbox.compatible()){message="You can Drag & Drop files into this box."}else{message="Unfortunately, your browser does not support multiple file uploads or drag&drop.<br>Some supported browsers are: Firefox 4+, Chrome 7+, IE 10+, Opera 12+ or Safari 6+."}}else{if(this.counter.running==0){message="You added "+this.counter.announce+" file(s) to the queue. Add more files or click 'Start' to proceed."}else{message="Please wait..."+this.counter.announce+" out of "+this.counter.running+" remaining."}}$("#upload-info").html(message);if(this.counter.running==0&&this.counter.announce+this.counter.success+this.counter.error>0){this.modal.enableButton("Reset")}else{this.modal.disableButton("Reset")}if(this.counter.running==0&&this.counter.announce>0){this.modal.enableButton("Start")}else{this.modal.disableButton("Start")}if(this.counter.running>0){this.modal.enableButton("Pause")}else{this.modal.disableButton("Pause")}if(this.counter.running==0){this.modal.enableButton("Choose local file");this.modal.enableButton("Choose FTP file");this.modal.enableButton("Create new file")}else{this.modal.disableButton("Choose local file");this.modal.disableButton("Choose FTP file");this.modal.disableButton("Create new file")}if(this.counter.announce+this.counter.success+this.counter.error>0){$(this.el).find("#upload-table").show()}else{$(this.el).find("#upload-table").hide()}},_upload_percentage:function(h,i){return(this.upload_completed+(h*i))/this.upload_size},_template:function(i,h){return'<div id="'+i+'" class="upload-box"><table id="upload-table" class="table table-striped" style="display: none;"><thead><tr><th>Name</th><th>Size</th><th>Type</th><th>Genome</th><th>Settings</th><th>Status</th><th></th></tr></thead><tbody></tbody></table></div><h6 id="'+h+'" class="upload-info"></h6>'}})});
\ No newline at end of file
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.
1
0
commit/galaxy-central: guerler: Upload: Add ftp support, separate modules, Ui: Add popovers, Grids: Fix search icon
by commits-noreply@bitbucket.org 07 Feb '14
by commits-noreply@bitbucket.org 07 Feb '14
07 Feb '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/d8fcf33cbe4a/
Changeset: d8fcf33cbe4a
User: guerler
Date: 2014-02-08 00:47:25
Summary: Upload: Add ftp support, separate modules, Ui: Add popovers, Grids: Fix search icon
Affected #: 31 files
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 lib/galaxy/webapps/galaxy/api/ftp_files.py
--- /dev/null
+++ b/lib/galaxy/webapps/galaxy/api/ftp_files.py
@@ -0,0 +1,52 @@
+"""
+API operations on ftp files.
+"""
+import logging
+from galaxy.web.base.controller import BaseAPIController, url_for
+from galaxy import web
+import os.path
+import os
+import time
+from operator import itemgetter
+
+log = logging.getLogger( __name__ )
+
+class FTPFilesAPIController( BaseAPIController ):
+ @web.expose_api
+ def index( self, trans, **kwd ):
+ """
+ GET /api/ftp_files/
+ Displays local files.
+ """
+ # initialize response
+ response = []
+
+ # identify ftp directory
+ user_ftp_base_dir = trans.app.config.ftp_upload_dir
+ if user_ftp_base_dir is None:
+ return []
+
+ # identify user sub directory
+ user_ftp_dir = None
+ if user_ftp_base_dir is not None and trans is not None and trans.user is not None:
+ identifier = trans.app.config.ftp_upload_dir_identifier
+ user_ftp_dir = os.path.join( user_ftp_base_dir, getattr(trans.user, identifier) )
+ if user_ftp_dir is None:
+ return []
+
+ # read directory
+ if os.path.exists( user_ftp_dir ):
+ for ( dirpath, dirnames, filenames ) in os.walk( user_ftp_dir ):
+ for filename in filenames:
+ path = os.path.relpath( os.path.join( dirpath, filename ), user_ftp_dir )
+ statinfo = os.lstat( os.path.join( dirpath, filename ) )
+ response.append( dict( path = path,
+ size = statinfo.st_size,
+ ctime = time.strftime( "%m/%d/%Y %I:%M:%S %p", time.localtime( statinfo.st_ctime ) ) ) )
+
+ # sort by path
+ response = sorted(response, key=itemgetter("path"))
+
+ # return
+ return response
+
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -127,6 +127,7 @@
webapp.mapper.resource( 'form', 'forms', path_prefix='/api' )
webapp.mapper.resource( 'request_type', 'request_types', path_prefix='/api' )
webapp.mapper.resource( 'role', 'roles', path_prefix='/api' )
+ webapp.mapper.resource( 'ftp_file', 'ftp_files', path_prefix='/api' )
webapp.mapper.resource( 'group', 'groups', path_prefix='/api' )
webapp.mapper.resource_with_deleted( 'quota', 'quotas', path_prefix='/api' )
webapp.mapper.connect( '/api/tools/{id:.+?}', action='show', controller="tools" )
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -86,6 +86,12 @@
this.$buttons.find('#' + button_id).hide();
},
+ // get button
+ getButton: function(name) {
+ var button_id = this.buttonList[name];
+ return this.$buttons.find('#' + button_id);
+ },
+
// returns scroll top for body element
scrollTop: function() {
return this.$body.scrollTop();
@@ -175,7 +181,7 @@
'<button type="button" class="close" style="display: none;">×</button>' +
'<h4 class="title">' + title + '</h4>' +
'</div>' +
- '<div class="modal-body"></div>' +
+ '<div class="modal-body" style="position: static;"></div>' +
'<div class="modal-footer">' +
'<div class="buttons" style="float: right;"></div>' +
'</div>' +
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/scripts/mvc/grid/grid-template.js
--- a/static/scripts/mvc/grid/grid-template.js
+++ b/static/scripts/mvc/grid/grid-template.js
@@ -556,7 +556,7 @@
tmpl += '<span class="search-box">' +
'<input class="search-box-input" id="input-' + column_key + '-filter" name="f-' + column_key + '" type="text" placeholder="' + value + '" size="' + size + '"/>' +
'<button type="submit" style="background: transparent; border: none; padding: 4px; margin: 0px;">' +
- '<i class="fa fa-plus-circle"></i>' +
+ '<i class="fa fa-search"></i>' +
'</button>' +
'</span>' +
'</form>';
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/scripts/mvc/ui.popover.js
--- /dev/null
+++ b/static/scripts/mvc/ui.popover.js
@@ -0,0 +1,169 @@
+// dependencies
+define(['utils/utils'], function(Utils) {
+
+var View = Backbone.View.extend({
+
+ // default options
+ optionsDefault: {
+ with_close : true,
+ container : 'body',
+ title : null,
+ placement : 'top'
+ },
+
+ // visibility flag
+ visible: false,
+
+ // initialize
+ initialize: function (options) {
+
+ // link this
+ var self = this;
+
+ // update options
+ this.options = _.defaults(options, this.optionsDefault);
+
+ // set element
+ this.setElement(this._template(this.options));
+
+ // attach popover to parent
+ this.options.container.parent().append(this.$el);
+
+ // attach close
+ if (this.options.with_close) {
+ this.$el.find('#close').on('click', function() { self.hide(); });
+ }
+
+ // generate unique id
+ this.uuid = Utils.uuid();
+
+ // add event to hide if click is outside of popup
+ var self = this;
+ $('body').on('mousedown.' + this.uuid, function(e) { self._hide(e) });
+ },
+
+ // title
+ title: function(val) {
+ if (val !== undefined) {
+ this.$el.find('.popover-title-label').html(val);
+ }
+ },
+
+ // show
+ show: function () {
+ // show popover
+ this.$el.show();
+ this.visible = true;
+
+ // calculate position
+ var position = this._get_placement(this.options.placement);
+
+ // set position
+ this.$el.css(position);
+ },
+
+ // calculate position and error
+ _get_placement: function(placement) {
+ // get popover dimensions
+ var width = this._get_width(this.$el);
+ var height = this.$el.height();
+
+ // get container details
+ var $container = this.options.container;
+ var container_width = this._get_width($container);
+ var container_height = this._get_height($container);
+ var container_position = $container.position();
+
+ // initialize position
+ var top = 0;
+ var left = 0;
+
+ // calculate position
+ if (placement == 'top' || placement == 'bottom') {
+ left = container_position.left - width + (container_width + width) / 2;
+ if (placement == 'top') {
+ top = container_position.top - height - 5;
+ } else {
+ top = container_position.top + container_height + 5;
+ }
+ }
+
+ // return
+ return {top: top, left: left};
+ },
+
+ // width
+ _get_width: function($el) {
+ return $el.width() + parseInt($el.css('padding-left')) + parseInt($el.css('padding-right'))
+ },
+
+ // heigth
+ _get_height: function($el) {
+ return $el.height() + parseInt($el.css('padding-top')) + parseInt($el.css('padding-bottom'))
+ },
+
+ // hide
+ hide: function () {
+ this.$el.hide();
+ this.visible = false;
+ },
+
+ // append
+ append: function($el) {
+ this.$el.find('.popover-content').append($el);
+ },
+
+ // empty
+ empty: function($el) {
+ this.$el.find('.popover-content').empty();
+ },
+
+ // remove
+ remove: function() {
+ // remove event handler
+ $('body').off('mousedown.' + this.uuid);
+
+ // remove element from dom
+ this.$el.remove();
+ },
+
+ // remove
+ _hide : function(e) {
+ //the 'is' for buttons that trigger popups
+ //the 'has' for icons within a button that triggers a popup
+ if (!$(this.options.container).is(e.target) &&
+ !$(this.el).is(e.target) &&
+ $(this.el).has(e.target).length === 0) {
+ this.hide();
+ }
+ },
+
+ // template
+ _template: function(options) {
+ var tmpl = '<div class="popover-view popover fade ' + options.placement + ' in">' +
+ '<div class="arrow"></div>' +
+ '<div class="popover-title">' +
+ '<div class="popover-title-label">' +
+ options.title +
+ '</div>';
+
+ // add close icon
+ if (options.with_close) {
+ tmpl += '<div id="close" class="popover-close fa fa-times-circle"></div>';
+ }
+
+ // finalize
+ tmpl += '</div>' +
+ '<div class="popover-content"></div>' +
+ '</div>';
+
+ // return
+ return tmpl;
+ }
+});
+
+return {
+ View: View
+}
+
+});
\ No newline at end of file
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/scripts/mvc/upload/upload-button.js
--- /dev/null
+++ b/static/scripts/mvc/upload/upload-button.js
@@ -0,0 +1,102 @@
+// dependencies
+define([], function() {
+
+// create model
+var Model = Backbone.Model.extend({
+ defaults: {
+ percentage : 0,
+ icon : 'fa-circle',
+ label : '',
+ status : ''
+ }
+});
+
+// progress bar button on ui
+var View = Backbone.View.extend({
+ // model
+ model : null,
+
+ // initialize
+ initialize : function(model) {
+ // link this
+ var self = this;
+
+ // create model
+ this.model = model;
+
+ // get options
+ this.options = this.model.attributes;
+
+ // create new element
+ this.setElement(this._template(this.options));
+
+ // add event
+ $(this.el).on('click', this.options.onclick);
+
+ // add tooltip
+ if (this.options.tooltip) {
+ $(this.el).tooltip({title: this.options.tooltip, placement: 'bottom'});
+ }
+
+ // events
+ this.model.on('change:percentage', function() {
+ self._percentage(self.model.get('percentage'));
+ });
+ this.model.on('change:status', function() {
+ self._status(self.model.get('status'));
+ });
+
+ // unload event
+ var self = this;
+ $(window).on('beforeunload', function() {
+ var text = "";
+ if (self.options.onunload) {
+ text = self.options.onunload();
+ }
+ if (text != "") {
+ return text;
+ }
+ });
+ },
+
+ // set status
+ _status: function(value) {
+ var $el = this.$el.find('.progress-bar');
+ $el.removeClass();
+ $el.addClass('progress-bar');
+ $el.addClass('progress-bar-notransition');
+ if (value != '') {
+ $el.addClass('progress-bar-' + value);
+ console.log($el);
+ }
+ },
+
+ // set percentage
+ _percentage: function(value) {
+ var $el = this.$el.find('.progress-bar');
+ $el.css({ width : value + '%' });
+ },
+
+ // template
+ _template: function(options) {
+ return '<div style="float: right">' +
+ '<div class="upload-button">' +
+ '<div class="progress">' +
+ '<div class="progress-bar"></div>' +
+ '</div>' +
+ '<div id="label" class="label">' +
+ '<a class="panel-header-button" href="javascript:void(0)">' +
+ '<span class="fa fa-upload"></span>' +
+ '</a>' +
+ '</div>' +
+ '</div>' +
+ '</div>';
+ }
+});
+
+return {
+ Model: Model,
+ View: View
+};
+
+});
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/scripts/mvc/upload/upload-ftp.js
--- /dev/null
+++ b/static/scripts/mvc/upload/upload-ftp.js
@@ -0,0 +1,173 @@
+// dependencies
+define(['utils/utils'], function(Utils) {
+
+// item view
+return Backbone.View.extend({
+ // options
+ options: {
+ class_add : 'upload-icon-button fa fa-square-o',
+ class_remove : 'upload-icon-button fa fa-check-square-o'
+ },
+
+ // render
+ initialize: function(app) {
+ // link app
+ this.app = app;
+
+ // link this
+ var self = this;
+
+ // set template
+ this.setElement(this._template());
+
+ // load extension
+ Utils.jsonFromUrl(galaxy_config.root + 'api/ftp_files', function(ftp_files) { self._fill(ftp_files); });
+ },
+
+ // events
+ events: {
+ 'mousedown' : function(e) { e.preventDefault(); }
+ },
+
+ // fill table
+ _fill: function(ftp_files) {
+
+ if (ftp_files.length > 0) {
+ // add table
+ this.$el.find('#upload-ftp-content').html($(this._template_table()));
+
+ // add files to table
+ var size = 0;
+ for (key in ftp_files) {
+ this.add(ftp_files[key]);
+ size += ftp_files[key].size;
+ }
+
+ // update stats
+ this.$el.find('#upload-ftp-number').html(ftp_files.length + ' files');
+ this.$el.find('#upload-ftp-disk').html(Utils.bytesToString (size, true));
+ } else {
+ // add info
+ this.$el.find('#upload-ftp-content').html($(this._template_info()));
+ }
+
+ // hide spinner
+ this.$el.find('#upload-ftp-wait').hide();
+ },
+
+ // add
+ add: function(ftp_file) {
+ // create new item
+ var $it = $(this._template_row(ftp_file));
+
+ // append to table
+ $(this.el).find('tbody').append($it);
+
+ // find model and set initial 'add' icon class
+ var icon_class = '';
+ if (this._find(ftp_file)) {
+ icon_class = this.options.class_remove;
+ } else {
+ icon_class = this.options.class_add;
+ }
+ $it.find('#upload-ftp-add').addClass(icon_class);
+
+ // click to add ftp files
+ var self = this;
+ $it.find('#upload-ftp-add').on('click', function() {
+ // find model
+ var model_index = self._find(ftp_file);
+
+ // update icon
+ $(this).removeClass();
+
+ // add model
+ if (!model_index) {
+ // add to uploadbox
+ self.app.uploadbox.add([{
+ mode : 'ftp',
+ name : ftp_file.path,
+ size : ftp_file.size,
+ path : ftp_file.path
+ }]);
+
+ // add new icon class
+ $(this).addClass(self.options.class_remove);
+ } else {
+ // remove
+ self.app.collection.remove(model_index);
+
+ // add new icon class
+ $(this).addClass(self.options.class_add);
+ }
+ });
+ },
+
+ // get model index
+ _find: function(ftp_file) {
+ // check if exists already
+ var filtered = this.app.collection.where({file_path : ftp_file.path});
+ var model_index = null;
+ for (var key in filtered) {
+ var item = filtered[key];
+ if (item.get('status') == 'init' && item.get('file_mode') == 'ftp') {
+ model_index = item.get('id');
+ }
+ }
+ return model_index;
+ },
+
+ // template row
+ _template_row: function(options) {
+ return '<tr>' +
+ '<td><div id="upload-ftp-add"/></td>' +
+ '<td>' + options.path + '</td>' +
+ '<td style="white-space: nowrap;">' + Utils.bytesToString(options.size) + '</td>' +
+ '<td style="white-space: nowrap;">' + options.ctime + '</td>' +
+ '</tr>';
+ },
+
+ // load table template
+ _template_table: function()
+ {
+ return '<span style="whitespace: nowrap; float: left;">Available files: </span>' +
+ '<span style="whitespace: nowrap; float: right;">' +
+ '<span class="upload-icon fa fa-file-text-o"/>' +
+ '<span id="upload-ftp-number"/> ' +
+ '<span class="upload-icon fa fa-hdd-o"/>' +
+ '<span id="upload-ftp-disk"/>' +
+ '</span>' +
+ '<table class="grid">' +
+ '<thead>' +
+ '<tr>' +
+ '<th></th>' +
+ '<th>Name</th>' +
+ '<th>Size</th>' +
+ '<th>Created</th>' +
+ '</tr>' +
+ '</thead>' +
+ '<tbody></tbody>' +
+ '</table>';
+ },
+
+ // load table template
+ _template_info: function()
+ {
+ return '<div class="upload-ftp-warning warningmessage">' +
+ 'Your FTP directory does not contain any files.' +
+ '</div>';
+ },
+
+ // load html template
+ _template: function()
+ {
+ return '<div class="upload-ftp">' +
+ '<div id="upload-ftp-wait" class="upload-ftp-wait fa fa-spinner fa-spin"/>' +
+ '<div class="upload-ftp-help">This Galaxy server allows you to upload files via FTP. To upload some files, log in to the FTP server at <strong>' + this.app.options.ftp_upload_site + '</strong> using your Galaxy credentials (email address and password).</div>' +
+ '<div id="upload-ftp-content"></div>' +
+ '<div>';
+ }
+
+});
+
+});
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/scripts/mvc/upload/upload-model.js
--- a/static/scripts/mvc/upload/upload-model.js
+++ b/static/scripts/mvc/upload/upload-model.js
@@ -7,9 +7,16 @@
extension : 'auto',
genome : '?',
url_paste : '',
+ status : 'init',
+ info : null,
+ file_mode : 'local',
+ file_size : 0,
+ file_type : null,
+ file_path : '',
+
+ // settings
space_to_tabs : false,
- status : 'init',
- info : null
+ to_posix_lines : true
}
});
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/scripts/mvc/upload/upload-row.js
--- a/static/scripts/mvc/upload/upload-row.js
+++ b/static/scripts/mvc/upload/upload-row.js
@@ -1,5 +1,16 @@
// dependencies
-define(['mvc/upload/upload-model', 'mvc/upload/upload-extensions'], function(UploadModel, UploadExtensions) {
+define(['utils/utils',
+ 'mvc/upload/upload-model',
+ 'mvc/upload/upload-extensions',
+ 'mvc/upload/upload-settings',
+ 'mvc/ui.popover'],
+
+ function( Utils,
+ UploadModel,
+ UploadExtensions,
+ UploadSettings,
+ Popover
+ ) {
// item view
return Backbone.View.extend({
@@ -11,13 +22,16 @@
// states
status_classes : {
- init : 'symbol fa fa-trash-o',
- queued : 'symbol fa fa-spinner fa-spin',
- running : 'symbol fa fa-spinner fa-spin',
- success : 'symbol fa fa-check',
- error : 'symbol fa fa-exclamation-triangle'
+ init : 'upload-icon-button fa fa-trash-o',
+ queued : 'upload-icon fa fa-spinner fa-spin',
+ running : 'upload-icon fa fa-spinner fa-spin',
+ success : 'upload-icon-button fa fa-check',
+ error : 'upload-icon-button fa fa-exclamation-triangle'
},
+ // handle for settings popover
+ settings: null,
+
// render
initialize: function(app, options) {
// link app
@@ -35,36 +49,32 @@
// link item
var it = this.$el;
+ // append popup to settings icon
+ this.settings = new Popover.View({
+ title : 'Upload configuration',
+ container : it.find('#settings'),
+ placement : 'bottom'
+ });
+
+ //
+ // ui events
+ //
+
// handle click event
- it.find('#symbol').on('click', function() {
- // get current status
- var status = self.model.get('status');
-
- // only remove from queue if not in processing line
- if (status == 'init' || status == 'success' || status == 'error')
- {
- // remove from collection
- self.app.collection.remove(self.model);
- }
- });
+ it.find('#symbol').on('click', function() { self._removeRow(); });
- // handle mouse over
- it.find('#extension_info').on('mouseover' , function() { self._showExtensionInfo(); })
- .on('mouseleave', function() { self._hideExtensionInfo(); });
+ // handle extension info popover
+ it.find('#extension-info').on('click' , function(e) { self._showExtensionInfo(); })
+ .on('mousedown', function(e) { e.preventDefault(); });
+
+ // handle settings popover
+ it.find('#settings').on('click' , function(e) { self._showSettings(); })
+ .on('mousedown', function(e) { e.preventDefault(); });
// handle text editing event
- it.find('#text-content').on('keyup', function() {
- // get properties
- var $el = it.find('#text-content');
- var value = $el.val();
- var count = value.length;
-
- // update size string
- it.find('#size').html(self._formatSize (count));
-
- // update url paste content
- self.model.set('url_paste', value);
- self.model.set('file_size', count);
+ it.find('#text-content').on('keyup', function(e) {
+ self.model.set('url_paste', $(e.target).val());
+ self.model.set('file_size', $(e.target).val().length);
});
// handle genome selection
@@ -75,7 +85,6 @@
// handle extension selection
it.find('#extension').on('change', function(e) {
self.model.set('extension', $(e.target).val());
- self.$el.find('#extension_info').popover('destroy');
});
// handle space to tabs button
@@ -83,19 +92,27 @@
self.model.set('space_to_tabs', $(e.target).prop('checked'));
});
- // events
+ //
+ // model events
+ //
this.model.on('change:percentage', function() {
self._refreshPercentage();
});
this.model.on('change:status', function() {
self._refreshStatus();
});
+ this.model.on('change:extension', function() {
+ self._destroyExtensionInfo();
+ });
this.model.on('change:info', function() {
self._refreshInfo();
});
this.model.on('change:genome', function() {
self._refreshGenome();
});
+ this.model.on('change:file_size', function() {
+ self._refreshFileSize();
+ });
this.model.on('remove', function() {
self._destroyExtensionInfo();
self.remove();
@@ -109,8 +126,9 @@
// render
render: function() {
// read model
- var file_name = this.model.get('file_name');
- var file_size = this.model.get('file_size');
+ var file_name = this.model.get('file_name');
+ var file_size = this.model.get('file_size');
+ var file_mode = this.model.get('file_mode');
// link item
var it = this.$el;
@@ -119,10 +137,14 @@
it.find('#title').html(file_name);
// update info
- it.find('#size').html(this._formatSize (file_size));
+ it.find('#size').html(Utils.bytesToString (file_size));
- // activate text field if file content is zero
- if (file_size == -1)
+ // remove mode class
+ it.find('#mode').removeClass()
+ .addClass('mode');
+
+ // activate text field if file is new
+ if (file_mode == 'new')
{
// get text component
var text = it.find('#text');
@@ -141,9 +163,28 @@
// show text field
text.show();
+
+ // update icon
+ it.find('#mode').addClass('fa fa-pencil');
+ }
+
+ // file from local disk
+ if (file_mode == 'local') {
+ // update icon
+ it.find('#mode').addClass('fa fa-laptop');
+ }
+
+ // file from ftp
+ if (file_mode == 'ftp') {
+ // update icon
+ it.find('#mode').addClass('fa fa-code-fork');
}
},
+ //
+ // handle model events
+ //
+
// genome
_refreshGenome: function()
{
@@ -218,45 +259,69 @@
}
},
- // to string
- _formatSize : function (size)
- {
- // identify unit
- var unit = "";
- if (size >= 100000000000) { size = size / 100000000000; unit = 'TB'; } else
- if (size >= 100000000) { size = size / 100000000; unit = 'GB'; } else
- if (size >= 100000) { size = size / 100000; unit = 'MB'; } else
- if (size >= 100) { size = size / 100; unit = 'KB'; } else
- if (size > 0) { size = size * 10; unit = 'b'; } else
- return '<strong>-</strong>';
-
- // return formatted string
- return '<strong>' + (Math.round(size) / 10) + '</strong> ' + unit;
+ // refresh size
+ _refreshFileSize: function() {
+ var count = this.model.get('file_size');
+ this.$el.find('#size').html(Utils.bytesToString (count));
+ },
+
+ //
+ // handle ui events
+ //
+
+ // remove row
+ _removeRow: function() {
+ // get current status
+ var status = this.model.get('status');
+
+ // only remove from queue if not in processing line
+ if (status == 'init' || status == 'success' || status == 'error') {
+ // remove from collection
+ this.app.collection.remove(this.model);
+ }
},
// attach file info popup
_showExtensionInfo : function()
{
// initialize
- var self = this;
- var $el = $(this.el).find('#extension_info');
+ var $el = $(this.el).find('#extension-info');
var extension = this.model.get('extension');
var title = $(this.el).find('#extension').find('option:selected').text();
// create popup
- $el.popover({
- html: true,
- title: title,
- content: UploadExtensions(extension),
- placement: 'bottom',
- container: self.$el.parent()
- });
+ if (!this.extension_popup) {
+ this.extension_popup = new Popover.View({
+ content: UploadExtensions(extension),
+ placement: 'bottom',
+ container: $el
+ });
+ }
- // show popup
- $el.popover('show');
-
- // clear previous timers
- clearTimeout(this.popover_timeout);
+ // show / hide popup
+ if (!this.extension_popup.visible) {
+ this.extension_popup.title(title);
+ this.extension_popup.empty();
+ this.extension_popup.append(UploadExtensions(extension));
+ this.extension_popup.show();
+ } else {
+ this.extension_popup.hide();
+ }
+ },
+
+ // attach file info popup
+ _showSettings : function()
+ {
+ // check if popover is visible
+ if (!this.settings.visible) {
+ // show popover
+ this.settings.empty();
+ this.settings.append((new UploadSettings(this)).$el);
+ this.settings.show();
+ } else {
+ // hide popover
+ this.settings.hide();
+ }
},
// attach file info popup
@@ -272,9 +337,9 @@
// attach file info popup
_destroyExtensionInfo : function()
{
- this.$el.find('#extension_info').popover('destroy');
+ this.$el.find('#extension-info').popover('destroy');
},
-
+
// template
_template: function(options)
{
@@ -285,6 +350,7 @@
var tmpl = '<tr id="upload-item-' + options.id + '" class="upload-item">' +
'<td>' +
'<div style="position: relative;">' +
+ '<div id="mode"></div>' +
'<div id="title" class="title"></div>' +
'<div id="text" class="text">' +
'<div class="text-info">You can tell Galaxy to download data from web by entering URL in this box (one per line). You can also directly paste the contents of a file.</div>' +
@@ -292,7 +358,9 @@
'</div>' +
'</div>' +
'</td>' +
- '<td><div id="size" class="size"></div></td>';
+ '<td>' +
+ '<div id="size" class="size"></div>' +
+ '</td>';
// add file type selectore
tmpl += '<td>' +
@@ -300,7 +368,7 @@
for (key in self.app.select_extension)
tmpl += '<option value="' + self.app.select_extension[key][1] + '">' + self.app.select_extension[key][0] + '</option>';
tmpl += '</select>' +
- ' <i id="extension_info" class="fa fa-search" style="cursor: pointer;"/>' +
+ ' <i id="extension-info" class="upload-icon-button fa fa-search"/>' +
'</td>';
// add genome selector
@@ -312,7 +380,7 @@
'</td>';
// add next row
- tmpl += '<td><input id="space_to_tabs" type="checkbox"></input></td>' +
+ tmpl += '<td><div id="settings" class="upload-icon-button fa fa-gear"></div>' +
'<td>' +
'<div id="info" class="info">' +
'<div class="progress">' +
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/scripts/mvc/upload/upload-settings.js
--- /dev/null
+++ b/static/scripts/mvc/upload/upload-settings.js
@@ -0,0 +1,102 @@
+// dependencies
+define(['utils/utils'], function(Utils) {
+
+// item view
+return Backbone.View.extend({
+ // options
+ options: {
+ class_check : 'upload-icon-button fa fa-check-square-o',
+ class_uncheck : 'upload-icon-button fa fa-square-o'
+ },
+
+ // render
+ initialize: function(app) {
+ // link app
+ this.app = app;
+
+ // link this
+ var self = this;
+
+ // set template
+ this.setElement(this._template());
+
+ // link model
+ this.model = this.app.model;
+
+ // ui event: space to tabs
+ this.$el.find('#upload-space-to-tabs').on('click', function() {
+ self._switch_state('#upload-space-to-tabs', 'space_to_tabs');
+ });
+
+ // ui event: to posix
+ this.$el.find('#upload-to-posix-lines').on('click', function() {
+ self._switch_state('#upload-to-posix-lines', 'to_posix_lines');
+ });
+
+ // render
+ this.render();
+ },
+
+ // events
+ events: {
+ 'mousedown' : function(e) { e.preventDefault(); }
+ },
+
+ // render
+ render: function() {
+ // render states
+ this._render_state('#upload-space-to-tabs', this.model.get('space_to_tabs'));
+ this._render_state('#upload-to-posix-lines', this.model.get('to_posix_lines'));
+
+ // disable options
+ var $cover = this.$el.find('#upload-settings-cover');
+ if (this.model.get('status') != 'init') {
+ $cover.show();
+ } else {
+ $cover.hide();
+ }
+ },
+
+ // switch state
+ _switch_state: function (element_id, parameter_id) {
+ if (this.model.get('status') == 'init') {
+ var checked = !this.model.get(parameter_id);
+ this.model.set(parameter_id, checked);
+ this._render_state(element_id, checked);
+ }
+ },
+
+ // render state
+ _render_state: function (element_id, checked) {
+ // swith icon class
+ var $it = this.$el.find(element_id);
+ $it.removeClass();
+ if (checked) {
+ $it.addClass(this.options.class_check);
+ } else {
+ $it.addClass(this.options.class_uncheck);
+ }
+ },
+
+ // load template
+ _template: function()
+ {
+ return '<div class="upload-settings" style="position: relative;">' +
+ '<div id="upload-settings-cover" class="upload-settings-cover"/>' +
+ '<table class="table table-striped">' +
+ '<tbody>' +
+ '<tr>' +
+ '<td><div id="upload-space-to-tabs"/></td>' +
+ '<td>Convert spaces to tabs</td>' +
+ '</tr>' +
+ '<tr>' +
+ '<td><div id="upload-to-posix-lines"/></td>' +
+ '<td>Use POSIX standard</td>' +
+ '</tr>' +
+ '</tbody>' +
+ '</table>' +
+ '</div>';
+ }
+});
+
+});
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/scripts/mvc/upload/upload-view.js
--- a/static/scripts/mvc/upload/upload-view.js
+++ b/static/scripts/mvc/upload/upload-view.js
@@ -1,117 +1,36 @@
// dependencies
define(["galaxy.modal",
"utils/utils",
+ "mvc/upload/upload-button",
"mvc/upload/upload-model",
"mvc/upload/upload-row",
+ "mvc/upload/upload-ftp",
+ "mvc/ui.popover",
"mvc/ui",
"utils/uploadbox"],
function( Modal,
Utils,
+ UploadButton,
UploadModel,
- UploadItem
+ UploadItem,
+ UploadFtp,
+ Popover
) {
-// create model
-var ProgressButtonModel = Backbone.Model.extend({
- defaults: {
- percentage : 0,
- icon : 'fa-circle',
- label : '',
- status : ''
- }
-});
-
-// progress bar button on ui
-var ProgressButton = Backbone.View.extend({
- // model
- model : null,
-
- // initialize
- initialize : function(model) {
- // link this
- var self = this;
-
- // create model
- this.model = model;
-
- // get options
- this.options = this.model.attributes;
-
- // create new element
- this.setElement(this._template(this.options));
-
- // add event
- $(this.el).on('click', this.options.onclick);
-
- // add tooltip
- if (this.options.tooltip) {
- $(this.el).tooltip({title: this.options.tooltip, placement: 'bottom'});
- }
-
- // events
- this.model.on('change:percentage', function() {
- self._percentage(self.model.get('percentage'));
- });
- this.model.on('change:status', function() {
- self._status(self.model.get('status'));
- });
-
- // unload event
- var self = this;
- $(window).on('beforeunload', function() {
- var text = "";
- if (self.options.onunload) {
- text = self.options.onunload();
- }
- if (text != "") {
- return text;
- }
- });
- },
-
- // set status
- _status: function(value) {
- var $el = this.$el.find('.progress-bar');
- $el.removeClass();
- $el.addClass('progress-bar');
- $el.addClass('progress-bar-notransition');
- if (value != '') {
- $el.addClass('progress-bar-' + value);
- }
- },
-
- // set percentage
- _percentage: function(value) {
- var $el = this.$el.find('.progress-bar');
- $el.css({ width : value + '%' });
- },
-
- // template
- _template: function(options) {
- return '<div style="float: right">' +
- '<div class="progress-button">' +
- '<div class="progress">' +
- '<div class="progress-bar"></div>' +
- '</div>' +
- '<div id="label" class="label">' +
- '<a class="panel-header-button" href="javascript:void(0)">' +
- '<span class="fa fa-upload"></span>' +
- '</a>' +
- '</div>' +
- '</div>' +
- '</div>';
- }
-});
-
// galaxy upload
return Backbone.View.extend(
{
+ // options
+ options : {
+ nginx_upload_path : ''
+ },
+
// own modal
modal : null,
// button
- button_show : null,
+ ui_button : null,
// jquery uploadbox plugin
uploadbox: null,
@@ -131,6 +50,9 @@
// collection
collection : new UploadModel.Collection(),
+ // ftp file viewer
+ ftp : null,
+
// counter
counter : {
// stats
@@ -140,36 +62,29 @@
running : 0,
// reset stats
- reset : function()
- {
+ reset : function() {
this.announce = this.success = this.error = this.running = 0;
}
},
-
- // options
- options : {
- nginx_upload_path : ''
- },
-
+
// initialize
- initialize : function(options)
- {
+ initialize : function(options) {
// link this
var self = this;
// wait for galaxy history panel (workaround due to the use of iframes)
- if (!Galaxy.currHistoryPanel || !Galaxy.currHistoryPanel.model)
- {
+ if (!Galaxy.currHistoryPanel || !Galaxy.currHistoryPanel.model) {
window.setTimeout(function() { self.initialize() }, 500)
return;
}
// check if logged in
- if (!Galaxy.currUser.get('id'))
+ if (!Galaxy.currUser.get('id')) {
return;
+ }
// create model
- this.button_show = new ProgressButtonModel({
+ this.ui_button = new UploadButton.Model({
icon : 'fa-upload',
tooltip : 'Download from URL or upload files from disk',
label : 'Load Data',
@@ -186,7 +101,7 @@
});
// define location
- $('#left .unified-panel-header-inner').append((new ProgressButton(this.button_show)).$el);
+ $('#left .unified-panel-header-inner').append((new UploadButton.View(this.ui_button)).$el);
// load extension
var self = this;
@@ -242,33 +157,29 @@
//
// show/hide upload frame
- _eventShow : function (e)
- {
+ _eventShow : function (e) {
// prevent default
e.preventDefault();
- // stop propagation of event (for click-outside-of-modal-to-close functionality)
- e.stopPropagation();
// create modal
- if (!this.modal)
- {
+ if (!this.modal) {
// make modal
var self = this;
- this.modal = new Modal.GalaxyModal(
- {
+ this.modal = new Modal.GalaxyModal({
title : 'Download data directly from web or upload files from your disk',
body : this._template('upload-box', 'upload-info'),
buttons : {
- 'Choose files' : function() {self.uploadbox.select()},
- 'Create file' : function() {self._eventCreate()},
- 'Start' : function() {self._eventStart()},
- 'Pause' : function() {self._eventStop()},
- 'Reset' : function() {self._eventReset()},
- 'Close' : function() {self.modal.hide()},
+ 'Choose local file' : function() {self.uploadbox.select()},
+ 'Choose FTP file' : function() {self._eventFtp()},
+ 'Create new file' : function() {self._eventCreate()},
+ 'Start' : function() {self._eventStart()},
+ 'Pause' : function() {self._eventStop()},
+ 'Reset' : function() {self._eventReset()},
+ 'Close' : function() {self.modal.hide()},
},
height : '400',
width : '900',
- closing_events : true
+ closing_events : true
});
// set element
@@ -276,8 +187,7 @@
// file upload
var self = this;
- this.uploadbox = this.$el.uploadbox(
- {
+ this.uploadbox = this.$el.uploadbox({
announce : function(index, file, message) { self._eventAnnounce(index, file, message) },
initialize : function(index, file, message) { return self._eventInitialize(index, file, message) },
progress : function(index, file, message) { self._eventProgress(index, file, message) },
@@ -288,6 +198,17 @@
// setup info
this._updateScreen();
+
+ // add ftp file viewer
+ if (this.options.ftp_upload_dir && this.options.ftp_upload_site) {
+ var button = this.modal.getButton('Choose FTP file');
+ this.ftp = new Popover.View({
+ title : 'FTP files',
+ container : button
+ });
+ } else {
+ this.modal.hideButton('Choose FTP file');
+ }
}
// show modal
@@ -299,8 +220,7 @@
//
// remove item from upload list
- _eventRemove : function(item)
- {
+ _eventRemove : function(item) {
// update status
var status = item.get('status');
@@ -325,8 +245,7 @@
//
// a new file has been dropped/selected through the uploadbox plugin
- _eventAnnounce : function(index, file, message)
- {
+ _eventAnnounce : function(index, file, message) {
// update counter
this.counter.announce++;
@@ -337,22 +256,23 @@
var upload_item = new UploadItem(this, {
id : index,
file_name : file.name,
- file_size : file.size
+ file_size : file.size,
+ file_mode : file.mode,
+ file_path : file.path
});
// add to collection
this.collection.add(upload_item.model);
// add upload item element to table
- $(this.el).find('tbody:last').append(upload_item.$el);
+ $(this.el).find('tbody:first').append(upload_item.$el);
// render
upload_item.render();
},
// the uploadbox plugin is initializing the upload for this file
- _eventInitialize : function(index, file, message)
- {
+ _eventInitialize : function(index, file, message) {
// get element
var it = this.collection.get(index);
@@ -360,26 +280,38 @@
it.set('status', 'running');
// get configuration
- var file_type = it.get('extension');
var file_name = it.get('file_name');
+ var file_path = it.get('file_path');
+ var file_mode = it.get('file_mode');
+ var extension = it.get('extension');
var genome = it.get('genome');
var url_paste = it.get('url_paste');
var space_to_tabs = it.get('space_to_tabs');
+ var to_posix_lines = it.get('to_posix_lines');
// validate
if (!url_paste && !(file.size > 0))
return null;
// configure uploadbox
- this.uploadbox.configure({url : this.options.nginx_upload_path, paramname : "files_0|file_data"});
+ this.uploadbox.configure({url : this.options.nginx_upload_path});
+
+ // configure file mode
+ if (file_mode == 'ftp') {
+ this.uploadbox.configure({paramname : ''});
+ } else {
+ this.uploadbox.configure({paramname : 'files_0|file_data'});
+ }
// configure tool
tool_input = {};
tool_input['dbkey'] = genome;
- tool_input['file_type'] = file_type;
+ tool_input['file_type'] = extension;
tool_input['files_0|type'] = 'upload_dataset';
tool_input['files_0|url_paste'] = url_paste;
+ tool_input['files_0|ftp_files'] = file_path;
tool_input['space_to_tabs'] = space_to_tabs;
+ tool_input['to_posix_lines'] = to_posix_lines;
// setup data
data = {};
@@ -392,19 +324,17 @@
},
// progress
- _eventProgress : function(index, file, percentage)
- {
+ _eventProgress : function(index, file, percentage) {
// set progress for row
var it = this.collection.get(index);
it.set('percentage', percentage);
// update ui button
- this.button_show.set('percentage', this._upload_percentage(percentage, file.size));
+ this.ui_button.set('percentage', this._upload_percentage(percentage, file.size));
},
// success
- _eventSuccess : function(index, file, message)
- {
+ _eventSuccess : function(index, file, message) {
// update status
var it = this.collection.get(index);
it.set('status', 'success');
@@ -413,7 +343,7 @@
var file_size = it.get('file_size');
// update ui button
- this.button_show.set('percentage', this._upload_percentage(100, file_size));
+ this.ui_button.set('percentage', this._upload_percentage(100, file_size));
// update completed
this.upload_completed += file_size * 100;
@@ -430,8 +360,7 @@
},
// error
- _eventError : function(index, file, message)
- {
+ _eventError : function(index, file, message) {
// get element
var it = this.collection.get(index);
@@ -440,8 +369,8 @@
it.set('info', message);
// update ui button
- this.button_show.set('percentage', this._upload_percentage(100, file.size));
- this.button_show.set('status', 'danger');
+ this.ui_button.set('percentage', this._upload_percentage(100, file.size));
+ this.ui_button.set('status', 'danger');
// update completed
this.upload_completed += file.size * 100;
@@ -472,10 +401,27 @@
// events triggered by this view
//
- // create (pseudo) file
+ _eventFtp: function() {
+ // check if popover is visible
+ if (!this.ftp.visible) {
+ // show popover
+ this.ftp.empty();
+ this.ftp.append((new UploadFtp(this)).$el);
+ this.ftp.show();
+ } else {
+ // hide popover
+ this.ftp.hide();
+ }
+ },
+
+ // create a new file
_eventCreate : function ()
{
- this.uploadbox.add([{ name : 'New File', size : -1 }]);
+ this.uploadbox.add([{
+ name : 'New File',
+ size : 0,
+ mode : 'new'
+ }]);
},
// start upload process
@@ -498,8 +444,8 @@
});
// reset progress
- this.button_show.set('percentage', 0);
- this.button_show.set('status', 'success');
+ this.ui_button.set('percentage', 0);
+ this.ui_button.set('status', 'success');
// backup current history
this.current_history = Galaxy.currHistoryPanel.model.get('id');
@@ -520,7 +466,7 @@
}
// show upload has paused
- this.button_show.set('status', 'info');
+ this.ui_button.set('status', 'info');
// request pause
this.uploadbox.stop();
@@ -547,13 +493,12 @@
this.uploadbox.reset();
// reset button
- this.button_show.set('percentage', 0);
+ this.ui_button.set('percentage', 0);
}
},
// set screen
- _updateScreen: function ()
- {
+ _updateScreen: function () {
/*
update on screen info
*/
@@ -600,18 +545,20 @@
// select upload button
if (this.counter.running == 0)
{
- this.modal.enableButton('Choose files');
- this.modal.enableButton('Create file');
+ this.modal.enableButton('Choose local file');
+ this.modal.enableButton('Choose FTP file');
+ this.modal.enableButton('Create new file');
} else {
- this.modal.disableButton('Choose files');
- this.modal.disableButton('Create file');
+ this.modal.disableButton('Choose local file');
+ this.modal.disableButton('Choose FTP file');
+ this.modal.disableButton('Create new file');
}
// table visibility
if (this.counter.announce + this.counter.success + this.counter.error > 0)
- $(this.el).find('table').show();
+ $(this.el).find('#upload-table').show();
else
- $(this.el).find('table').hide();
+ $(this.el).find('#upload-table').hide();
},
// calculate percentage of all queued uploads
@@ -620,17 +567,16 @@
},
// load html template
- _template: function(id, idInfo)
- {
+ _template: function(id, idInfo) {
return '<div id="' + id + '" class="upload-box">' +
- '<table class="table table-striped" style="display: none;">' +
+ '<table id="upload-table" class="table table-striped" style="display: none;">' +
'<thead>' +
'<tr>' +
'<th>Name</th>' +
'<th>Size</th>' +
'<th>Type</th>' +
'<th>Genome</th>' +
- '<th>Space→Tab</th>' +
+ '<th>Settings</th>' +
'<th>Status</th>' +
'<th></th>' +
'</tr>' +
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/scripts/packed/galaxy.modal.js
--- a/static/scripts/packed/galaxy.modal.js
+++ b/static/scripts/packed/galaxy.modal.js
@@ -1,1 +1,1 @@
-define([],function(){var a=Backbone.View.extend({elMain:"#everything",optionsDefault:{title:"galaxy-modal",body:"",backdrop:true,height:null,width:null,closing_events:false},buttonList:{},initialize:function(b){if(b){this._create(b)}},show:function(b){this.initialize(b);if(this.options.height){this.$body.css("height",this.options.height);this.$body.css("overflow","hidden")}else{this.$body.css("max-height",$(window).height()/2)}if(this.options.width){this.$dialog.css("width",this.options.width)}if(this.visible){this.$el.show()}else{this.$el.fadeIn("fast")}this.visible=true},hide:function(){this.visible=false;this.$el.fadeOut("fast")},enableButton:function(b){var c=this.buttonList[b];this.$buttons.find("#"+c).prop("disabled",false)},disableButton:function(b){var c=this.buttonList[b];this.$buttons.find("#"+c).prop("disabled",true)},showButton:function(b){var c=this.buttonList[b];this.$buttons.find("#"+c).show()},hideButton:function(b){var c=this.buttonList[b];this.$buttons.find("#"+c).hide()},scrollTop:function(){return this.$body.scrollTop()},_create:function(d){var c=this;this.options=_.defaults(d,this.optionsDefault);if(this.options.body=="progress"){this.options.body=$('<div class="progress progress-striped active"><div class="progress-bar progress-bar-info" style="width:100%"></div></div>')}if(this.$el){this.$el.remove();$(document).off("keyup")}this.setElement(this._template(this.options.title));this.$dialog=(this.$el).find(".modal-dialog");this.$body=(this.$el).find(".modal-body");this.$footer=(this.$el).find(".modal-footer");this.$buttons=(this.$el).find(".buttons");this.$backdrop=(this.$el).find(".modal-backdrop");this.$body.html(this.options.body);if(!this.options.backdrop){this.$backdrop.removeClass("in")}if(this.options.buttons){this.buttonList={};var b=0;$.each(this.options.buttons,function(e,g){var f="button-"+b++;c.$buttons.append($('<button id="'+f+'"></button>').text(e).click(g)).append(" ");c.buttonList[e]=f})}else{this.$footer.hide()}$(this.elMain).append($(this.el));if(this.options.closing_events){if(!this.options.buttons.Pause){$(document).on("keyup",function(f){if(f.keyCode==27){c.hide()}})}this.$el.find(".modal-backdrop").on("click",function(){c.hide()})}},_template:function(b){return'<div class="modal"><div class="modal-backdrop fade in" style="z-index: -1;"></div><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><button type="button" class="close" style="display: none;">×</button><h4 class="title">'+b+'</h4></div><div class="modal-body"></div><div class="modal-footer"><div class="buttons" style="float: right;"></div></div></div</div></div>'}});return{GalaxyModal:a}});
\ No newline at end of file
+define([],function(){var a=Backbone.View.extend({elMain:"#everything",optionsDefault:{title:"galaxy-modal",body:"",backdrop:true,height:null,width:null,closing_events:false},buttonList:{},initialize:function(b){if(b){this._create(b)}},show:function(b){this.initialize(b);if(this.options.height){this.$body.css("height",this.options.height);this.$body.css("overflow","hidden")}else{this.$body.css("max-height",$(window).height()/2)}if(this.options.width){this.$dialog.css("width",this.options.width)}if(this.visible){this.$el.show()}else{this.$el.fadeIn("fast")}this.visible=true},hide:function(){this.visible=false;this.$el.fadeOut("fast")},enableButton:function(b){var c=this.buttonList[b];this.$buttons.find("#"+c).prop("disabled",false)},disableButton:function(b){var c=this.buttonList[b];this.$buttons.find("#"+c).prop("disabled",true)},showButton:function(b){var c=this.buttonList[b];this.$buttons.find("#"+c).show()},hideButton:function(b){var c=this.buttonList[b];this.$buttons.find("#"+c).hide()},getButton:function(b){var c=this.buttonList[b];return this.$buttons.find("#"+c)},scrollTop:function(){return this.$body.scrollTop()},_create:function(d){var c=this;this.options=_.defaults(d,this.optionsDefault);if(this.options.body=="progress"){this.options.body=$('<div class="progress progress-striped active"><div class="progress-bar progress-bar-info" style="width:100%"></div></div>')}if(this.$el){this.$el.remove();$(document).off("keyup")}this.setElement(this._template(this.options.title));this.$dialog=(this.$el).find(".modal-dialog");this.$body=(this.$el).find(".modal-body");this.$footer=(this.$el).find(".modal-footer");this.$buttons=(this.$el).find(".buttons");this.$backdrop=(this.$el).find(".modal-backdrop");this.$body.html(this.options.body);if(!this.options.backdrop){this.$backdrop.removeClass("in")}if(this.options.buttons){this.buttonList={};var b=0;$.each(this.options.buttons,function(e,g){var f="button-"+b++;c.$buttons.append($('<button id="'+f+'"></button>').text(e).click(g)).append(" ");c.buttonList[e]=f})}else{this.$footer.hide()}$(this.elMain).append($(this.el));if(this.options.closing_events){if(!this.options.buttons.Pause){$(document).on("keyup",function(f){if(f.keyCode==27){c.hide()}})}this.$el.find(".modal-backdrop").on("click",function(){c.hide()})}},_template:function(b){return'<div class="modal"><div class="modal-backdrop fade in" style="z-index: -1;"></div><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><button type="button" class="close" style="display: none;">×</button><h4 class="title">'+b+'</h4></div><div class="modal-body" style="position: static;"></div><div class="modal-footer"><div class="buttons" style="float: right;"></div></div></div</div></div>'}});return{GalaxyModal:a}});
\ No newline at end of file
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/scripts/packed/mvc/grid/grid-template.js
--- a/static/scripts/packed/mvc/grid/grid-template.js
+++ b/static/scripts/packed/mvc/grid/grid-template.js
@@ -1,1 +1,1 @@
-define([],function(){return{grid:function(b){var a="";if(b.embedded){a=this.grid_header(b)+this.grid_table(b)}else{a='<div class="loading-elt-overlay"></div><table><tr><td width="75%">'+this.grid_header(b)+'</td><td></td><td></td></tr><tr><td width="100%" id="grid-message" valign="top"></td><td></td><td></td></tr></table>'+this.grid_table(b)}if(b.info_text){a+='<br><div class="toolParamHelp" style="clear: both;">'+b.info_text+"</div>"}return a},grid_table:function(a){return'<form method="post" onsubmit="return false;"><table id="grid-table" class="grid"><thead id="grid-table-header"></thead><tbody id="grid-table-body"></tbody><tfoot id="grid-table-footer"></tfoot></table></form>'},grid_header:function(c){var b='<div class="grid-header">';if(!c.embedded){b+="<h2>"+c.title+"</h2>"}if(c.global_actions){b+='<ul class="manage-table-actions">';var d=(c.global_actions.length>=3);if(d){b+='<li><a class="action-button" id="popup-global-actions" class="menubutton">Actions</a></li><div popupmenu="popup-global-actions">'}for(i in c.global_actions){var e=c.global_actions[i];var a="";if(e.inbound){a="use-inbound"}else{a="use-outbound"}b+='<li><a class="action-button '+a+'" href="'+e.url_args+'" onclick="return false;">'+e.label+"</a></li>"}if(d){b+="</div>"}b+="</ul>"}if(c.insert){b+=c.insert}b+=this.grid_filters(c);b+="</div>";return b},header:function(b){var a="<tr>";if(b.show_item_checkboxes){a+="<th>";if(b.items.length>0){a+='<input type="checkbox" id="check_all" name=select_all_checkbox value="true"><input type="hidden" name=select_all_checkbox value="true">'}a+="</th>"}for(var c in b.columns){var d=b.columns[c];if(d.visible){a+='<th id="'+d.key+'-header">';if(d.href){a+='<a href="'+d.href+'" class="sort-link" sort_key="'+d.key+'">'+d.label+"</a>"}else{a+=d.label}a+='<span class="sort-arrow">'+d.extra+"</span></th>"}}a+="</tr>";return a},body:function(r){var k="";var s=0;var e=r.items.length;if(e==0){k+='<tr><td colspan="100"><em>No Items</em></td></tr>';s=1}for(var f in r.items){var p=r.items[f];var a=p.encode_id;var g="grid-"+f+"-popup";k+="<tr ";if(r.current_item_id==p.id){k+='class="current"'}k+=">";if(r.show_item_checkboxes){k+='<td style="width: 1.5em;"><input type="checkbox" name="id" value="'+a+'" id="'+a+'" class="grid-row-select-checkbox" /></td>'}for(j in r.columns){var d=r.columns[j];if(d.visible){var c="";if(d.nowrap){c='style="white-space:nowrap;"'}var q=p.column_config[d.label];var h=q.link;var l=q.value;var o=q.inbound;if(jQuery.type(l)==="string"){l=l.replace(/\/\//g,"/")}var b="";var n="";if(d.attach_popup){b="grid-"+f+"-popup";n="menubutton";if(h!=""){n+=" split"}n+=" popup"}k+="<td "+c+">";if(h){if(r.operations.length!=0){k+='<div id="'+b+'" class="'+n+'" style="float: left;">'}var m="";if(o){m="use-inbound"}else{m="use-outbound"}k+='<a class="label '+m+'" href="'+h+'" onclick="return false;">'+l+"</a>";if(r.operations.length!=0){k+="</div>"}}else{k+='<div id="'+b+'" class="'+n+'"><label id="'+d.label_id_prefix+a+'" for="'+a+'">'+l+"</label></div>"}k+="</td>"}}k+="</tr>";s++}return k},footer:function(o){var k="";if(o.use_paging&&o.num_pages>1){var m=o.num_page_links;var a=o.cur_page_num;var n=o.num_pages;var h=m/2;var g=a-h;var e=0;if(g==0){g=1;e=h-(a-g)}var d=h+e;var c=a+d;if(c<=n){max_offset=0}else{c=n;max_offset=d-(c+1-a)}if(max_offset!=0){g-=max_offset;if(g<1){g=1}}k+='<tr id="page-links-row">';if(o.show_item_checkboxes){k+="<td></td>"}k+='<td colspan="100"><span id="page-link-container">Page:';if(g>1){k+='<span class="page-link" id="page-link-1"><a href="javascript:void(0);" page_num="1" onclick="return false;">1</a></span> ...'}for(var l=g;l<c+1;l++){if(l==o.cur_page_num){k+='<span class="page-link inactive-link" id="page-link-'+l+'">'+l+"</span>"}else{k+='<span class="page-link" id="page-link-'+l+'"><a href="javascript:void(0);" onclick="return false;" page_num="'+l+'">'+l+"</a></span>"}}if(c<n){k+='...<span class="page-link" id="page-link-'+n+'"><a href="javascript:void(0);" onclick="return false;" page_num="'+n+'">'+n+"</a></span>"}k+="</span>";k+='<span class="page-link" id="show-all-link-span"> | <a href="javascript:void(0);" onclick="return false;" page_num="all">Show All</a></span></td></tr>'}if(o.show_item_checkboxes){k+='<tr><input type="hidden" id="operation" name="operation" value=""><td></td><td colspan="100">For <span class="grid-selected-count"></span> selected '+o.get_class_plural+": ";for(i in o.operations){var b=o.operations[i];if(b.allow_multiple){k+='<input type="button" value="'+b.label+'" class="operation-button action-button"> '}}k+="</td></tr>"}var f=false;for(i in o.operations){if(o.operations[i].global_operation){f=true;break}}if(f){k+='<tr><td colspan="100">';for(i in o.operations){var b=o.operations[i];if(b.global_operation){k+='<a class="action-button" href="'+b.global_operation+'">'+b.label+"</a>"}}k+="</td></tr>"}if(o.legend){k+='<tr><td colspan="100">'+o.legend+"</td></tr>"}return k},message:function(a){return'<p><div class="'+a.status+'message transient-message">'+a.message+'</div><div style="clear: both"></div></p>'},grid_filters:function(n){var a=n.default_filter_dict;var b=n.filters;var e="none";if(n.advanced_search){e="block"}var m=false;for(var g in n.columns){var d=n.columns[g];if(d.filterable=="advanced"){var l=d.key;var c=b[l];var f=a[l];if(c&&f&&c!=f){e="block"}m=true}}var k="block";if(e=="block"){k="none"}var h='<div id="standard-search" style="display: '+k+';"><table><tr><td style="padding: 0;"><table>';for(var g in n.columns){var d=n.columns[g];if(d.filterable=="standard"){h+=this.grid_column_filter(n,d)}}h+="</table></td></tr><tr><td>";if(m){h+='<a href="" class="advanced-search-toggle">Advanced Search</a>'}h+="</td></tr></table></div>";h+='<div id="advanced-search" style="display: '+e+'; margin-top: 5px; border: 1px solid #ccc;"><table><tr><td style="text-align: left" colspan="100"><a href="" class="advanced-search-toggle">Close Advanced Search</a></td></tr>';for(var g in n.columns){var d=n.columns[g];if(d.filterable=="advanced"){h+=this.grid_column_filter(n,d)}}h+="</table></div>";return h},grid_column_filter:function(e,c){var t=e.default_filter_dict;var l=e.filters;var a=c.label;var b=c.key;if(c.filterable=="advanced"){a=a.toLowerCase()}var k="<tr>";if(c.filterable=="advanced"){k+='<td align="left" style="padding-left: 10px">'+a+":</td>"}k+='<td style="padding-bottom: 1px;">';if(c.is_text){k+='<form class="text-filter-form" column_key="'+b+'" action="'+e.url+'" method="get" >';for(u in e.columns){var g=e.columns[u];var p=l[g.key];if(p){if(p!="All"){if(g.is_text){p=JSON.stringify(p)}k+='<input type="hidden" id="'+g.key+'" name="f-'+g.key+'" value="'+p+'"/>'}}}k+='<span id="'+b+'-filtering-criteria">';var h=l[b];if(h){var f=jQuery.type(h);if(f=="string"){if(h!="All"){k+=this.filter_element(b,h)}}if(f=="array"){for(var u in h){var n=h[u];var v=h;v=v.slice(u);k+=this.filter_element(b,n)}}}k+="</span>";var r="";if(c.filterable=="standard"){r=c.label.toLowerCase();var q=r.length;if(q<20){q=20}q=q+4}k+='<span class="search-box"><input class="search-box-input" id="input-'+b+'-filter" name="f-'+b+'" type="text" placeholder="'+r+'" size="'+q+'"/><button type="submit" style="background: transparent; border: none; padding: 4px; margin: 0px;"><i class="fa fa-plus-circle"></i></button></span></form>'}else{k+='<span id="'+b+'-filtering-criteria">';var s=false;for(cf_label in e.categorical_filters[b]){var o=e.categorical_filters[b][cf_label];var d="";var m="";for(key in o){d=key;m=o[key]}if(s){k+=" | "}s=true;var n=l[b];if(n&&o[b]&&n==m){k+='<span class="categorical-filter '+b+'-filter current-filter">'+cf_label+"</span>"}else{k+='<span class="categorical-filter '+b+'-filter"><a href="javascript:void(0);" filter_key="'+d+'" filter_val="'+m+'">'+cf_label+"</a></span>"}}k+="</span>"}k+="</td></tr>";return k},filter_element:function(b,a){return'<span class="text-filter-val">'+a+'<a href="javascript:void(0);" filter_key="'+b+'" filter_val="'+a+'"><i class="fa fa-times" style="padding-left: 5px; padding-bottom: 6px;"/></a></span>'}}});
\ No newline at end of file
+define([],function(){return{grid:function(b){var a="";if(b.embedded){a=this.grid_header(b)+this.grid_table(b)}else{a='<div class="loading-elt-overlay"></div><table><tr><td width="75%">'+this.grid_header(b)+'</td><td></td><td></td></tr><tr><td width="100%" id="grid-message" valign="top"></td><td></td><td></td></tr></table>'+this.grid_table(b)}if(b.info_text){a+='<br><div class="toolParamHelp" style="clear: both;">'+b.info_text+"</div>"}return a},grid_table:function(a){return'<form method="post" onsubmit="return false;"><table id="grid-table" class="grid"><thead id="grid-table-header"></thead><tbody id="grid-table-body"></tbody><tfoot id="grid-table-footer"></tfoot></table></form>'},grid_header:function(c){var b='<div class="grid-header">';if(!c.embedded){b+="<h2>"+c.title+"</h2>"}if(c.global_actions){b+='<ul class="manage-table-actions">';var d=(c.global_actions.length>=3);if(d){b+='<li><a class="action-button" id="popup-global-actions" class="menubutton">Actions</a></li><div popupmenu="popup-global-actions">'}for(i in c.global_actions){var e=c.global_actions[i];var a="";if(e.inbound){a="use-inbound"}else{a="use-outbound"}b+='<li><a class="action-button '+a+'" href="'+e.url_args+'" onclick="return false;">'+e.label+"</a></li>"}if(d){b+="</div>"}b+="</ul>"}if(c.insert){b+=c.insert}b+=this.grid_filters(c);b+="</div>";return b},header:function(b){var a="<tr>";if(b.show_item_checkboxes){a+="<th>";if(b.items.length>0){a+='<input type="checkbox" id="check_all" name=select_all_checkbox value="true"><input type="hidden" name=select_all_checkbox value="true">'}a+="</th>"}for(var c in b.columns){var d=b.columns[c];if(d.visible){a+='<th id="'+d.key+'-header">';if(d.href){a+='<a href="'+d.href+'" class="sort-link" sort_key="'+d.key+'">'+d.label+"</a>"}else{a+=d.label}a+='<span class="sort-arrow">'+d.extra+"</span></th>"}}a+="</tr>";return a},body:function(r){var k="";var s=0;var e=r.items.length;if(e==0){k+='<tr><td colspan="100"><em>No Items</em></td></tr>';s=1}for(var f in r.items){var p=r.items[f];var a=p.encode_id;var g="grid-"+f+"-popup";k+="<tr ";if(r.current_item_id==p.id){k+='class="current"'}k+=">";if(r.show_item_checkboxes){k+='<td style="width: 1.5em;"><input type="checkbox" name="id" value="'+a+'" id="'+a+'" class="grid-row-select-checkbox" /></td>'}for(j in r.columns){var d=r.columns[j];if(d.visible){var c="";if(d.nowrap){c='style="white-space:nowrap;"'}var q=p.column_config[d.label];var h=q.link;var l=q.value;var o=q.inbound;if(jQuery.type(l)==="string"){l=l.replace(/\/\//g,"/")}var b="";var n="";if(d.attach_popup){b="grid-"+f+"-popup";n="menubutton";if(h!=""){n+=" split"}n+=" popup"}k+="<td "+c+">";if(h){if(r.operations.length!=0){k+='<div id="'+b+'" class="'+n+'" style="float: left;">'}var m="";if(o){m="use-inbound"}else{m="use-outbound"}k+='<a class="label '+m+'" href="'+h+'" onclick="return false;">'+l+"</a>";if(r.operations.length!=0){k+="</div>"}}else{k+='<div id="'+b+'" class="'+n+'"><label id="'+d.label_id_prefix+a+'" for="'+a+'">'+l+"</label></div>"}k+="</td>"}}k+="</tr>";s++}return k},footer:function(o){var k="";if(o.use_paging&&o.num_pages>1){var m=o.num_page_links;var a=o.cur_page_num;var n=o.num_pages;var h=m/2;var g=a-h;var e=0;if(g==0){g=1;e=h-(a-g)}var d=h+e;var c=a+d;if(c<=n){max_offset=0}else{c=n;max_offset=d-(c+1-a)}if(max_offset!=0){g-=max_offset;if(g<1){g=1}}k+='<tr id="page-links-row">';if(o.show_item_checkboxes){k+="<td></td>"}k+='<td colspan="100"><span id="page-link-container">Page:';if(g>1){k+='<span class="page-link" id="page-link-1"><a href="javascript:void(0);" page_num="1" onclick="return false;">1</a></span> ...'}for(var l=g;l<c+1;l++){if(l==o.cur_page_num){k+='<span class="page-link inactive-link" id="page-link-'+l+'">'+l+"</span>"}else{k+='<span class="page-link" id="page-link-'+l+'"><a href="javascript:void(0);" onclick="return false;" page_num="'+l+'">'+l+"</a></span>"}}if(c<n){k+='...<span class="page-link" id="page-link-'+n+'"><a href="javascript:void(0);" onclick="return false;" page_num="'+n+'">'+n+"</a></span>"}k+="</span>";k+='<span class="page-link" id="show-all-link-span"> | <a href="javascript:void(0);" onclick="return false;" page_num="all">Show All</a></span></td></tr>'}if(o.show_item_checkboxes){k+='<tr><input type="hidden" id="operation" name="operation" value=""><td></td><td colspan="100">For <span class="grid-selected-count"></span> selected '+o.get_class_plural+": ";for(i in o.operations){var b=o.operations[i];if(b.allow_multiple){k+='<input type="button" value="'+b.label+'" class="operation-button action-button"> '}}k+="</td></tr>"}var f=false;for(i in o.operations){if(o.operations[i].global_operation){f=true;break}}if(f){k+='<tr><td colspan="100">';for(i in o.operations){var b=o.operations[i];if(b.global_operation){k+='<a class="action-button" href="'+b.global_operation+'">'+b.label+"</a>"}}k+="</td></tr>"}if(o.legend){k+='<tr><td colspan="100">'+o.legend+"</td></tr>"}return k},message:function(a){return'<p><div class="'+a.status+'message transient-message">'+a.message+'</div><div style="clear: both"></div></p>'},grid_filters:function(n){var a=n.default_filter_dict;var b=n.filters;var e="none";if(n.advanced_search){e="block"}var m=false;for(var g in n.columns){var d=n.columns[g];if(d.filterable=="advanced"){var l=d.key;var c=b[l];var f=a[l];if(c&&f&&c!=f){e="block"}m=true}}var k="block";if(e=="block"){k="none"}var h='<div id="standard-search" style="display: '+k+';"><table><tr><td style="padding: 0;"><table>';for(var g in n.columns){var d=n.columns[g];if(d.filterable=="standard"){h+=this.grid_column_filter(n,d)}}h+="</table></td></tr><tr><td>";if(m){h+='<a href="" class="advanced-search-toggle">Advanced Search</a>'}h+="</td></tr></table></div>";h+='<div id="advanced-search" style="display: '+e+'; margin-top: 5px; border: 1px solid #ccc;"><table><tr><td style="text-align: left" colspan="100"><a href="" class="advanced-search-toggle">Close Advanced Search</a></td></tr>';for(var g in n.columns){var d=n.columns[g];if(d.filterable=="advanced"){h+=this.grid_column_filter(n,d)}}h+="</table></div>";return h},grid_column_filter:function(e,c){var t=e.default_filter_dict;var l=e.filters;var a=c.label;var b=c.key;if(c.filterable=="advanced"){a=a.toLowerCase()}var k="<tr>";if(c.filterable=="advanced"){k+='<td align="left" style="padding-left: 10px">'+a+":</td>"}k+='<td style="padding-bottom: 1px;">';if(c.is_text){k+='<form class="text-filter-form" column_key="'+b+'" action="'+e.url+'" method="get" >';for(u in e.columns){var g=e.columns[u];var p=l[g.key];if(p){if(p!="All"){if(g.is_text){p=JSON.stringify(p)}k+='<input type="hidden" id="'+g.key+'" name="f-'+g.key+'" value="'+p+'"/>'}}}k+='<span id="'+b+'-filtering-criteria">';var h=l[b];if(h){var f=jQuery.type(h);if(f=="string"){if(h!="All"){k+=this.filter_element(b,h)}}if(f=="array"){for(var u in h){var n=h[u];var v=h;v=v.slice(u);k+=this.filter_element(b,n)}}}k+="</span>";var r="";if(c.filterable=="standard"){r=c.label.toLowerCase();var q=r.length;if(q<20){q=20}q=q+4}k+='<span class="search-box"><input class="search-box-input" id="input-'+b+'-filter" name="f-'+b+'" type="text" placeholder="'+r+'" size="'+q+'"/><button type="submit" style="background: transparent; border: none; padding: 4px; margin: 0px;"><i class="fa fa-search"></i></button></span></form>'}else{k+='<span id="'+b+'-filtering-criteria">';var s=false;for(cf_label in e.categorical_filters[b]){var o=e.categorical_filters[b][cf_label];var d="";var m="";for(key in o){d=key;m=o[key]}if(s){k+=" | "}s=true;var n=l[b];if(n&&o[b]&&n==m){k+='<span class="categorical-filter '+b+'-filter current-filter">'+cf_label+"</span>"}else{k+='<span class="categorical-filter '+b+'-filter"><a href="javascript:void(0);" filter_key="'+d+'" filter_val="'+m+'">'+cf_label+"</a></span>"}}k+="</span>"}k+="</td></tr>";return k},filter_element:function(b,a){return'<span class="text-filter-val">'+a+'<a href="javascript:void(0);" filter_key="'+b+'" filter_val="'+a+'"><i class="fa fa-times" style="padding-left: 5px; padding-bottom: 6px;"/></a></span>'}}});
\ No newline at end of file
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/scripts/packed/mvc/ui.popover.js
--- /dev/null
+++ b/static/scripts/packed/mvc/ui.popover.js
@@ -0,0 +1,1 @@
+define(["utils/utils"],function(a){var b=Backbone.View.extend({optionsDefault:{with_close:true,container:"body",title:null,placement:"top"},visible:false,initialize:function(d){var c=this;this.options=_.defaults(d,this.optionsDefault);this.setElement(this._template(this.options));this.options.container.parent().append(this.$el);if(this.options.with_close){this.$el.find("#close").on("click",function(){c.hide()})}this.uuid=a.uuid();var c=this;$("body").on("mousedown."+this.uuid,function(f){c._hide(f)})},title:function(c){if(c!==undefined){this.$el.find(".popover-title-label").html(c)}},show:function(){this.$el.show();this.visible=true;var c=this._get_placement(this.options.placement);this.$el.css(c)},_get_placement:function(h){var d=this._get_width(this.$el);var j=this.$el.height();var k=this.options.container;var c=this._get_width(k);var f=this._get_height(k);var g=k.position();var i=0;var e=0;if(h=="top"||h=="bottom"){e=g.left-d+(c+d)/2;if(h=="top"){i=g.top-j-5}else{i=g.top+f+5}}return{top:i,left:e}},_get_width:function(c){return c.width()+parseInt(c.css("padding-left"))+parseInt(c.css("padding-right"))},_get_height:function(c){return c.height()+parseInt(c.css("padding-top"))+parseInt(c.css("padding-bottom"))},hide:function(){this.$el.hide();this.visible=false},append:function(c){this.$el.find(".popover-content").append(c)},empty:function(c){this.$el.find(".popover-content").empty()},remove:function(){$("body").off("mousedown."+this.uuid);this.$el.remove()},_hide:function(c){if(!$(this.options.container).is(c.target)&&!$(this.el).is(c.target)&&$(this.el).has(c.target).length===0){this.hide()}},_template:function(d){var c='<div class="popover-view popover fade '+d.placement+' in"><div class="arrow"></div><div class="popover-title"><div class="popover-title-label">'+d.title+"</div>";if(d.with_close){c+='<div id="close" class="popover-close fa fa-times-circle"></div>'}c+='</div><div class="popover-content"></div></div>';return c}});return{View:b}});
\ No newline at end of file
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/scripts/packed/mvc/upload/upload-button.js
--- /dev/null
+++ b/static/scripts/packed/mvc/upload/upload-button.js
@@ -0,0 +1,1 @@
+define([],function(){var b=Backbone.Model.extend({defaults:{percentage:0,icon:"fa-circle",label:"",status:""}});var a=Backbone.View.extend({model:null,initialize:function(d){var c=this;this.model=d;this.options=this.model.attributes;this.setElement(this._template(this.options));$(this.el).on("click",this.options.onclick);if(this.options.tooltip){$(this.el).tooltip({title:this.options.tooltip,placement:"bottom"})}this.model.on("change:percentage",function(){c._percentage(c.model.get("percentage"))});this.model.on("change:status",function(){c._status(c.model.get("status"))});var c=this;$(window).on("beforeunload",function(){var e="";if(c.options.onunload){e=c.options.onunload()}if(e!=""){return e}})},_status:function(d){var c=this.$el.find(".progress-bar");c.removeClass();c.addClass("progress-bar");c.addClass("progress-bar-notransition");if(d!=""){c.addClass("progress-bar-"+d);console.log(c)}},_percentage:function(d){var c=this.$el.find(".progress-bar");c.css({width:d+"%"})},_template:function(c){return'<div style="float: right"><div class="upload-button"><div class="progress"><div class="progress-bar"></div></div><div id="label" class="label"><a class="panel-header-button" href="javascript:void(0)"><span class="fa fa-upload"></span></a></div></div></div>'}});return{Model:b,View:a}});
\ No newline at end of file
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/scripts/packed/mvc/upload/upload-ftp.js
--- /dev/null
+++ b/static/scripts/packed/mvc/upload/upload-ftp.js
@@ -0,0 +1,1 @@
+define(["utils/utils"],function(a){return Backbone.View.extend({options:{class_add:"upload-icon-button fa fa-square-o",class_remove:"upload-icon-button fa fa-check-square-o"},initialize:function(c){this.app=c;var b=this;this.setElement(this._template());a.jsonFromUrl(galaxy_config.root+"api/ftp_files",function(d){b._fill(d)})},events:{mousedown:function(b){b.preventDefault()}},_fill:function(c){if(c.length>0){this.$el.find("#upload-ftp-content").html($(this._template_table()));var b=0;for(key in c){this.add(c[key]);b+=c[key].size}this.$el.find("#upload-ftp-number").html(c.length+" files");this.$el.find("#upload-ftp-disk").html(a.bytesToString(b,true))}else{this.$el.find("#upload-ftp-content").html($(this._template_info()))}this.$el.find("#upload-ftp-wait").hide()},add:function(e){var d=$(this._template_row(e));$(this.el).find("tbody").append(d);var c="";if(this._find(e)){c=this.options.class_remove}else{c=this.options.class_add}d.find("#upload-ftp-add").addClass(c);var b=this;d.find("#upload-ftp-add").on("click",function(){var f=b._find(e);$(this).removeClass();if(!f){b.app.uploadbox.add([{mode:"ftp",name:e.path,size:e.size,path:e.path}]);$(this).addClass(b.options.class_remove)}else{b.app.collection.remove(f);$(this).addClass(b.options.class_add)}})},_find:function(f){var c=this.app.collection.where({file_path:f.path});var b=null;for(var d in c){var e=c[d];if(e.get("status")=="init"&&e.get("file_mode")=="ftp"){b=e.get("id")}}return b},_template_row:function(b){return'<tr><td><div id="upload-ftp-add"/></td><td>'+b.path+'</td><td style="white-space: nowrap;">'+a.bytesToString(b.size)+'</td><td style="white-space: nowrap;">'+b.ctime+"</td></tr>"},_template_table:function(){return'<span style="whitespace: nowrap; float: left;">Available files: </span><span style="whitespace: nowrap; float: right;"><span class="upload-icon fa fa-file-text-o"/><span id="upload-ftp-number"/> <span class="upload-icon fa fa-hdd-o"/><span id="upload-ftp-disk"/></span><table class="grid"><thead><tr><th></th><th>Name</th><th>Size</th><th>Created</th></tr></thead><tbody></tbody></table>'},_template_info:function(){return'<div class="upload-ftp-warning warningmessage">Your FTP directory does not contain any files.</div>'},_template:function(){return'<div class="upload-ftp"><div id="upload-ftp-wait" class="upload-ftp-wait fa fa-spinner fa-spin"/><div class="upload-ftp-help">This Galaxy server allows you to upload files via FTP. To upload some files, log in to the FTP server at <strong>'+this.app.options.ftp_upload_site+'</strong> using your Galaxy credentials (email address and password).</div><div id="upload-ftp-content"></div><div>'}})});
\ No newline at end of file
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/scripts/packed/mvc/upload/upload-model.js
--- a/static/scripts/packed/mvc/upload/upload-model.js
+++ b/static/scripts/packed/mvc/upload/upload-model.js
@@ -1,1 +1,1 @@
-define([],function(){var b=Backbone.Model.extend({defaults:{extension:"auto",genome:"?",url_paste:"",space_to_tabs:false,status:"init",info:null}});var a=Backbone.Collection.extend({model:b});return{Model:b,Collection:a}});
\ No newline at end of file
+define([],function(){var b=Backbone.Model.extend({defaults:{extension:"auto",genome:"?",url_paste:"",status:"init",info:null,file_mode:"local",file_size:0,file_type:null,file_path:"",space_to_tabs:false,to_posix_lines:true}});var a=Backbone.Collection.extend({model:b});return{Model:b,Collection:a}});
\ No newline at end of file
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/scripts/packed/mvc/upload/upload-row.js
--- a/static/scripts/packed/mvc/upload/upload-row.js
+++ b/static/scripts/packed/mvc/upload/upload-row.js
@@ -1,1 +1,1 @@
-define(["mvc/upload/upload-model","mvc/upload/upload-extensions"],function(a,b){return Backbone.View.extend({options:{padding:8,timeout:2000},status_classes:{init:"symbol fa fa-trash-o",queued:"symbol fa fa-spinner fa-spin",running:"symbol fa fa-spinner fa-spin",success:"symbol fa fa-check",error:"symbol fa fa-exclamation-triangle"},initialize:function(f,d){this.app=f;var c=this;this.model=new a.Model(d);this.setElement(this._template(d));var e=this.$el;e.find("#symbol").on("click",function(){var g=c.model.get("status");if(g=="init"||g=="success"||g=="error"){c.app.collection.remove(c.model)}});e.find("#extension_info").on("mouseover",function(){c._showExtensionInfo()}).on("mouseleave",function(){c._hideExtensionInfo()});e.find("#text-content").on("keyup",function(){var g=e.find("#text-content");var i=g.val();var h=i.length;e.find("#size").html(c._formatSize(h));c.model.set("url_paste",i);c.model.set("file_size",h)});e.find("#genome").on("change",function(g){c.model.set("genome",$(g.target).val())});e.find("#extension").on("change",function(g){c.model.set("extension",$(g.target).val());c.$el.find("#extension_info").popover("destroy")});e.find("#space_to_tabs").on("change",function(g){c.model.set("space_to_tabs",$(g.target).prop("checked"))});this.model.on("change:percentage",function(){c._refreshPercentage()});this.model.on("change:status",function(){c._refreshStatus()});this.model.on("change:info",function(){c._refreshInfo()});this.model.on("change:genome",function(){c._refreshGenome()});this.model.on("remove",function(){c._destroyExtensionInfo();c.remove()});this.app.collection.on("reset",function(){c._destroyExtensionInfo();c.remove()})},render:function(){var i=this.model.get("file_name");var d=this.model.get("file_size");var f=this.$el;f.find("#title").html(i);f.find("#size").html(this._formatSize(d));if(d==-1){var h=f.find("#text");var g=this.options.padding;var e=f.width()-2*g;var c=f.height()-g;h.css("width",e+"px");h.css("top",c+"px");f.height(c+h.height()+2*g);h.show()}},_refreshGenome:function(){var c=this.model.get("genome");this.$el.find("#genome").val(c)},_refreshInfo:function(){var c=this.model.get("info");if(c){this.$el.find("#info").html("<strong>Failed: </strong>"+c).show()}else{this.$el.find("#info").hide()}},_refreshPercentage:function(){var c=parseInt(this.model.get("percentage"));this.$el.find(".progress-bar").css({width:c+"%"});if(c!=100){this.$el.find("#percentage").html(c+"%")}else{this.$el.find("#percentage").html("Adding to history...")}},_refreshStatus:function(){var d=this.$el;var c=this.model.get("status");var f=this.status_classes[c];var e=this.$el.find("#symbol");e.removeClass();e.addClass(f);if(c=="init"){d.find("#text-content").attr("disabled",false);d.find("#genome").attr("disabled",false);d.find("#extension").attr("disabled",false);d.find("#space_to_tabs").attr("disabled",false)}else{d.find("#text-content").attr("disabled",true);d.find("#genome").attr("disabled",true);d.find("#extension").attr("disabled",true);d.find("#space_to_tabs").attr("disabled",true)}if(c=="success"){d.addClass("success");d.find("#percentage").html("100%")}if(c=="error"){d.addClass("danger");d.find(".progress").remove()}},_formatSize:function(c){var d="";if(c>=100000000000){c=c/100000000000;d="TB"}else{if(c>=100000000){c=c/100000000;d="GB"}else{if(c>=100000){c=c/100000;d="MB"}else{if(c>=100){c=c/100;d="KB"}else{if(c>0){c=c*10;d="b"}else{return"<strong>-</strong>"}}}}}return"<strong>"+(Math.round(c)/10)+"</strong> "+d},_showExtensionInfo:function(){var c=this;var d=$(this.el).find("#extension_info");var f=this.model.get("extension");var e=$(this.el).find("#extension").find("option:selected").text();d.popover({html:true,title:e,content:b(f),placement:"bottom",container:c.$el.parent()});d.popover("show");clearTimeout(this.popover_timeout)},_hideExtensionInfo:function(){var c=this;this.popover_timeout=setTimeout(function(){c._destroyExtensionInfo()},this.options.timeout)},_destroyExtensionInfo:function(){this.$el.find("#extension_info").popover("destroy")},_template:function(e){var d=this;var c='<tr id="upload-item-'+e.id+'" class="upload-item"><td><div style="position: relative;"><div id="title" class="title"></div><div id="text" class="text"><div class="text-info">You can tell Galaxy to download data from web by entering URL in this box (one per line). You can also directly paste the contents of a file.</div><textarea id="text-content" class="text-content form-control"></textarea></div></div></td><td><div id="size" class="size"></div></td>';c+='<td><select id="extension" class="extension">';for(key in d.app.select_extension){c+='<option value="'+d.app.select_extension[key][1]+'">'+d.app.select_extension[key][0]+"</option>"}c+='</select> <i id="extension_info" class="fa fa-search" style="cursor: pointer;"/></td>';c+='<td><select id="genome" class="genome">';for(key in d.app.select_genome){c+='<option value="'+d.app.select_genome[key][1]+'">'+d.app.select_genome[key][0]+"</option>"}c+="</select></td>";c+='<td><input id="space_to_tabs" type="checkbox"></input></td><td><div id="info" class="info"><div class="progress"><div class="progress-bar progress-bar-success"></div><div id="percentage" class="percentage">0%</div></div></div></td><td><div id="symbol" class="'+this.status_classes.init+'"></div></td></tr>';return c}})});
\ No newline at end of file
+define(["utils/utils","mvc/upload/upload-model","mvc/upload/upload-extensions","mvc/upload/upload-settings","mvc/ui.popover"],function(d,b,e,a,c){return Backbone.View.extend({options:{padding:8,timeout:2000},status_classes:{init:"upload-icon-button fa fa-trash-o",queued:"upload-icon fa fa-spinner fa-spin",running:"upload-icon fa fa-spinner fa-spin",success:"upload-icon-button fa fa-check",error:"upload-icon-button fa fa-exclamation-triangle"},settings:null,initialize:function(i,g){this.app=i;var f=this;this.model=new b.Model(g);this.setElement(this._template(g));var h=this.$el;this.settings=new c.View({title:"Upload configuration",container:h.find("#settings"),placement:"bottom"});h.find("#symbol").on("click",function(){f._removeRow()});h.find("#extension-info").on("click",function(j){f._showExtensionInfo()}).on("mousedown",function(j){j.preventDefault()});h.find("#settings").on("click",function(j){f._showSettings()}).on("mousedown",function(j){j.preventDefault()});h.find("#text-content").on("keyup",function(j){f.model.set("url_paste",$(j.target).val());f.model.set("file_size",$(j.target).val().length)});h.find("#genome").on("change",function(j){f.model.set("genome",$(j.target).val())});h.find("#extension").on("change",function(j){f.model.set("extension",$(j.target).val())});h.find("#space_to_tabs").on("change",function(j){f.model.set("space_to_tabs",$(j.target).prop("checked"))});this.model.on("change:percentage",function(){f._refreshPercentage()});this.model.on("change:status",function(){f._refreshStatus()});this.model.on("change:extension",function(){f._destroyExtensionInfo()});this.model.on("change:info",function(){f._refreshInfo()});this.model.on("change:genome",function(){f._refreshGenome()});this.model.on("change:file_size",function(){f._refreshFileSize()});this.model.on("remove",function(){f._destroyExtensionInfo();f.remove()});this.app.collection.on("reset",function(){f._destroyExtensionInfo();f.remove()})},render:function(){var m=this.model.get("file_name");var g=this.model.get("file_size");var j=this.model.get("file_mode");var i=this.$el;i.find("#title").html(m);i.find("#size").html(d.bytesToString(g));i.find("#mode").removeClass().addClass("mode");if(j=="new"){var l=i.find("#text");var k=this.options.padding;var h=i.width()-2*k;var f=i.height()-k;l.css("width",h+"px");l.css("top",f+"px");i.height(f+l.height()+2*k);l.show();i.find("#mode").addClass("fa fa-pencil")}if(j=="local"){i.find("#mode").addClass("fa fa-laptop")}if(j=="ftp"){i.find("#mode").addClass("fa fa-code-fork")}},_refreshGenome:function(){var f=this.model.get("genome");this.$el.find("#genome").val(f)},_refreshInfo:function(){var f=this.model.get("info");if(f){this.$el.find("#info").html("<strong>Failed: </strong>"+f).show()}else{this.$el.find("#info").hide()}},_refreshPercentage:function(){var f=parseInt(this.model.get("percentage"));this.$el.find(".progress-bar").css({width:f+"%"});if(f!=100){this.$el.find("#percentage").html(f+"%")}else{this.$el.find("#percentage").html("Adding to history...")}},_refreshStatus:function(){var g=this.$el;var f=this.model.get("status");var i=this.status_classes[f];var h=this.$el.find("#symbol");h.removeClass();h.addClass(i);if(f=="init"){g.find("#text-content").attr("disabled",false);g.find("#genome").attr("disabled",false);g.find("#extension").attr("disabled",false);g.find("#space_to_tabs").attr("disabled",false)}else{g.find("#text-content").attr("disabled",true);g.find("#genome").attr("disabled",true);g.find("#extension").attr("disabled",true);g.find("#space_to_tabs").attr("disabled",true)}if(f=="success"){g.addClass("success");g.find("#percentage").html("100%")}if(f=="error"){g.addClass("danger");g.find(".progress").remove()}},_refreshFileSize:function(){var f=this.model.get("file_size");this.$el.find("#size").html(d.bytesToString(f))},_removeRow:function(){var f=this.model.get("status");if(f=="init"||f=="success"||f=="error"){this.app.collection.remove(this.model)}},_showExtensionInfo:function(){var f=$(this.el).find("#extension-info");var h=this.model.get("extension");var g=$(this.el).find("#extension").find("option:selected").text();if(!this.extension_popup){this.extension_popup=new c.View({content:e(h),placement:"bottom",container:f})}if(!this.extension_popup.visible){this.extension_popup.title(g);this.extension_popup.empty();this.extension_popup.append(e(h));this.extension_popup.show()}else{this.extension_popup.hide()}},_showSettings:function(){if(!this.settings.visible){this.settings.empty();this.settings.append((new a(this)).$el);this.settings.show()}else{this.settings.hide()}},_hideExtensionInfo:function(){var f=this;this.popover_timeout=setTimeout(function(){f._destroyExtensionInfo()},this.options.timeout)},_destroyExtensionInfo:function(){this.$el.find("#extension-info").popover("destroy")},_template:function(h){var g=this;var f='<tr id="upload-item-'+h.id+'" class="upload-item"><td><div style="position: relative;"><div id="mode"></div><div id="title" class="title"></div><div id="text" class="text"><div class="text-info">You can tell Galaxy to download data from web by entering URL in this box (one per line). You can also directly paste the contents of a file.</div><textarea id="text-content" class="text-content form-control"></textarea></div></div></td><td><div id="size" class="size"></div></td>';f+='<td><select id="extension" class="extension">';for(key in g.app.select_extension){f+='<option value="'+g.app.select_extension[key][1]+'">'+g.app.select_extension[key][0]+"</option>"}f+='</select> <i id="extension-info" class="upload-icon-button fa fa-search"/></td>';f+='<td><select id="genome" class="genome">';for(key in g.app.select_genome){f+='<option value="'+g.app.select_genome[key][1]+'">'+g.app.select_genome[key][0]+"</option>"}f+="</select></td>";f+='<td><div id="settings" class="upload-icon-button fa fa-gear"></div><td><div id="info" class="info"><div class="progress"><div class="progress-bar progress-bar-success"></div><div id="percentage" class="percentage">0%</div></div></div></td><td><div id="symbol" class="'+this.status_classes.init+'"></div></td></tr>';return f}})});
\ No newline at end of file
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/scripts/packed/mvc/upload/upload-settings.js
--- /dev/null
+++ b/static/scripts/packed/mvc/upload/upload-settings.js
@@ -0,0 +1,1 @@
+define(["utils/utils"],function(a){return Backbone.View.extend({options:{class_check:"upload-icon-button fa fa-check-square-o",class_uncheck:"upload-icon-button fa fa-square-o"},initialize:function(c){this.app=c;var b=this;this.setElement(this._template());this.model=this.app.model;this.$el.find("#upload-space-to-tabs").on("click",function(){b._switch_state("#upload-space-to-tabs","space_to_tabs")});this.$el.find("#upload-to-posix-lines").on("click",function(){b._switch_state("#upload-to-posix-lines","to_posix_lines")});this.render()},events:{mousedown:function(b){b.preventDefault()}},render:function(){this._render_state("#upload-space-to-tabs",this.model.get("space_to_tabs"));this._render_state("#upload-to-posix-lines",this.model.get("to_posix_lines"));var b=this.$el.find("#upload-settings-cover");if(this.model.get("status")!="init"){b.show()}else{b.hide()}},_switch_state:function(c,b){if(this.model.get("status")=="init"){var d=!this.model.get(b);this.model.set(b,d);this._render_state(c,d)}},_render_state:function(b,d){var c=this.$el.find(b);c.removeClass();if(d){c.addClass(this.options.class_check)}else{c.addClass(this.options.class_uncheck)}},_template:function(){return'<div class="upload-settings" style="position: relative;"><div id="upload-settings-cover" class="upload-settings-cover"/><table class="table table-striped"><tbody><tr><td><div id="upload-space-to-tabs"/></td><td>Convert spaces to tabs</td></tr><tr><td><div id="upload-to-posix-lines"/></td><td>Use POSIX standard</td></tr></tbody></table></div>'}})});
\ No newline at end of file
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/scripts/packed/mvc/upload/upload-view.js
--- a/static/scripts/packed/mvc/upload/upload-view.js
+++ b/static/scripts/packed/mvc/upload/upload-view.js
@@ -1,1 +1,1 @@
-define(["galaxy.modal","utils/utils","mvc/upload/upload-model","mvc/upload/upload-row","mvc/ui","utils/uploadbox"],function(c,f,e,d){var b=Backbone.Model.extend({defaults:{percentage:0,icon:"fa-circle",label:"",status:""}});var a=Backbone.View.extend({model:null,initialize:function(h){var g=this;this.model=h;this.options=this.model.attributes;this.setElement(this._template(this.options));$(this.el).on("click",this.options.onclick);if(this.options.tooltip){$(this.el).tooltip({title:this.options.tooltip,placement:"bottom"})}this.model.on("change:percentage",function(){g._percentage(g.model.get("percentage"))});this.model.on("change:status",function(){g._status(g.model.get("status"))});var g=this;$(window).on("beforeunload",function(){var i="";if(g.options.onunload){i=g.options.onunload()}if(i!=""){return i}})},_status:function(h){var g=this.$el.find(".progress-bar");g.removeClass();g.addClass("progress-bar");g.addClass("progress-bar-notransition");if(h!=""){g.addClass("progress-bar-"+h)}},_percentage:function(h){var g=this.$el.find(".progress-bar");g.css({width:h+"%"})},_template:function(g){return'<div style="float: right"><div class="progress-button"><div class="progress"><div class="progress-bar"></div></div><div id="label" class="label"><a class="panel-header-button" href="javascript:void(0)"><span class="fa fa-upload"></span></a></div></div></div>'}});return Backbone.View.extend({modal:null,button_show:null,uploadbox:null,current_history:null,upload_size:0,select_extension:[["Auto-detect","auto"]],select_genome:[["Unspecified (?)","?"]],collection:new e.Collection(),counter:{announce:0,success:0,error:0,running:0,reset:function(){this.announce=this.success=this.error=this.running=0}},options:{nginx_upload_path:""},initialize:function(h){var g=this;if(!Galaxy.currHistoryPanel||!Galaxy.currHistoryPanel.model){window.setTimeout(function(){g.initialize()},500);return}if(!Galaxy.currUser.get("id")){return}this.button_show=new b({icon:"fa-upload",tooltip:"Download from URL or upload files from disk",label:"Load Data",onclick:function(i){if(i){g._eventShow(i)}},onunload:function(){if(g.counter.running>0){return"Several uploads are still processing."}}});$("#left .unified-panel-header-inner").append((new a(this.button_show)).$el);var g=this;f.jsonFromUrl(galaxy_config.root+"api/datatypes",function(i){for(key in i){g.select_extension.push([i[key],i[key]])}});f.jsonFromUrl(galaxy_config.root+"api/genomes",function(i){var j=g.select_genome[0];g.select_genome=[];for(key in i){if(i[key].length>1){if(i[key][1]!==j[1]){g.select_genome.push(i[key])}}}g.select_genome.sort(function(l,k){return l[0]>k[0]?1:l[0]<k[0]?-1:0});g.select_genome.unshift(j)});if(h){this.options=_.defaults(h,this.options)}this.collection.on("remove",function(i){g._eventRemove(i)});this.collection.on("change:genome",function(j){var i=j.get("genome");g.collection.each(function(k){if(k.get("status")=="init"&&k.get("genome")=="?"){k.set("genome",i)}})})},_eventShow:function(h){h.preventDefault();h.stopPropagation();if(!this.modal){var g=this;this.modal=new c.GalaxyModal({title:"Download data directly from web or upload files from your disk",body:this._template("upload-box","upload-info"),buttons:{"Choose files":function(){g.uploadbox.select()},"Create file":function(){g._eventCreate()},Start:function(){g._eventStart()},Pause:function(){g._eventStop()},Reset:function(){g._eventReset()},Close:function(){g.modal.hide()},},height:"400",width:"900",closing_events:true});this.setElement("#upload-box");var g=this;this.uploadbox=this.$el.uploadbox({announce:function(i,j,k){g._eventAnnounce(i,j,k)},initialize:function(i,j,k){return g._eventInitialize(i,j,k)},progress:function(i,j,k){g._eventProgress(i,j,k)},success:function(i,j,k){g._eventSuccess(i,j,k)},error:function(i,j,k){g._eventError(i,j,k)},complete:function(){g._eventComplete()}});this._updateScreen()}this.modal.show()},_eventRemove:function(h){var g=h.get("status");if(g=="success"){this.counter.success--}else{if(g=="error"){this.counter.error--}else{this.counter.announce--}}this._updateScreen();this.uploadbox.remove(h.id)},_eventAnnounce:function(g,h,j){this.counter.announce++;this._updateScreen();var i=new d(this,{id:g,file_name:h.name,file_size:h.size});this.collection.add(i.model);$(this.el).find("tbody:last").append(i.$el);i.render()},_eventInitialize:function(k,g,o){var i=this.collection.get(k);i.set("status","running");var h=i.get("extension");var l=i.get("file_name");var n=i.get("genome");var m=i.get("url_paste");var j=i.get("space_to_tabs");if(!m&&!(g.size>0)){return null}this.uploadbox.configure({url:this.options.nginx_upload_path,paramname:"files_0|file_data"});tool_input={};tool_input.dbkey=n;tool_input.file_type=h;tool_input["files_0|type"]="upload_dataset";tool_input["files_0|url_paste"]=m;tool_input.space_to_tabs=j;data={};data.history_id=this.current_history;data.tool_id="upload1";data.inputs=JSON.stringify(tool_input);return data},_eventProgress:function(h,i,g){var j=this.collection.get(h);j.set("percentage",g);this.button_show.set("percentage",this._upload_percentage(g,i.size))},_eventSuccess:function(h,i,k){var j=this.collection.get(h);j.set("status","success");var g=j.get("file_size");this.button_show.set("percentage",this._upload_percentage(100,g));this.upload_completed+=g*100;this.counter.announce--;this.counter.success++;this._updateScreen();Galaxy.currHistoryPanel.refreshHdas()},_eventError:function(g,h,j){var i=this.collection.get(g);i.set("status","error");i.set("info",j);this.button_show.set("percentage",this._upload_percentage(100,h.size));this.button_show.set("status","danger");this.upload_completed+=h.size*100;this.counter.announce--;this.counter.error++;this._updateScreen()},_eventComplete:function(){this.collection.each(function(g){if(g.get("status")=="queued"){g.set("status","init")}});this.counter.running=0;this._updateScreen()},_eventCreate:function(){this.uploadbox.add([{name:"New File",size:-1}])},_eventStart:function(){if(this.counter.announce==0||this.counter.running>0){return}var g=this;this.upload_size=0;this.upload_completed=0;this.collection.each(function(h){if(h.get("status")=="init"){h.set("status","queued");g.upload_size+=h.get("file_size")}});this.button_show.set("percentage",0);this.button_show.set("status","success");this.current_history=Galaxy.currHistoryPanel.model.get("id");this.counter.running=this.counter.announce;this._updateScreen();this.uploadbox.start()},_eventStop:function(){if(this.counter.running==0){return}this.button_show.set("status","info");this.uploadbox.stop();$("#upload-info").html("Queue will pause after completing the current file...")},_eventReset:function(){if(this.counter.running==0){this.collection.reset();this.counter.reset();this._updateScreen();this.uploadbox.reset();this.button_show.set("percentage",0)}},_updateScreen:function(){if(this.counter.announce==0){if(this.uploadbox.compatible()){message="You can Drag & Drop files into this box."}else{message="Unfortunately, your browser does not support multiple file uploads or drag&drop.<br>Some supported browsers are: Firefox 4+, Chrome 7+, IE 10+, Opera 12+ or Safari 6+."}}else{if(this.counter.running==0){message="You added "+this.counter.announce+" file(s) to the queue. Add more files or click 'Start' to proceed."}else{message="Please wait..."+this.counter.announce+" out of "+this.counter.running+" remaining."}}$("#upload-info").html(message);if(this.counter.running==0&&this.counter.announce+this.counter.success+this.counter.error>0){this.modal.enableButton("Reset")}else{this.modal.disableButton("Reset")}if(this.counter.running==0&&this.counter.announce>0){this.modal.enableButton("Start")}else{this.modal.disableButton("Start")}if(this.counter.running>0){this.modal.enableButton("Pause")}else{this.modal.disableButton("Pause")}if(this.counter.running==0){this.modal.enableButton("Choose files");this.modal.enableButton("Create file")}else{this.modal.disableButton("Choose files");this.modal.disableButton("Create file")}if(this.counter.announce+this.counter.success+this.counter.error>0){$(this.el).find("table").show()}else{$(this.el).find("table").hide()}},_upload_percentage:function(g,h){return(this.upload_completed+(g*h))/this.upload_size},_template:function(h,g){return'<div id="'+h+'" class="upload-box"><table class="table table-striped" style="display: none;"><thead><tr><th>Name</th><th>Size</th><th>Type</th><th>Genome</th><th>Space→Tab</th><th>Status</th><th></th></tr></thead><tbody></tbody></table></div><h6 id="'+g+'" class="upload-info"></h6>'}})});
\ No newline at end of file
+define(["galaxy.modal","utils/utils","mvc/upload/upload-button","mvc/upload/upload-model","mvc/upload/upload-row","mvc/upload/upload-ftp","mvc/ui.popover","mvc/ui","utils/uploadbox"],function(a,f,e,c,b,g,d){return Backbone.View.extend({options:{nginx_upload_path:""},modal:null,ui_button:null,uploadbox:null,current_history:null,upload_size:0,select_extension:[["Auto-detect","auto"]],select_genome:[["Unspecified (?)","?"]],collection:new c.Collection(),ftp:null,counter:{announce:0,success:0,error:0,running:0,reset:function(){this.announce=this.success=this.error=this.running=0}},initialize:function(i){var h=this;if(!Galaxy.currHistoryPanel||!Galaxy.currHistoryPanel.model){window.setTimeout(function(){h.initialize()},500);return}if(!Galaxy.currUser.get("id")){return}this.ui_button=new e.Model({icon:"fa-upload",tooltip:"Download from URL or upload files from disk",label:"Load Data",onclick:function(j){if(j){h._eventShow(j)}},onunload:function(){if(h.counter.running>0){return"Several uploads are still processing."}}});$("#left .unified-panel-header-inner").append((new e.View(this.ui_button)).$el);var h=this;f.jsonFromUrl(galaxy_config.root+"api/datatypes",function(j){for(key in j){h.select_extension.push([j[key],j[key]])}});f.jsonFromUrl(galaxy_config.root+"api/genomes",function(j){var k=h.select_genome[0];h.select_genome=[];for(key in j){if(j[key].length>1){if(j[key][1]!==k[1]){h.select_genome.push(j[key])}}}h.select_genome.sort(function(m,l){return m[0]>l[0]?1:m[0]<l[0]?-1:0});h.select_genome.unshift(k)});if(i){this.options=_.defaults(i,this.options)}this.collection.on("remove",function(j){h._eventRemove(j)});this.collection.on("change:genome",function(k){var j=k.get("genome");h.collection.each(function(l){if(l.get("status")=="init"&&l.get("genome")=="?"){l.set("genome",j)}})})},_eventShow:function(j){j.preventDefault();if(!this.modal){var h=this;this.modal=new a.GalaxyModal({title:"Download data directly from web or upload files from your disk",body:this._template("upload-box","upload-info"),buttons:{"Choose local file":function(){h.uploadbox.select()},"Choose FTP file":function(){h._eventFtp()},"Create new file":function(){h._eventCreate()},Start:function(){h._eventStart()},Pause:function(){h._eventStop()},Reset:function(){h._eventReset()},Close:function(){h.modal.hide()},},height:"400",width:"900",closing_events:true});this.setElement("#upload-box");var h=this;this.uploadbox=this.$el.uploadbox({announce:function(k,l,m){h._eventAnnounce(k,l,m)},initialize:function(k,l,m){return h._eventInitialize(k,l,m)},progress:function(k,l,m){h._eventProgress(k,l,m)},success:function(k,l,m){h._eventSuccess(k,l,m)},error:function(k,l,m){h._eventError(k,l,m)},complete:function(){h._eventComplete()}});this._updateScreen();if(this.options.ftp_upload_dir&&this.options.ftp_upload_site){var i=this.modal.getButton("Choose FTP file");this.ftp=new d.View({title:"FTP files",container:i})}else{this.modal.hideButton("Choose FTP file")}}this.modal.show()},_eventRemove:function(i){var h=i.get("status");if(h=="success"){this.counter.success--}else{if(h=="error"){this.counter.error--}else{this.counter.announce--}}this._updateScreen();this.uploadbox.remove(i.id)},_eventAnnounce:function(h,i,k){this.counter.announce++;this._updateScreen();var j=new b(this,{id:h,file_name:i.name,file_size:i.size,file_mode:i.mode,file_path:i.path});this.collection.add(j.model);$(this.el).find("tbody:first").append(j.$el);j.render()},_eventInitialize:function(m,j,s){var k=this.collection.get(m);k.set("status","running");var o=k.get("file_name");var n=k.get("file_path");var h=k.get("file_mode");var p=k.get("extension");var r=k.get("genome");var q=k.get("url_paste");var l=k.get("space_to_tabs");var i=k.get("to_posix_lines");if(!q&&!(j.size>0)){return null}this.uploadbox.configure({url:this.options.nginx_upload_path});if(h=="ftp"){this.uploadbox.configure({paramname:""})}else{this.uploadbox.configure({paramname:"files_0|file_data"})}tool_input={};tool_input.dbkey=r;tool_input.file_type=p;tool_input["files_0|type"]="upload_dataset";tool_input["files_0|url_paste"]=q;tool_input["files_0|ftp_files"]=n;tool_input.space_to_tabs=l;tool_input.to_posix_lines=i;data={};data.history_id=this.current_history;data.tool_id="upload1";data.inputs=JSON.stringify(tool_input);return data},_eventProgress:function(i,j,h){var k=this.collection.get(i);k.set("percentage",h);this.ui_button.set("percentage",this._upload_percentage(h,j.size))},_eventSuccess:function(i,j,l){var k=this.collection.get(i);k.set("status","success");var h=k.get("file_size");this.ui_button.set("percentage",this._upload_percentage(100,h));this.upload_completed+=h*100;this.counter.announce--;this.counter.success++;this._updateScreen();Galaxy.currHistoryPanel.refreshHdas()},_eventError:function(h,i,k){var j=this.collection.get(h);j.set("status","error");j.set("info",k);this.ui_button.set("percentage",this._upload_percentage(100,i.size));this.ui_button.set("status","danger");this.upload_completed+=i.size*100;this.counter.announce--;this.counter.error++;this._updateScreen()},_eventComplete:function(){this.collection.each(function(h){if(h.get("status")=="queued"){h.set("status","init")}});this.counter.running=0;this._updateScreen()},_eventFtp:function(){if(!this.ftp.visible){this.ftp.empty();this.ftp.append((new g(this)).$el);this.ftp.show()}else{this.ftp.hide()}},_eventCreate:function(){this.uploadbox.add([{name:"New File",size:0,mode:"new"}])},_eventStart:function(){if(this.counter.announce==0||this.counter.running>0){return}var h=this;this.upload_size=0;this.upload_completed=0;this.collection.each(function(i){if(i.get("status")=="init"){i.set("status","queued");h.upload_size+=i.get("file_size")}});this.ui_button.set("percentage",0);this.ui_button.set("status","success");this.current_history=Galaxy.currHistoryPanel.model.get("id");this.counter.running=this.counter.announce;this._updateScreen();this.uploadbox.start()},_eventStop:function(){if(this.counter.running==0){return}this.ui_button.set("status","info");this.uploadbox.stop();$("#upload-info").html("Queue will pause after completing the current file...")},_eventReset:function(){if(this.counter.running==0){this.collection.reset();this.counter.reset();this._updateScreen();this.uploadbox.reset();this.ui_button.set("percentage",0)}},_updateScreen:function(){if(this.counter.announce==0){if(this.uploadbox.compatible()){message="You can Drag & Drop files into this box."}else{message="Unfortunately, your browser does not support multiple file uploads or drag&drop.<br>Some supported browsers are: Firefox 4+, Chrome 7+, IE 10+, Opera 12+ or Safari 6+."}}else{if(this.counter.running==0){message="You added "+this.counter.announce+" file(s) to the queue. Add more files or click 'Start' to proceed."}else{message="Please wait..."+this.counter.announce+" out of "+this.counter.running+" remaining."}}$("#upload-info").html(message);if(this.counter.running==0&&this.counter.announce+this.counter.success+this.counter.error>0){this.modal.enableButton("Reset")}else{this.modal.disableButton("Reset")}if(this.counter.running==0&&this.counter.announce>0){this.modal.enableButton("Start")}else{this.modal.disableButton("Start")}if(this.counter.running>0){this.modal.enableButton("Pause")}else{this.modal.disableButton("Pause")}if(this.counter.running==0){this.modal.enableButton("Choose local file");this.modal.enableButton("Choose FTP file");this.modal.enableButton("Create new file")}else{this.modal.disableButton("Choose local file");this.modal.disableButton("Choose FTP file");this.modal.disableButton("Create new file")}if(this.counter.announce+this.counter.success+this.counter.error>0){$(this.el).find("#upload-table").show()}else{$(this.el).find("#upload-table").hide()}},_upload_percentage:function(h,i){return(this.upload_completed+(h*i))/this.upload_size},_template:function(i,h){return'<div id="'+i+'" class="upload-box"><table id="upload-table" class="table table-striped" style="display: none;"><thead><tr><th>Name</th><th>Size</th><th>Type</th><th>Genome</th><th>Settings</th><th>Status</th><th></th></tr></thead><tbody></tbody></table></div><h6 id="'+h+'" class="upload-info"></h6>'}})});
\ No newline at end of file
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/scripts/packed/utils/config.js
--- a/static/scripts/packed/utils/config.js
+++ b/static/scripts/packed/utils/config.js
@@ -1,1 +1,1 @@
-define(["libs/underscore","viz/trackster/util","utils/config"],function(c,f,b){var d=Backbone.Model.extend({initialize:function(g){var h=this.get("key");this.set("id",h);var i=c.find(d.known_settings_defaults,function(j){return j.key===h});if(i){this.set(c.extend({},i,g))}if(this.get("type")==="color"&&!this.get("value")){this.set("value",f.get_random_color())}},set_value:function(h){var g=this.get("type");if(g==="float"){h=parseFloat(h)}else{if(g==="int"){h=parseInt(h,10)}}this.set("value",h)}},{known_settings_defaults:[{key:"name",label:"Name",type:"text",default_value:""},{key:"color",label:"Color",type:"color",default_value:null},{key:"min_value",label:"Min Value",type:"float",default_value:null},{key:"max_value",label:"Max Value",type:"float",default_value:null},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:32,hidden:true},{key:"pos_color",label:"Positive Color",type:"color",default_value:"#FF8C00"},{key:"neg_color",label:"Negative Color",type:"color",default_value:"#4169E1"},{key:"block_color",label:"Block color",type:"color",default_value:null},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_insertions",label:"Show insertions",type:"bool",default_value:false},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:null},{key:"show_differences",label:"Show differences only",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true}]});var e=Backbone.Collection.extend({model:d,to_key_value_dict:function(){var g={};this.each(function(h){g[h.get("key")]=h.get("value")});return g},get_value:function(g){var h=this.get(g);if(h){return h.get("value")}return undefined}},{from_config_dict:function(h){var g=c.map(c.keys(h),function(i){return{key:i,value:h[i]}});return new e(g)}});var a=Backbone.View.extend({className:"config-settings-view",render:function(){var g=this.$el;this.collection.each(function(k,p){if(k.get("hidden")){return}var j="param_"+p,q=k.get("type"),u=k.get("value");var w=$("<div class='form-row' />").appendTo(g);w.append($("<label />").attr("for",j).text(k.get("label")+":"));if(q==="bool"){w.append($('<input type="checkbox" />').attr("id",j).attr("name",j).attr("checked",u))}else{if(q==="text"){w.append($('<input type="text"/>').attr("id",j).val(u).click(function(){$(this).select()}))}else{if(q==="select"){var s=$("<select />").attr("id",j);for(var n=0;n<k.options.length;n++){$("<option/>").text(k.options[n].label).attr("value",k.options[n].value).appendTo(s)}s.val(u);w.append(s)}else{if(q==="color"){var v=$("<div/>").appendTo(w),r=$("<input />").attr("id",j).attr("name",j).val(u).css("float","left").appendTo(v).click(function(x){$(".tooltip").removeClass("in");var i=$(this).siblings(".tooltip").addClass("in");i.css({left:$(this).position().left+$(this).width()+5,top:$(this).position().top-($(i).height()/2)+($(this).height()/2)}).show();i.click(function(y){y.stopPropagation()});$(document).bind("click.color-picker",function(){i.hide();$(document).unbind("click.color-picker")});x.stopPropagation()}),o=$("<a href='javascript:void(0)'/>").addClass("icon-button arrow-circle").appendTo(v).attr("title","Set new random color").tooltip(),t=$("<div class='tooltip right' style='position: absolute;' />").appendTo(v).hide(),l=$("<div class='tooltip-inner' style='text-align: inherit'></div>").appendTo(t),h=$("<div class='tooltip-arrow'></div>").appendTo(t),m=$.farbtastic(l,{width:100,height:100,callback:r,color:u});v.append($("<div/>").css("clear","both"));(function(i){o.click(function(){i.setColor(f.get_random_color())})})(m)}else{w.append($("<input />").attr("id",j).attr("name",j).val(u))}}}}if(k.help){w.append($("<div class='help'/>").text(k.help))}});return this},render_in_modal:function(k){var g=this,j=function(){Galaxy.modal.hide();$(window).unbind("keypress.check_enter_esc")},h=function(){g.update_from_form();Galaxy.modal.hide();$(window).unbind("keypress.check_enter_esc")},i=function(l){if((l.keyCode||l.which)===27){j()}else{if((l.keyCode||l.which)===13){h()}}};$(window).bind("keypress.check_enter_esc",i);if(this.$el.children().length===0){this.render()}Galaxy.modal.show({title:k||"Configure",body:this.$el,buttons:{Cancel:j,OK:h}})},update_from_form:function(){var g=this;this.collection.each(function(i,h){if(!i.get("hidden")){var k="param_"+h;var j=g.$el.find("#"+k).val();if(i.get("type")==="bool"){j=container.find("#"+k).is(":checked")}i.set_value(j)}})}});return{ConfigSettingCollection:e,ConfigSettingCollectionView:a}});
\ No newline at end of file
+define(["libs/underscore","viz/trackster/util","utils/config"],function(c,f,b){var d=Backbone.Model.extend({initialize:function(g){var h=this.get("key");this.set("id",h);var i=c.find(d.known_settings_defaults,function(j){return j.key===h});if(i){this.set(c.extend({},i,g))}if(this.get("type")==="color"&&!this.get("value")){this.set("value",f.get_random_color())}},set_value:function(h){var g=this.get("type");if(g==="float"){h=parseFloat(h)}else{if(g==="int"){h=parseInt(h,10)}}this.set("value",h)}},{known_settings_defaults:[{key:"name",label:"Name",type:"text",default_value:""},{key:"color",label:"Color",type:"color",default_value:null},{key:"min_value",label:"Min Value",type:"float",default_value:null},{key:"max_value",label:"Max Value",type:"float",default_value:null},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:32,hidden:true},{key:"pos_color",label:"Positive Color",type:"color",default_value:"#FF8C00"},{key:"neg_color",label:"Negative Color",type:"color",default_value:"#4169E1"},{key:"block_color",label:"Block color",type:"color",default_value:null},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_insertions",label:"Show insertions",type:"bool",default_value:false},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:null},{key:"show_differences",label:"Show differences only",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true}]});var e=Backbone.Collection.extend({model:d,to_key_value_dict:function(){var g={};this.each(function(h){g[h.get("key")]=h.get("value")});return g},get_value:function(g){var h=this.get(g);if(h){return h.get("value")}return undefined}},{from_config_dict:function(h){var g=c.map(c.keys(h),function(i){return{key:i,value:h[i]}});return new e(g)}});var a=Backbone.View.extend({className:"config-settings-view",render:function(){var g=this.$el;this.collection.each(function(k,p){if(k.get("hidden")){return}var j="param_"+p,q=k.get("type"),u=k.get("value");var w=$("<div class='form-row' />").appendTo(g);w.append($("<label />").attr("for",j).text(k.get("label")+":"));if(q==="bool"){w.append($('<input type="checkbox" />').attr("id",j).attr("name",j).attr("checked",u))}else{if(q==="text"){w.append($('<input type="text"/>').attr("id",j).val(u).click(function(){$(this).select()}))}else{if(q==="select"){var s=$("<select />").attr("id",j);for(var n=0;n<k.options.length;n++){$("<option/>").text(k.options[n].label).attr("value",k.options[n].value).appendTo(s)}s.val(u);w.append(s)}else{if(q==="color"){var v=$("<div/>").appendTo(w),r=$("<input />").attr("id",j).attr("name",j).val(u).css("float","left").appendTo(v).click(function(x){$(".tooltip").removeClass("in");var i=$(this).siblings(".tooltip").addClass("in");i.css({left:$(this).position().left+$(this).width()+5,top:$(this).position().top-($(i).height()/2)+($(this).height()/2)}).show();i.click(function(y){y.stopPropagation()});$(document).bind("click.color-picker",function(){i.hide();$(document).unbind("click.color-picker")});x.stopPropagation()}),o=$("<a href='javascript:void(0)'/>").addClass("icon-button arrow-circle").appendTo(v).attr("title","Set new random color").tooltip(),t=$("<div class='tooltip right' style='position: absolute;' />").appendTo(v).hide(),l=$("<div class='tooltip-inner' style='text-align: inherit'></div>").appendTo(t),h=$("<div class='tooltip-arrow'></div>").appendTo(t),m=$.farbtastic(l,{width:100,height:100,callback:r,color:u});v.append($("<div/>").css("clear","both"));(function(i){o.click(function(){i.setColor(f.get_random_color())})})(m)}else{w.append($("<input />").attr("id",j).attr("name",j).val(u))}}}}if(k.help){w.append($("<div class='help'/>").text(k.help))}});return this},render_in_modal:function(k){var g=this,j=function(){Galaxy.modal.hide();$(window).unbind("keypress.check_enter_esc")},h=function(){Galaxy.modal.hide();$(window).unbind("keypress.check_enter_esc");g.update_from_form()},i=function(l){if((l.keyCode||l.which)===27){j()}else{if((l.keyCode||l.which)===13){h()}}};$(window).bind("keypress.check_enter_esc",i);if(this.$el.children().length===0){this.render()}Galaxy.modal.show({title:k||"Configure",body:this.$el,buttons:{Cancel:j,OK:h}})},update_from_form:function(){var g=this;this.collection.each(function(i,h){if(!i.get("hidden")){var k="param_"+h;var j=g.$el.find("#"+k).val();if(i.get("type")==="bool"){j=container.find("#"+k).is(":checked")}i.set_value(j)}})}});return{ConfigSettingCollection:e,ConfigSettingCollectionView:a}});
\ No newline at end of file
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/scripts/packed/utils/uploadbox.js
--- a/static/scripts/packed/utils/uploadbox.js
+++ b/static/scripts/packed/utils/uploadbox.js
@@ -1,1 +1,1 @@
-(function(c){jQuery.event.props.push("dataTransfer");var h={url:"",paramname:"content",maxfilesize:2048,maxfilenumber:50,dragover:function(){},dragleave:function(){},announce:function(){},initialize:function(){},progress:function(){},success:function(){},error:function(k,l,m){alert(m)},complete:function(){},error_filesize:"File exceeds 2GB. Please use an FTP client.",error_default:"Please make sure the file is available.",error_server:"Upload request failed.",error_login:"Uploads require you to log in.",error_missing:"No upload content available."};var a={};var e={};var f=0;var j=0;var d=false;var g=false;var b=null;var i=null;c.fn.uploadbox=function(A){a=c.extend({},h,A);b=this;b.append('<input id="uploadbox_input" type="file" style="display: none" multiple>');b.on("drop",o);b.on("dragover",p);b.on("dragleave",x);c("#uploadbox_input").change(function(B){z(B.target.files);c(this).val("")});function o(B){if(!B.dataTransfer){return}z(B.dataTransfer.files);B.preventDefault();return false}function p(B){B.preventDefault();a.dragover.call(B)}function x(B){B.stopPropagation();a.dragleave.call(B)}function k(B){if(B.lengthComputable){a.progress(this.index,this.file,Math.round((B.loaded*100)/B.total))}}function z(D){if(d){return}for(var C=0;C<D.length;C++){if(j>=a.maxfilenumber){break}var B=String(f++);e[B]=D[C];a.announce(B,e[B],"");j++}}function r(B){if(e[B]){delete e[B];j--}}function m(){if(j==0||g){g=false;d=false;a.complete();return}else{d=true}var D=-1;for(var F in e){D=F;break}var E=e[D];r(D);var C=E.size;var B=1048576*a.maxfilesize;if(C<B){var G=a.initialize(D,E);if(G){q(D,E,G)}else{u(D,E,a.error_missing)}}else{u(D,E,a.error_filesize)}}function q(B,D,E){var F=new FormData();for(var C in E){F.append(C,E[C])}if(D.size>0){F.append(a.paramname,D,D.name)}i=new XMLHttpRequest();i.open("POST",a.url,true);i.setRequestHeader("Accept","application/json");i.setRequestHeader("Cache-Control","no-cache");i.setRequestHeader("X-Requested-With","XMLHttpRequest");i.onreadystatechange=function(){if(i.readyState!=i.DONE){return}var G=null;if(i.responseText){try{G=jQuery.parseJSON(i.responseText)}catch(H){G=i.responseText}}if(i.status<200||i.status>299){var I=i.statusText;if(i.status==403){I=a.error_login}else{if(i.status==0){I=a.error_server}else{if(!I){I=a.error_default}}}u(B,D,I+" ("+i.status+")")}else{y(B,D,G)}};i.upload.index=B;i.upload.file=D;i.upload.addEventListener("progress",k,false);i.send(F)}function y(B,C,D){a.success(B,C,D);m()}function u(B,C,D){a.error(B,C,D);m()}function w(){c("#uploadbox_input").trigger("click")}function t(B){for(B in e){r(B)}}function l(){if(!d){d=true;m()}}function v(){g=true}function n(B){a=c.extend({},a,B);return a}function s(){return window.File&&window.FormData&&window.XMLHttpRequest&&window.FileList}return{select:w,add:z,remove:r,start:l,stop:v,reset:t,configure:n,compatible:s}}})(jQuery);
\ No newline at end of file
+(function(c){jQuery.event.props.push("dataTransfer");var h={url:"",paramname:"content",maxfilesize:2048,maxfilenumber:50,dragover:function(){},dragleave:function(){},announce:function(){},initialize:function(){},progress:function(){},success:function(){},error:function(k,l,m){alert(m)},complete:function(){},error_filesize:"File exceeds 2GB. Please use an FTP client.",error_default:"Please make sure the file is available.",error_server:"Upload request failed.",error_login:"Uploads require you to log in.",error_missing:"No upload content available."};var a={};var e={};var f=0;var j=0;var d=false;var g=false;var b=null;var i=null;c.fn.uploadbox=function(A){a=c.extend({},h,A);b=this;b.append('<input id="uploadbox_input" type="file" style="display: none" multiple>');b.on("drop",o);b.on("dragover",p);b.on("dragleave",x);c("#uploadbox_input").change(function(B){z(B.target.files);c(this).val("")});function o(B){if(!B.dataTransfer){return}z(B.dataTransfer.files);B.preventDefault();return false}function p(B){B.preventDefault();a.dragover.call(B)}function x(B){B.stopPropagation();a.dragleave.call(B)}function k(B){if(B.lengthComputable){a.progress(this.index,this.file,Math.round((B.loaded*100)/B.total))}}function z(D){if(d){return}var E=f;for(var C=0;C<D.length;C++){if(j>=a.maxfilenumber){break}var B=String(f++);e[B]=D[C];a.announce(B,e[B],"");j++}return E}function r(B){if(e[B]){delete e[B];j--}}function m(){if(j==0||g){g=false;d=false;a.complete();return}else{d=true}var D=-1;for(var F in e){D=F;break}var E=e[D];r(D);var C=E.size;var H=E.mode;var B=1048576*a.maxfilesize;if(C<B||E.mode=="ftp"){var G=a.initialize(D,E);if(G){q(D,E,G)}else{u(D,E,a.error_missing)}}else{u(D,E,a.error_filesize)}}function q(B,D,E){var F=new FormData();for(var C in E){F.append(C,E[C])}if(D.size>0){F.append(a.paramname,D,D.name)}i=new XMLHttpRequest();i.open("POST",a.url,true);i.setRequestHeader("Accept","application/json");i.setRequestHeader("Cache-Control","no-cache");i.setRequestHeader("X-Requested-With","XMLHttpRequest");i.onreadystatechange=function(){if(i.readyState!=i.DONE){return}var G=null;if(i.responseText){try{G=jQuery.parseJSON(i.responseText)}catch(H){G=i.responseText}}if(i.status<200||i.status>299){var I=i.statusText;if(i.status==403){I=a.error_login}else{if(i.status==0){I=a.error_server}else{if(!I){I=a.error_default}}}u(B,D,I+" ("+i.status+")")}else{y(B,D,G)}};i.upload.index=B;i.upload.file=D;i.upload.addEventListener("progress",k,false);i.send(F)}function y(B,C,D){a.success(B,C,D);m()}function u(B,C,D){a.error(B,C,D);m()}function w(){c("#uploadbox_input").trigger("click")}function t(B){for(B in e){r(B)}}function l(){if(!d){d=true;m()}}function v(){g=true}function n(B){a=c.extend({},a,B);return a}function s(){return window.File&&window.FormData&&window.XMLHttpRequest&&window.FileList}return{select:w,add:z,remove:r,start:l,stop:v,reset:t,configure:n,compatible:s}}})(jQuery);
\ No newline at end of file
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/scripts/packed/utils/utils.js
--- a/static/scripts/packed/utils/utils.js
+++ b/static/scripts/packed/utils/utils.js
@@ -1,1 +1,1 @@
-define(["libs/underscore"],function(c){function d(h,g,f){var i=new XMLHttpRequest();i.open("GET",h,true);i.setRequestHeader("Accept","application/json");i.setRequestHeader("Cache-Control","no-cache");i.setRequestHeader("X-Requested-With","XMLHttpRequest");i.onloadend=function(){var j=i.status;if(j==200){try{response=jQuery.parseJSON(i.responseText)}catch(k){response=i.responseText}g&&g(response)}else{f&&f(j)}};i.send()}function b(i,f){var g=$('<div class="'+i+'"></div>');g.appendTo(":eq(0)");var h=g.css(f);g.remove();return h}function a(f){if(!$('link[href^="'+f+'"]').length){$('<link href="'+galaxy_config.root+f+'" rel="stylesheet">').appendTo("head")}}function e(f,g){if(f){return c.defaults(f,g)}else{return g}}return{cssLoadFile:a,cssGetAttribute:b,jsonFromUrl:d,merge:e}});
\ No newline at end of file
+define(["libs/underscore"],function(c){function f(j,i,h){var k=new XMLHttpRequest();k.open("GET",j,true);k.setRequestHeader("Accept","application/json");k.setRequestHeader("Cache-Control","no-cache");k.setRequestHeader("X-Requested-With","XMLHttpRequest");k.onloadend=function(){var l=k.status;if(l==200){try{response=jQuery.parseJSON(k.responseText)}catch(m){response=k.responseText}i&&i(response)}else{h&&h(l)}};k.send()}function b(k,h){var i=$('<div class="'+k+'"></div>');i.appendTo(":eq(0)");var j=i.css(h);i.remove();return j}function a(h){if(!$('link[href^="'+h+'"]').length){$('<link href="'+galaxy_config.root+h+'" rel="stylesheet">').appendTo("head")}}function g(h,i){if(h){return c.defaults(h,i)}else{return i}}function d(i,k){var j="";if(i>=100000000000){i=i/100000000000;j="TB"}else{if(i>=100000000){i=i/100000000;j="GB"}else{if(i>=100000){i=i/100000;j="MB"}else{if(i>=100){i=i/100;j="KB"}else{if(i>0){i=i*10;j="b"}else{return"<strong>-</strong>"}}}}}var h=(Math.round(i)/10);if(k){return h+" "+j}else{return"<strong>"+h+"</strong> "+j}}function e(){return(new Date().getTime()).toString(36)}return{cssLoadFile:a,cssGetAttribute:b,jsonFromUrl:f,merge:g,bytesToString:d,uuid:e}});
\ No newline at end of file
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/scripts/packed/viz/circster.js
--- a/static/scripts/packed/viz/circster.js
+++ b/static/scripts/packed/viz/circster.js
@@ -1,1 +1,1 @@
-define(["utils/utils","libs/underscore","libs/d3","viz/visualization","utils/config"],function(h,i,n,k,c){h.cssLoadFile("static/style/circster.css");var o=Backbone.Model.extend({is_visible:function(s,p){var q=s.getBoundingClientRect(),r=$("svg")[0].getBoundingClientRect();if(q.right<0||q.left>r.right||q.bottom<0||q.top>r.bottom){return false}return true}});var j={drawTicks:function(t,s,x,r,p){var w=t.append("g").selectAll("g").data(s).enter().append("g").selectAll("g").data(x).enter().append("g").attr("class","tick").attr("transform",function(y){return"rotate("+(y.angle*180/Math.PI-90)+")translate("+y.radius+",0)"});var v=[],u=[],q=function(y){return y.angle>Math.PI?"end":null};if(p){v=[0,0,0,-4];u=[4,0,"",".35em"];q=null}else{v=[1,0,4,0];u=[0,4,".35em",""]}w.append("line").attr("x1",v[0]).attr("y1",v[1]).attr("x2",v[2]).attr("y1",v[3]).style("stroke","#000");w.append("text").attr("x",u[0]).attr("y",u[1]).attr("dx",u[2]).attr("dy",u[3]).attr("text-anchor",q).attr("transform",r).text(function(y){return y.label})},formatNum:function(q,p){if(p===undefined){p=2}if(q===null){return null}var s=null;if(q<1){s=q.toPrecision(p)}else{var r=Math.round(q.toPrecision(p));if(q<1000){s=r}else{if(q<1000000){s=Math.round((r/1000).toPrecision(3)).toFixed(0)+"K"}else{if(q<1000000000){s=Math.round((r/1000000).toPrecision(3)).toFixed(0)+"M"}}}}return s}};var d=Backbone.Model.extend({});var a=Backbone.View.extend({className:"circster",initialize:function(p){this.genome=p.genome;this.track_gap=10;this.label_arc_height=50;this.scale=1;this.circular_views=null;this.chords_views=null;this.model.get("drawables").on("add",this.add_track,this);this.model.get("drawables").on("remove",this.remove_track,this);var q=this.model.get("config");q.get("arc_dataset_height").on("change:value",this.update_track_bounds,this)},get_circular_tracks:function(){return this.model.get("drawables").filter(function(p){return p.get("track_type")!=="DiagonalHeatmapTrack"})},get_chord_tracks:function(){return this.model.get("drawables").filter(function(p){return p.get("track_type")==="DiagonalHeatmapTrack"})},get_tracks_bounds:function(){var r=this.get_circular_tracks(),s=this.model.get("config").get_value("arc_dataset_height"),p=Math.min(this.$el.width(),this.$el.height()),u=p/2-r.length*(s+this.track_gap)-(this.label_arc_height+this.track_gap),t=n.range(u,p/2,s+this.track_gap);var q=this;return i.map(t,function(v){return[v,v+s]})},render:function(){var y=this,p=y.$el.width(),x=y.$el.height(),u=this.get_circular_tracks(),s=this.get_chord_tracks(),r=y.model.get("config").get_value("total_gap"),t=this.get_tracks_bounds(),q=n.select(y.$el[0]).append("svg").attr("width",p).attr("height",x).attr("pointer-events","all").append("svg:g").call(n.behavior.zoom().on("zoom",function(){var z=n.event.scale;q.attr("transform","translate("+n.event.translate+") scale("+z+")");if(y.scale!==z){if(y.zoom_drag_timeout){clearTimeout(y.zoom_drag_timeout)}y.zoom_drag_timeout=setTimeout(function(){},400)}})).attr("transform","translate("+p/2+","+x/2+")").append("svg:g").attr("class","tracks");this.circular_views=u.map(function(A,B){var z=new e({el:q.append("g")[0],track:A,radius_bounds:t[B],genome:y.genome,total_gap:r});z.render();return z});this.chords_views=s.map(function(A){var z=new l({el:q.append("g")[0],track:A,radius_bounds:t[0],genome:y.genome,total_gap:r});z.render();return z});var w=this.circular_views[this.circular_views.length-1].radius_bounds[1],v=[w,w+this.label_arc_height];this.label_track_view=new b({el:q.append("g")[0],track:new d(),radius_bounds:v,genome:y.genome,total_gap:r});this.label_track_view.render()},add_track:function(w){var q=this.model.get("config").get_value("total_gap");if(w.get("track_type")==="DiagonalHeatmapTrack"){var s=this.circular_views[0].radius_bounds,v=new l({el:n.select("g.tracks").append("g")[0],track:w,radius_bounds:s,genome:this.genome,total_gap:q});v.render();this.chords_views.push(v)}else{var u=this.get_tracks_bounds();i.each(this.circular_views,function(x,y){x.update_radius_bounds(u[y])});i.each(this.chords_views,function(x){x.update_radius_bounds(u[0])});var t=this.circular_views.length,p=new e({el:n.select("g.tracks").append("g")[0],track:w,radius_bounds:u[t],genome:this.genome,total_gap:q});p.render();this.circular_views.push(p);var r=u[u.length-1];r[1]=r[0];this.label_track_view.update_radius_bounds(r)}},remove_track:function(q,s,r){var p=this.circular_views[r.index];this.circular_views.splice(r.index,1);p.$el.remove();var t=this.get_tracks_bounds();i.each(this.circular_views,function(u,v){u.update_radius_bounds(t[v])})},update_track_bounds:function(){var p=this.get_tracks_bounds();i.each(this.circular_views,function(q,r){q.update_radius_bounds(p[r])})}});var m=Backbone.View.extend({tagName:"g",initialize:function(p){this.bg_stroke="ccc";this.loading_bg_fill="000";this.bg_fill="ccc";this.total_gap=p.total_gap;this.track=p.track;this.radius_bounds=p.radius_bounds;this.genome=p.genome;this.chroms_layout=this._chroms_layout();this.data_bounds=[];this.scale=1;this.parent_elt=n.select(this.$el[0])},get_fill_color:function(){var p=this.track.get("config").get_value("block_color");if(!p){p=this.track.get("config").get_value("color")}return p},render:function(){var t=this.parent_elt;if(!t){console.log("no parent elt")}var s=this.chroms_layout,v=n.svg.arc().innerRadius(this.radius_bounds[0]).outerRadius(this.radius_bounds[1]),p=t.selectAll("g").data(s).enter().append("svg:g"),r=p.append("path").attr("d",v).attr("class","chrom-background").style("stroke",this.bg_stroke).style("fill",this.loading_bg_fill);r.append("title").text(function(x){return x.data.chrom});var q=this,u=q.track.get("data_manager"),w=(u?u.data_is_ready():true);$.when(w).then(function(){$.when(q._render_data(t)).then(function(){r.style("fill",q.bg_fill);q.render_labels()})})},render_labels:function(){},update_radius_bounds:function(q){this.radius_bounds=q;var p=n.svg.arc().innerRadius(this.radius_bounds[0]).outerRadius(this.radius_bounds[1]);this.parent_elt.selectAll("g>path.chrom-background").transition().duration(1000).attr("d",p);this._transition_chrom_data();this._transition_labels()},update_scale:function(s){var r=this.scale;this.scale=s;if(s<=r){return}var q=this,p=new o();this.parent_elt.selectAll("path.chrom-data").filter(function(u,t){return p.is_visible(this)}).each(function(z,v){var y=n.select(this),u=y.attr("chrom"),x=q.genome.get_chrom_region(u),w=q.track.get("data_manager"),t;if(!w.can_get_more_detailed_data(x)){return}t=q.track.get("data_manager").get_more_detailed_data(x,"Coverage",0,s);$.when(t).then(function(C){y.remove();q._update_data_bounds();var B=i.find(q.chroms_layout,function(D){return D.data.chrom===u});var A=q.get_fill_color();q._render_chrom_data(q.parent_elt,B,C).style("stroke",A).style("fill",A)})});return q},_transition_chrom_data:function(){var q=this.track,s=this.chroms_layout,p=this.parent_elt.selectAll("g>path.chrom-data"),t=p[0].length;if(t>0){var r=this;$.when(q.get("data_manager").get_genome_wide_data(this.genome)).then(function(v){var u=i.reject(i.map(v,function(w,x){var y=null,z=r._get_path_function(s[x],w);if(z){y=z(w.data)}return y}),function(w){return w===null});p.each(function(x,w){n.select(this).transition().duration(1000).attr("d",u[w])})})}},_transition_labels:function(){},_update_data_bounds:function(){var p=this.data_bounds;this.data_bounds=this.get_data_bounds(this.track.get("data_manager").get_genome_wide_data(this.genome));if(this.data_bounds[0]<p[0]||this.data_bounds[1]>p[1]){this._transition_chrom_data()}},_render_data:function(s){var r=this,q=this.chroms_layout,p=this.track,t=$.Deferred();$.when(p.get("data_manager").get_genome_wide_data(this.genome)).then(function(v){r.data_bounds=r.get_data_bounds(v);layout_and_data=i.zip(q,v),chroms_data_layout=i.map(layout_and_data,function(w){var x=w[0],y=w[1];return r._render_chrom_data(s,x,y)});var u=r.get_fill_color();r.parent_elt.selectAll("path.chrom-data").style("stroke",u).style("fill",u);t.resolve(s)});return t},_render_chrom_data:function(p,q,r){},_get_path_function:function(q,p){},_chroms_layout:function(){var q=this.genome.get_chroms_info(),s=n.layout.pie().value(function(u){return u.len}).sort(null),t=s(q),p=2*Math.PI*this.total_gap/q.length,r=i.map(t,function(w,v){var u=w.endAngle-p;w.endAngle=(u>w.startAngle?u:w.startAngle);return w});return r}});var b=m.extend({initialize:function(p){m.prototype.initialize.call(this,p);this.innerRadius=this.radius_bounds[0];this.radius_bounds[0]=this.radius_bounds[1];this.bg_stroke="fff";this.bg_fill="fff";this.min_arc_len=0.05},_render_data:function(r){var q=this,p=r.selectAll("g");p.selectAll("path").attr("id",function(v){return"label-"+v.data.chrom});p.append("svg:text").filter(function(v){return v.endAngle-v.startAngle>q.min_arc_len}).attr("text-anchor","middle").append("svg:textPath").attr("class","chrom-label").attr("xlink:href",function(v){return"#label-"+v.data.chrom}).attr("startOffset","25%").text(function(v){return v.data.chrom});var s=function(x){var v=(x.endAngle-x.startAngle)/x.value,w=n.range(0,x.value,25000000).map(function(y,z){return{radius:q.innerRadius,angle:y*v+x.startAngle,label:z===0?0:(z%3?null:q.formatNum(y))}});if(w.length<4){w[w.length-1].label=q.formatNum(Math.round((w[w.length-1].angle-x.startAngle)/v))}return w};var u=function(v){return v.angle>Math.PI?"rotate(180)translate(-16)":null};var t=i.filter(this.chroms_layout,function(v){return v.endAngle-v.startAngle>q.min_arc_len});this.drawTicks(this.parent_elt,t,s,u)}});i.extend(b.prototype,j);var g=m.extend({_quantile:function(q,p){q.sort(n.ascending);return n.quantile(q,p)},_render_chrom_data:function(p,s,q){var t=this._get_path_function(s,q);if(!t){return null}var r=p.datum(q.data),u=r.append("path").attr("class","chrom-data").attr("chrom",s.data.chrom).attr("d",t);return u},_get_path_function:function(s,r){if(typeof r==="string"||!r.data||r.data.length===0){return null}var p=n.scale.linear().domain(this.data_bounds).range(this.radius_bounds).clamp(true);var t=n.scale.linear().domain([0,r.data.length]).range([s.startAngle,s.endAngle]);var q=n.svg.line.radial().interpolate("linear").radius(function(u){return p(u[1])}).angle(function(v,u){return t(u)});return n.svg.area.radial().interpolate(q.interpolate()).innerRadius(p(0)).outerRadius(q.radius()).angle(q.angle())},render_labels:function(){var p=this,q=function(){return"rotate(90)"};this.drawTicks(this.parent_elt,[this.chroms_layout[0]],this._data_bounds_ticks_fn(),q,true)},_transition_labels:function(){if(this.data_bounds.length===0){return}var q=this,s=i.filter(this.chroms_layout,function(t){return t.endAngle-t.startAngle>0.08}),r=i.filter(s,function(u,t){return t%3===0}),p=i.flatten(i.map(r,function(t){return q._data_bounds_ticks_fn()(t)}));this.parent_elt.selectAll("g.tick").data(p).transition().attr("transform",function(t){return"rotate("+(t.angle*180/Math.PI-90)+")translate("+t.radius+",0)"})},_data_bounds_ticks_fn:function(){var p=this;visibleChroms=0;return function(q){return[{radius:p.radius_bounds[0],angle:q.startAngle,label:p.formatNum(p.data_bounds[0])},{radius:p.radius_bounds[1],angle:q.startAngle,label:p.formatNum(p.data_bounds[1])}]}},get_data_bounds:function(p){}});i.extend(g.prototype,j);var e=g.extend({get_data_bounds:function(q){var p=i.flatten(i.map(q,function(r){if(r){return i.map(r.data,function(s){return parseInt(s[1],10)||0})}else{return 0}}));return[i.min(p),this._quantile(p,0.98)||i.max(p)]}});var l=m.extend({render:function(){var p=this;$.when(p.track.get("data_manager").data_is_ready()).then(function(){$.when(p.track.get("data_manager").get_genome_wide_data(p.genome)).then(function(s){var r=[],q=p.genome.get_chroms_info();i.each(s,function(w,v){var t=q[v].chrom;var u=i.map(w.data,function(y){var x=p._get_region_angle(t,y[1]),z=p._get_region_angle(y[3],y[4]);return{source:{startAngle:x,endAngle:x+0.01},target:{startAngle:z,endAngle:z+0.01}}});r=r.concat(u)});p.parent_elt.append("g").attr("class","chord").selectAll("path").data(r).enter().append("path").style("fill",p.get_fill_color()).attr("d",n.svg.chord().radius(p.radius_bounds[0])).style("opacity",1)})})},update_radius_bounds:function(p){this.radius_bounds=p;this.parent_elt.selectAll("path").transition().attr("d",n.svg.chord().radius(this.radius_bounds[0]))},_get_region_angle:function(r,p){var q=i.find(this.chroms_layout,function(s){return s.data.chrom===r});return q.endAngle-((q.endAngle-q.startAngle)*(q.data.len-p)/q.data.len)}});var f=Backbone.View.extend({initialize:function(){var p=new k.Genome(galaxy_config.app.genome),q=new k.GenomeVisualization(galaxy_config.app.viz_config);q.get("config").add([{key:"arc_dataset_height",label:"Arc Dataset Height",type:"int",value:25,view:"circster"},{key:"total_gap",label:"Gap [0-1]",type:"float",value:0.4,view:"circster",hidden:true}]);var s=new a({el:$("#center .unified-panel-body"),genome:p,model:q});s.render();$("#center .unified-panel-header-inner").append(galaxy_config.app.viz_config.title+" "+galaxy_config.app.viz_config.dbkey);var r=create_icon_buttons_menu([{icon_class:"plus-button",title:"Add tracks",on_click:function(){k.select_datasets(galaxy_config.root+"visualization/list_current_history_datasets",galaxy_config.root+"api/datasets",q.get("dbkey"),function(t){q.add_tracks(t)})}},{icon_class:"gear",title:"Settings",on_click:function(){var t=new c.ConfigSettingCollectionView({collection:q.get("config")});t.render_in_modal("Configure Visualization")}},{icon_class:"disk--arrow",title:"Save",on_click:function(){Galaxy.modal.show({title:"Saving...",body:"progress"});$.ajax({url:galaxy_config.root+"visualization/save",type:"POST",dataType:"json",data:{id:q.get("vis_id"),title:q.get("title"),dbkey:q.get("dbkey"),type:"trackster",vis_json:JSON.stringify(q)}}).success(function(t){Galaxy.modal.hide();q.set("vis_id",t.vis_id)}).error(function(){Galaxy.modal.show({title:"Could Not Save",body:"Could not save visualization. Please try again later.",buttons:{Cancel:function(){Galaxy.modal.hide()}}})})}},{icon_class:"cross-circle",title:"Close",on_click:function(){window.location=galaxy_config.root+"visualization/list"}}],{tooltip_config:{placement:"bottom"}});r.$el.attr("style","float: right");$("#center .unified-panel-header-inner").append(r.$el);$(".menu-button").tooltip({placement:"bottom"})}});return{GalaxyApp:f}});
\ No newline at end of file
+define(["utils/utils","libs/underscore","libs/d3","viz/visualization","utils/config"],function(h,i,n,k,c){h.cssLoadFile("static/style/circster.css");var o=Backbone.Model.extend({is_visible:function(s,p){var q=s.getBoundingClientRect(),r=$("svg")[0].getBoundingClientRect();if(q.right<0||q.left>r.right||q.bottom<0||q.top>r.bottom){return false}return true}});var j={drawTicks:function(t,s,x,r,p){var w=t.append("g").selectAll("g").data(s).enter().append("g").selectAll("g").data(x).enter().append("g").attr("class","tick").attr("transform",function(y){return"rotate("+(y.angle*180/Math.PI-90)+")translate("+y.radius+",0)"});var v=[],u=[],q=function(y){return y.angle>Math.PI?"end":null};if(p){v=[0,0,0,-4];u=[4,0,"",".35em"];q=null}else{v=[1,0,4,0];u=[0,4,".35em",""]}w.append("line").attr("x1",v[0]).attr("y1",v[1]).attr("x2",v[2]).attr("y1",v[3]).style("stroke","#000");w.append("text").attr("x",u[0]).attr("y",u[1]).attr("dx",u[2]).attr("dy",u[3]).attr("text-anchor",q).attr("transform",r).text(function(y){return y.label})},formatNum:function(q,p){if(p===undefined){p=2}if(q===null){return null}var s=null;if(q<1){s=q.toPrecision(p)}else{var r=Math.round(q.toPrecision(p));if(q<1000){s=r}else{if(q<1000000){s=Math.round((r/1000).toPrecision(3)).toFixed(0)+"K"}else{if(q<1000000000){s=Math.round((r/1000000).toPrecision(3)).toFixed(0)+"M"}}}}return s}};var d=Backbone.Model.extend({});var a=Backbone.View.extend({className:"circster",initialize:function(p){this.genome=p.genome;this.label_arc_height=50;this.scale=1;this.circular_views=null;this.chords_views=null;this.model.get("drawables").on("add",this.add_track,this);this.model.get("drawables").on("remove",this.remove_track,this);var q=this.model.get("config");q.get("arc_dataset_height").on("change:value",this.update_track_bounds,this);q.get("track_gap").on("change:value",this.update_track_bounds,this)},get_circular_tracks:function(){return this.model.get("drawables").filter(function(p){return p.get("track_type")!=="DiagonalHeatmapTrack"})},get_chord_tracks:function(){return this.model.get("drawables").filter(function(p){return p.get("track_type")==="DiagonalHeatmapTrack"})},get_tracks_bounds:function(){var r=this.get_circular_tracks(),t=this.model.get("config").get_value("arc_dataset_height"),s=this.model.get("config").get_value("track_gap"),p=Math.min(this.$el.width(),this.$el.height())-20,v=p/2-r.length*(t+s)+s-this.label_arc_height,u=n.range(v,p/2,t+s);var q=this;return i.map(u,function(w){return[w,w+t]})},render:function(){var y=this,p=y.$el.width(),x=y.$el.height(),u=this.get_circular_tracks(),s=this.get_chord_tracks(),r=y.model.get("config").get_value("total_gap"),t=this.get_tracks_bounds(),q=n.select(y.$el[0]).append("svg").attr("width",p).attr("height",x).attr("pointer-events","all").append("svg:g").call(n.behavior.zoom().on("zoom",function(){var z=n.event.scale;q.attr("transform","translate("+n.event.translate+") scale("+z+")");if(y.scale!==z){if(y.zoom_drag_timeout){clearTimeout(y.zoom_drag_timeout)}y.zoom_drag_timeout=setTimeout(function(){},400)}})).attr("transform","translate("+p/2+","+x/2+")").append("svg:g").attr("class","tracks");this.circular_views=u.map(function(A,B){var z=new e({el:q.append("g")[0],track:A,radius_bounds:t[B],genome:y.genome,total_gap:r});z.render();return z});this.chords_views=s.map(function(A){var z=new l({el:q.append("g")[0],track:A,radius_bounds:t[0],genome:y.genome,total_gap:r});z.render();return z});var w=this.circular_views[this.circular_views.length-1].radius_bounds[1],v=[w,w+this.label_arc_height];this.label_track_view=new b({el:q.append("g")[0],track:new d(),radius_bounds:v,genome:y.genome,total_gap:r});this.label_track_view.render()},add_track:function(v){var q=this.model.get("config").get_value("total_gap");if(v.get("track_type")==="DiagonalHeatmapTrack"){var r=this.circular_views[0].radius_bounds,u=new l({el:n.select("g.tracks").append("g")[0],track:v,radius_bounds:r,genome:this.genome,total_gap:q});u.render();this.chords_views.push(u)}else{var t=this.get_tracks_bounds();i.each(this.circular_views,function(w,x){w.update_radius_bounds(t[x])});i.each(this.chords_views,function(w){w.update_radius_bounds(t[0])});var s=this.circular_views.length,p=new e({el:n.select("g.tracks").append("g")[0],track:v,radius_bounds:t[s],genome:this.genome,total_gap:q});p.render();this.circular_views.push(p)}},remove_track:function(q,s,r){var p=this.circular_views[r.index];this.circular_views.splice(r.index,1);p.$el.remove();var t=this.get_tracks_bounds();i.each(this.circular_views,function(u,v){u.update_radius_bounds(t[v])})},update_track_bounds:function(){var p=this.get_tracks_bounds();i.each(this.circular_views,function(q,r){q.update_radius_bounds(p[r])});i.each(this.chords_views,function(q){q.update_radius_bounds(p[0])})}});var m=Backbone.View.extend({tagName:"g",initialize:function(p){this.bg_stroke="ddd";this.loading_bg_fill="ffc";this.bg_fill="ddd";this.total_gap=p.total_gap;this.track=p.track;this.radius_bounds=p.radius_bounds;this.genome=p.genome;this.chroms_layout=this._chroms_layout();this.data_bounds=[];this.scale=1;this.parent_elt=n.select(this.$el[0])},get_fill_color:function(){var p=this.track.get("config").get_value("block_color");if(!p){p=this.track.get("config").get_value("color")}return p},render:function(){var t=this.parent_elt;if(!t){console.log("no parent elt")}var s=this.chroms_layout,v=n.svg.arc().innerRadius(this.radius_bounds[0]).outerRadius(this.radius_bounds[1]),p=t.selectAll("g").data(s).enter().append("svg:g"),r=p.append("path").attr("d",v).attr("class","chrom-background").style("stroke",this.bg_stroke).style("fill",this.loading_bg_fill);r.append("title").text(function(x){return x.data.chrom});var q=this,u=q.track.get("data_manager"),w=(u?u.data_is_ready():true);$.when(w).then(function(){$.when(q._render_data(t)).then(function(){r.style("fill",q.bg_fill);q.render_labels()})})},render_labels:function(){},update_radius_bounds:function(q){this.radius_bounds=q;var p=n.svg.arc().innerRadius(this.radius_bounds[0]).outerRadius(this.radius_bounds[1]);this.parent_elt.selectAll("g>path.chrom-background").transition().duration(1000).attr("d",p);this._transition_chrom_data();this._transition_labels()},update_scale:function(s){var r=this.scale;this.scale=s;if(s<=r){return}var q=this,p=new o();this.parent_elt.selectAll("path.chrom-data").filter(function(u,t){return p.is_visible(this)}).each(function(z,v){var y=n.select(this),u=y.attr("chrom"),x=q.genome.get_chrom_region(u),w=q.track.get("data_manager"),t;if(!w.can_get_more_detailed_data(x)){return}t=q.track.get("data_manager").get_more_detailed_data(x,"Coverage",0,s);$.when(t).then(function(C){y.remove();q._update_data_bounds();var B=i.find(q.chroms_layout,function(D){return D.data.chrom===u});var A=q.get_fill_color();q._render_chrom_data(q.parent_elt,B,C).style("stroke",A).style("fill",A)})});return q},_transition_chrom_data:function(){var q=this.track,s=this.chroms_layout,p=this.parent_elt.selectAll("g>path.chrom-data"),t=p[0].length;if(t>0){var r=this;$.when(q.get("data_manager").get_genome_wide_data(this.genome)).then(function(v){var u=i.reject(i.map(v,function(w,x){var y=null,z=r._get_path_function(s[x],w);if(z){y=z(w.data)}return y}),function(w){return w===null});p.each(function(x,w){n.select(this).transition().duration(1000).attr("d",u[w])})})}},_transition_labels:function(){},_update_data_bounds:function(){var p=this.data_bounds;this.data_bounds=this.get_data_bounds(this.track.get("data_manager").get_genome_wide_data(this.genome));if(this.data_bounds[0]<p[0]||this.data_bounds[1]>p[1]){this._transition_chrom_data()}},_render_data:function(s){var r=this,q=this.chroms_layout,p=this.track,t=$.Deferred();$.when(p.get("data_manager").get_genome_wide_data(this.genome)).then(function(v){r.data_bounds=r.get_data_bounds(v);layout_and_data=i.zip(q,v),chroms_data_layout=i.map(layout_and_data,function(w){var x=w[0],y=w[1];return r._render_chrom_data(s,x,y)});var u=r.get_fill_color();r.parent_elt.selectAll("path.chrom-data").style("stroke",u).style("fill",u);t.resolve(s)});return t},_render_chrom_data:function(p,q,r){},_get_path_function:function(q,p){},_chroms_layout:function(){var q=this.genome.get_chroms_info(),s=n.layout.pie().value(function(u){return u.len}).sort(null),t=s(q),p=2*Math.PI*this.total_gap/q.length,r=i.map(t,function(w,v){var u=w.endAngle-p;w.endAngle=(u>w.startAngle?u:w.startAngle);return w});return r}});var b=m.extend({initialize:function(p){m.prototype.initialize.call(this,p);this.innerRadius=this.radius_bounds[0];this.radius_bounds[0]=this.radius_bounds[1];this.bg_stroke="fff";this.bg_fill="fff";this.min_arc_len=0.05},_render_data:function(r){var q=this,p=r.selectAll("g");p.selectAll("path").attr("id",function(v){return"label-"+v.data.chrom});p.append("svg:text").filter(function(v){return v.endAngle-v.startAngle>q.min_arc_len}).attr("text-anchor","middle").append("svg:textPath").attr("class","chrom-label").attr("xlink:href",function(v){return"#label-"+v.data.chrom}).attr("startOffset","25%").text(function(v){return v.data.chrom});var s=function(x){var v=(x.endAngle-x.startAngle)/x.value,w=n.range(0,x.value,25000000).map(function(y,z){return{radius:q.innerRadius,angle:y*v+x.startAngle,label:z===0?0:(z%3?null:q.formatNum(y))}});if(w.length<4){w[w.length-1].label=q.formatNum(Math.round((w[w.length-1].angle-x.startAngle)/v))}return w};var u=function(v){return v.angle>Math.PI?"rotate(180)translate(-16)":null};var t=i.filter(this.chroms_layout,function(v){return v.endAngle-v.startAngle>q.min_arc_len});this.drawTicks(this.parent_elt,t,s,u)}});i.extend(b.prototype,j);var g=m.extend({_quantile:function(q,p){q.sort(n.ascending);return n.quantile(q,p)},_render_chrom_data:function(p,s,q){var t=this._get_path_function(s,q);if(!t){return null}var r=p.datum(q.data),u=r.append("path").attr("class","chrom-data").attr("chrom",s.data.chrom).attr("d",t);return u},_get_path_function:function(s,r){if(typeof r==="string"||!r.data||r.data.length===0){return null}var p=n.scale.linear().domain(this.data_bounds).range(this.radius_bounds).clamp(true);var t=n.scale.linear().domain([0,r.data.length]).range([s.startAngle,s.endAngle]);var q=n.svg.line.radial().interpolate("linear").radius(function(u){return p(u[1])}).angle(function(v,u){return t(u)});return n.svg.area.radial().interpolate(q.interpolate()).innerRadius(p(0)).outerRadius(q.radius()).angle(q.angle())},render_labels:function(){var p=this,q=function(){return"rotate(90)"};this.drawTicks(this.parent_elt,[this.chroms_layout[0]],this._data_bounds_ticks_fn(),q,true)},_transition_labels:function(){if(this.data_bounds.length===0){return}var q=this,s=i.filter(this.chroms_layout,function(t){return t.endAngle-t.startAngle>0.08}),r=i.filter(s,function(u,t){return t%3===0}),p=i.flatten(i.map(r,function(t){return q._data_bounds_ticks_fn()(t)}));this.parent_elt.selectAll("g.tick").data(p).transition().attr("transform",function(t){return"rotate("+(t.angle*180/Math.PI-90)+")translate("+t.radius+",0)"})},_data_bounds_ticks_fn:function(){var p=this;visibleChroms=0;return function(q){return[{radius:p.radius_bounds[0],angle:q.startAngle,label:p.formatNum(p.data_bounds[0])},{radius:p.radius_bounds[1],angle:q.startAngle,label:p.formatNum(p.data_bounds[1])}]}},get_data_bounds:function(p){}});i.extend(g.prototype,j);var e=g.extend({get_data_bounds:function(q){var p=i.flatten(i.map(q,function(r){if(r){return i.map(r.data,function(s){return parseInt(s[1],10)||0})}else{return 0}}));return[i.min(p),this._quantile(p,0.5)||i.max(p)]}});var l=m.extend({render:function(){var p=this;$.when(p.track.get("data_manager").data_is_ready()).then(function(){$.when(p.track.get("data_manager").get_genome_wide_data(p.genome)).then(function(s){var r=[],q=p.genome.get_chroms_info();i.each(s,function(w,v){var t=q[v].chrom;var u=i.map(w.data,function(y){var x=p._get_region_angle(t,y[1]),z=p._get_region_angle(y[3],y[4]);return{source:{startAngle:x,endAngle:x+0.01},target:{startAngle:z,endAngle:z+0.01}}});r=r.concat(u)});p.parent_elt.append("g").attr("class","chord").selectAll("path").data(r).enter().append("path").style("fill",p.get_fill_color()).attr("d",n.svg.chord().radius(p.radius_bounds[0])).style("opacity",1)})})},update_radius_bounds:function(p){this.radius_bounds=p;this.parent_elt.selectAll("path").transition().attr("d",n.svg.chord().radius(this.radius_bounds[0]))},_get_region_angle:function(r,p){var q=i.find(this.chroms_layout,function(s){return s.data.chrom===r});return q.endAngle-((q.endAngle-q.startAngle)*(q.data.len-p)/q.data.len)}});var f=Backbone.View.extend({initialize:function(){var p=new k.Genome(galaxy_config.app.genome),q=new k.GenomeVisualization(galaxy_config.app.viz_config);q.get("config").add([{key:"arc_dataset_height",label:"Arc Dataset Height",type:"int",value:25,view:"circster"},{key:"track_gap",label:"Gap Between Tracks",type:"int",value:5,view:"circster"},{key:"total_gap",label:"Gap [0-1]",type:"float",value:0.4,view:"circster",hidden:true}]);var s=new a({el:$("#center .unified-panel-body"),genome:p,model:q});s.render();$("#center .unified-panel-header-inner").append(galaxy_config.app.viz_config.title+" "+galaxy_config.app.viz_config.dbkey);var r=create_icon_buttons_menu([{icon_class:"plus-button",title:"Add tracks",on_click:function(){k.select_datasets(galaxy_config.root+"visualization/list_current_history_datasets",galaxy_config.root+"api/datasets",q.get("dbkey"),function(t){q.add_tracks(t)})}},{icon_class:"gear",title:"Settings",on_click:function(){var t=new c.ConfigSettingCollectionView({collection:q.get("config")});t.render_in_modal("Configure Visualization")}},{icon_class:"disk--arrow",title:"Save",on_click:function(){Galaxy.modal.show({title:"Saving...",body:"progress"});$.ajax({url:galaxy_config.root+"visualization/save",type:"POST",dataType:"json",data:{id:q.get("vis_id"),title:q.get("title"),dbkey:q.get("dbkey"),type:"trackster",vis_json:JSON.stringify(q)}}).success(function(t){Galaxy.modal.hide();q.set("vis_id",t.vis_id)}).error(function(){Galaxy.modal.show({title:"Could Not Save",body:"Could not save visualization. Please try again later.",buttons:{Cancel:function(){Galaxy.modal.hide()}}})})}},{icon_class:"cross-circle",title:"Close",on_click:function(){window.location=galaxy_config.root+"visualization/list"}}],{tooltip_config:{placement:"bottom"}});r.$el.attr("style","float: right");$("#center .unified-panel-header-inner").append(r.$el);$(".menu-button").tooltip({placement:"bottom"})}});return{GalaxyApp:f}});
\ No newline at end of file
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/scripts/utils/uploadbox.js
--- a/static/scripts/utils/uploadbox.js
+++ b/static/scripts/utils/uploadbox.js
@@ -123,6 +123,10 @@
if (queue_running)
return;
+ // backup queue index
+ var current_index = queue_index;
+
+ // add files to queue
for (var i = 0; i < files.length; i++)
{
// check
@@ -136,11 +140,14 @@
queue[index] = files[i];
// announce
- opts.announce(index, queue[index], "");
+ opts.announce(index, queue[index], '');
// increase counter
queue_length++;
}
+
+ // return
+ return current_index;
}
// remove entry from queue
@@ -183,12 +190,15 @@
// remove from queue
remove(index)
+ // collect file details
+ var filesize = file.size;
+ var filemode = file.mode;
+
// identify maximum file size
- var filesize = file.size;
var maxfilesize = 1048576 * opts.maxfilesize;
- // check file size
- if (filesize < maxfilesize)
+ // check file size, unless its an ftp file
+ if (filesize < maxfilesize || file.mode == 'ftp')
{
// get parameters
var data = opts.initialize(index, file);
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/scripts/utils/utils.js
--- a/static/scripts/utils/utils.js
+++ b/static/scripts/utils/utils.js
@@ -71,12 +71,39 @@
return optionsDefault;
};
+// to string
+function bytesToString (size, normal_font) {
+ // identify unit
+ var unit = "";
+ if (size >= 100000000000) { size = size / 100000000000; unit = 'TB'; } else
+ if (size >= 100000000) { size = size / 100000000; unit = 'GB'; } else
+ if (size >= 100000) { size = size / 100000; unit = 'MB'; } else
+ if (size >= 100) { size = size / 100; unit = 'KB'; } else
+ if (size > 0) { size = size * 10; unit = 'b'; } else
+ return '<strong>-</strong>';
+
+ // return formatted string
+ var rounded = (Math.round(size) / 10);
+ if (normal_font) {
+ return rounded + ' ' + unit;
+ } else {
+ return '<strong>' + rounded + '</strong> ' + unit;
+ }
+};
+
+// unique ide
+function uuid(){
+ return (new Date().getTime()).toString(36);
+};
+
// return
return {
cssLoadFile : cssLoadFile,
cssGetAttribute : cssGetAttribute,
jsonFromUrl : jsonFromUrl,
- merge : merge
+ merge : merge,
+ bytesToString: bytesToString,
+ uuid: uuid
};
});
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/style/blue/base.css
--- a/static/style/blue/base.css
+++ b/static/style/blue/base.css
@@ -1250,26 +1250,34 @@
.galaxy-frame .frame .f-close{right:5px;top:3px}
.galaxy-frame .frame .f-pin{left:6px;top:3px}
.galaxy-frame .frame .f-resize{background:#fff;width:16px;height:16px;color:#2c3143;right:0px;bottom:0px;text-align:center;line-height:16px;border:0px}
-.progress-button{position:relative;width:31px;height:21px;top:-4px;cursor:pointer;-moz-border-radius:3px;border-radius:3px}.progress-button .progress{height:21px}
-.progress-button .progress-bar-danger{background:#f9c7c5}
-.progress-button .progress-bar-success{background:#aff1af}
-.progress-button .progress-bar-info{background:#ffc}
-.progress-button .progress-bar-notransition{-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none}
-.progress-button .label{position:absolute;top:2px;width:inherit;text-align:center;line-height:19px;font-size:1.2em}
+.upload-button{position:relative;width:31px;height:21px;top:-4px;cursor:pointer;-moz-border-radius:3px;border-radius:3px}.upload-button .progress{height:21px}
+.upload-button .progress-bar-danger{background:#f9c7c5}
+.upload-button .progress-bar-success{background:#aff1af}
+.upload-button .progress-bar-info{background:#ffc}
+.upload-button .progress-bar-notransition{-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none}
+.upload-button .label{position:absolute;top:2px;width:inherit;text-align:center;line-height:19px;font-size:1.2em}
+.upload-icon{font-size:1.2em;width:1.2em}
+.upload-icon-button{font-size:1.2em;width:1.2em;cursor:pointer}
.upload-info{font-weight:normal;text-align:center}
.upload-box{width:100%;height:95%;text-align:center;overflow:scroll;font-size:12px;line-height:1.33;-moz-border-radius:5px;border-radius:5px;border:1px dashed #bfbfbf;padding:10px;overflow-x:hidden;-ms-overflow-style:none}.upload-box .popover{max-width:500px;width:auto}
.upload-box .table{width:100%}
.upload-box .table th{text-align:center;white-space:nowrap}
.upload-box .table td{margin:0px;paddign:0px}
-.upload-box .title{width:130px;word-wrap:break-word;font-size:11px}
+.upload-box .mode{float:left;font-size:1.2em;width:1.2em;color:#555}
+.upload-box .title{width:130px;word-wrap:break-word;font-size:11px;float:left}
.upload-box .text{position:absolute;display:none}.upload-box .text .text-content{font-size:11px;width:100%;height:50px;resize:none;background:inherit;color:#000}
.upload-box .text .text-info{font-size:11px}
.upload-box .extension{width:100px;font-size:11px}
.upload-box .genome{width:150px;font-size:11px}
.upload-box .size{width:60px;white-space:nowrap}
-.upload-box .info{width:130px;font-size:11px}.upload-box .info .progress{top:1px;position:relative;width:100%;padding:0px;margin:0px}.upload-box .info .progress .progress-bar{border-radius:inherit;-moz-border-radius:inherit}
+.upload-box .info{width:130px;font-size:11px;line-height:1.2em}.upload-box .info .progress{top:1px;position:relative;width:100%;padding:0px;margin:0px;line-height:1.55em}.upload-box .info .progress .progress-bar{border-radius:inherit;-moz-border-radius:inherit}
.upload-box .info .progress .percentage{position:absolute;text-align:center;width:100%;color:#fff}
-.upload-box .symbol{font-size:1.2em;cursor:pointer}
+.upload-ftp{height:250px;width:500px;overflow:auto}.upload-ftp .upload-ftp-wait{font-size:1.2em;position:absolute;bottom:10px;right:10px}
+.upload-ftp .upload-ftp-help{margin-bottom:10px}
+.upload-ftp .upload-ftp-warning{text-align:center;margin-top:20px}
+.upload-settings .upload-settings-cover{position:absolute;width:100%;height:100%;top:0px;left:0px;background:#fff;opacity:0.4;cursor:no-drop}
+.popover-view{max-width:700px;display:none}.popover-view .popover-close{position:absolute;right:10px;top:7px;font-size:1.2em;cursor:pointer}
+.popover-view .popover-title{padding:4px 10px}
.libraryRow{background-color:#ebd9b2}
.datasetHighlighted{background-color:#f9f9f9}
.libraryItemDeleted-True{font-style:italic}
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/style/src/less/base.less
--- a/static/style/src/less/base.less
+++ b/static/style/src/less/base.less
@@ -23,6 +23,7 @@
// galaxy sub-components
@import "frame.less";
@import "upload.less";
+@import "ui.less";
@import "library.less";
@import "toastr.less";
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/style/src/less/ui.less
--- /dev/null
+++ b/static/style/src/less/ui.less
@@ -0,0 +1,16 @@
+.popover-view {
+ max-width: 700px;
+ display: none;
+
+ .popover-close {
+ position: absolute;
+ right: 10px;
+ top: 7px;
+ font-size: 1.2em;
+ cursor: pointer;
+ }
+
+ .popover-title {
+ padding: 4px 10px;
+ }
+}
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 static/style/src/less/upload.less
--- a/static/style/src/less/upload.less
+++ b/static/style/src/less/upload.less
@@ -1,4 +1,4 @@
-.progress-button {
+.upload-button {
position: relative;
width: 31px;
height: 21px;
@@ -41,6 +41,17 @@
}
}
+.upload-icon {
+ font-size: 1.2em;
+ width: 1.2em;
+}
+
+.upload-icon-button {
+ font-size: 1.2em;
+ width: 1.2em;
+ cursor: pointer;
+}
+
.upload-info {
font-weight: normal;
text-align: center;
@@ -79,10 +90,18 @@
paddign: 0px;
}
+ .mode {
+ float: left;
+ font-size: 1.2em;
+ width: 1.2em;
+ color: @gray;
+ }
+
.title {
width: 130px;
word-wrap: break-word;
font-size : @font-size-small;
+ float: left;
}
.text {
@@ -122,6 +141,7 @@
.info {
width: 130px;
font-size : @font-size-small;
+ line-height: 1.2em;
.progress {
top:1px;
@@ -129,7 +149,8 @@
width: 100%;
padding: 0px;
margin: 0px;
-
+ line-height: 1.55em;
+
.progress-bar {
border-radius: inherit;
-moz-border-radius: inherit;
@@ -143,9 +164,40 @@
}
}
}
+}
+
+.upload-ftp {
+ height: 250px;
+ width: 500px;
+ overflow: auto;
- .symbol {
+ .upload-ftp-wait {
font-size: 1.2em;
- cursor: pointer;
+ position: absolute;
+ bottom: 10px;
+ right: 10px;
+ }
+
+ .upload-ftp-help {
+ margin-bottom: 10px;
+ }
+
+ .upload-ftp-warning {
+ text-align: center;
+ margin-top: 20px;
}
}
+
+
+.upload-settings {
+ .upload-settings-cover {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ top: 0px;
+ left: 0px;
+ background: @white;
+ opacity: 0.4;
+ cursor: no-drop;
+ }
+}
diff -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 -r d8fcf33cbe4ac5e1129c674b1807675b2c02ccc6 templates/webapps/galaxy/galaxy.masthead.mako
--- a/templates/webapps/galaxy/galaxy.masthead.mako
+++ b/templates/webapps/galaxy/galaxy.masthead.mako
@@ -52,6 +52,8 @@
'logo_url' : h.url_for(app.config.get( 'logo_url', '/')),
'is_admin_user' : trans.user and app.config.is_admin_user(trans.user),
'active_view' : active_view,
+ 'ftp_upload_dir' : app.config.get("ftp_upload_dir", None),
+ 'ftp_upload_site' : app.config.get("ftp_upload_site", None),
## user details
'user' : {
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.
1
0
commit/galaxy-central: jmchilton: Minor style and refactoring touch up to new workflow/run.py.
by commits-noreply@bitbucket.org 07 Feb '14
by commits-noreply@bitbucket.org 07 Feb '14
07 Feb '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/21c6fe1aec03/
Changeset: 21c6fe1aec03
User: jmchilton
Date: 2014-02-07 23:59:37
Summary: Minor style and refactoring touch up to new workflow/run.py.
Reduces cyclomatic complexity and makes downstream diffs with dataset collection work less dramatic.
Affected #: 2 files
diff -r 749db2ae03b4ff4c83053322ef291af3b9173325 -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 lib/galaxy/workflow/modules.py
--- a/lib/galaxy/workflow/modules.py
+++ b/lib/galaxy/workflow/modules.py
@@ -152,6 +152,7 @@
.add_text( "name", "Name", value=self.state['name'] )
return self.trans.fill_template( "workflow/editor_generic_form.mako",
module=self, form=form )
+
def get_state( self, secure=True ):
return to_json_string( self.state )
@@ -168,11 +169,11 @@
return state
def encode_runtime_state( self, trans, state ):
- fake_tool = Bunch( inputs = self.get_runtime_inputs() )
+ fake_tool = Bunch( inputs=self.get_runtime_inputs() )
return state.encode( fake_tool, trans.app )
def decode_runtime_state( self, trans, string ):
- fake_tool = Bunch( inputs = self.get_runtime_inputs() )
+ fake_tool = Bunch( inputs=self.get_runtime_inputs() )
state = galaxy.tools.DefaultToolState()
state.decode( string, fake_tool, trans.app )
return state
@@ -221,7 +222,7 @@
module.state = galaxy.tools.DefaultToolState()
if module.tool is not None:
if d.get('tool_version', 'Unspecified') != module.get_tool_version():
- module.version_changes.append("%s: using version '%s' instead of version '%s' indicated in this workflow." % (tool_id, d.get('tool_version', 'Unspecified'), module.get_tool_version()) )
+ module.version_changes.append( "%s: using version '%s' instead of version '%s' indicated in this workflow." % ( tool_id, d.get( 'tool_version', 'Unspecified' ), module.get_tool_version() ) )
module.state.decode( d[ "tool_state" ], module.tool, module.trans.app, secure=secure )
module.errors = d.get( "tool_errors", None )
module.post_job_actions = d.get( "post_job_actions", {} )
@@ -329,7 +330,7 @@
data_inputs = None
for name, tool_output in self.tool.outputs.iteritems():
if tool_output.format_source != None:
- formats = [ 'input' ] # default to special name "input" which remove restrictions on connections
+ formats = [ 'input' ] # default to special name "input" which remove restrictions on connections
if data_inputs == None:
data_inputs = self.get_data_inputs()
# find the input parameter referenced by format_source
diff -r 749db2ae03b4ff4c83053322ef291af3b9173325 -r 21c6fe1aec03c44a4fd9abbc2267bec5b29611d5 lib/galaxy/workflow/run.py
--- a/lib/galaxy/workflow/run.py
+++ b/lib/galaxy/workflow/run.py
@@ -64,7 +64,6 @@
def _execute_tool_step( self, step ):
trans = self.trans
outputs = self.outputs
- replacement_dict = self.replacement_dict
tool = trans.app.toolbox.get_tool( step.tool_id )
@@ -72,12 +71,7 @@
def callback( input, value, prefixed_name, prefixed_label ):
replacement = None
if isinstance( input, DataToolParameter ):
- if prefixed_name in step.input_connections_by_name:
- conn = step.input_connections_by_name[ prefixed_name ]
- if input.multiple:
- replacement = [outputs[ c.output_step.id ][ c.output_name ] for c in conn]
- else:
- replacement = outputs[ conn[0].output_step.id ][ conn[0].output_name ]
+ replacement = self._replacement_for_input( input, prefixed_name, step )
return replacement
try:
# Replace DummyDatasets with historydatasetassociations
@@ -85,17 +79,10 @@
except KeyError, k:
raise exceptions.MessageException( "Error due to input mapping of '%s' in '%s'. A common cause of this is conditional outputs that cannot be determined until runtime, please review your workflow." % (tool.name, k.message))
# Execute it
- job, out_data = tool.execute( trans, step.state.inputs, history=self.target_history)
+ job, out_data = tool.execute( trans, step.state.inputs, history=self.target_history )
outputs[ step.id ] = out_data
- # Create new PJA associations with the created job, to be run on completion.
- # PJA Parameter Replacement (only applies to immediate actions-- rename specifically, for now)
- # Pass along replacement dict with the execution of the PJA so we don't have to modify the object.
- for pja in step.post_job_actions:
- if pja.action_type in ActionBox.immediate_actions:
- ActionBox.execute( trans.app, trans.sa_session, pja, job, replacement_dict )
- else:
- job.add_post_job_action(pja)
+ self._handle_post_job_actions( step, job )
return job
def _execute_input_step( self, step ):
@@ -110,10 +97,35 @@
if self.copy_inputs_to_history:
for input_dataset_hda in out_data.values():
new_hda = input_dataset_hda.copy( copy_children=True )
- self.target_history.add_dataset(new_hda)
- outputs[ step.id ]['input_ds_copy'] = new_hda
+ self.target_history.add_dataset( new_hda )
+ outputs[ step.id ][ 'input_ds_copy' ] = new_hda
if self.ds_map:
- outputs[step.id]['output'] = self.ds_map[ str( step.id ) ][ 'hda' ]
+ outputs[ step.id ][ 'output' ] = self.ds_map[ str( step.id ) ][ 'hda' ]
+
return job
+ def _handle_post_job_actions( self, step, job ):
+ # Create new PJA associations with the created job, to be run on completion.
+ # PJA Parameter Replacement (only applies to immediate actions-- rename specifically, for now)
+ # Pass along replacement dict with the execution of the PJA so we don't have to modify the object.
+ for pja in step.post_job_actions:
+ if pja.action_type in ActionBox.immediate_actions:
+ ActionBox.execute( self.trans.app, self.trans.sa_session, pja, job, self.replacement_dict )
+ else:
+ job.add_post_job_action( pja )
+
+ def _replacement_for_input( self, input, prefixed_name, step ):
+ """ For given workflow 'step' that has had input_connections_by_name
+ populated fetch the actual runtime input for the given tool 'input'.
+ """
+ replacement = None
+ if prefixed_name in step.input_connections_by_name:
+ outputs = self.outputs
+ connection = step.input_connections_by_name[ prefixed_name ]
+ if input.multiple:
+ replacement = [ outputs[ c.output_step.id ][ c.output_name ] for c in connection ]
+ else:
+ replacement = outputs[ connection[ 0 ].output_step.id ][ connection[ 0 ].output_name ]
+ return replacement
+
__all__ = [ invoke ]
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.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/def88c93886e/
Changeset: def88c93886e
User: jeremy goecks
Date: 2014-02-07 21:02:49
Summary: Backbone config settings view: hide modal before updating model.
Affected #: 1 file
diff -r a253d56a0dd6be3816581a0c7ed0ada191a1693b -r def88c93886e490e70f449644e002277be996b30 static/scripts/utils/config.js
--- a/static/scripts/utils/config.js
+++ b/static/scripts/utils/config.js
@@ -225,9 +225,9 @@
var self = this,
cancel_fn = function() { Galaxy.modal.hide(); $(window).unbind("keypress.check_enter_esc"); },
ok_fn = function() {
- self.update_from_form();
Galaxy.modal.hide();
$(window).unbind("keypress.check_enter_esc");
+ self.update_from_form();
},
check_enter_esc = function(e) {
if ((e.keyCode || e.which) === 27) { // Escape key
https://bitbucket.org/galaxy/galaxy-central/commits/749db2ae03b4/
Changeset: 749db2ae03b4
User: jeremy goecks
Date: 2014-02-07 21:04:42
Summary: Circster: (a) fix bad math that led to incorrect track positioning and (b) make gap between tracks a configurable option.
Affected #: 1 file
diff -r def88c93886e490e70f449644e002277be996b30 -r 749db2ae03b4ff4c83053322ef291af3b9173325 static/scripts/viz/circster.js
--- a/static/scripts/viz/circster.js
+++ b/static/scripts/viz/circster.js
@@ -134,7 +134,6 @@
initialize: function(options) {
this.genome = options.genome;
- this.track_gap = 10;
this.label_arc_height = 50;
this.scale = 1;
this.circular_views = null;
@@ -147,6 +146,7 @@
// When config settings change, update view.
var vis_config = this.model.get('config');
vis_config.get('arc_dataset_height').on('change:value', this.update_track_bounds, this);
+ vis_config.get('track_gap').on('change:value', this.update_track_bounds, this);
},
// HACKs: using track_type for circular/chord distinction in the functions below for now.
@@ -175,16 +175,20 @@
get_tracks_bounds: function() {
var circular_tracks = this.get_circular_tracks(),
dataset_arc_height = this.model.get('config').get_value('arc_dataset_height'),
- min_dimension = Math.min(this.$el.width(), this.$el.height()),
+ track_gap = this.model.get('config').get_value('track_gap'),
+ // Subtract 20 to make sure chrom labels are on screen.
+ min_dimension = Math.min(this.$el.width(), this.$el.height()) - 20,
// Compute radius start based on model, will be centered
// and fit entirely inside element by default.
radius_start = min_dimension / 2 -
- circular_tracks.length * (dataset_arc_height + this.track_gap) -
- (this.label_arc_height + this.track_gap),
+ circular_tracks.length * (dataset_arc_height + track_gap) +
+ // Add track_gap back in because no gap is needed for last track.
+ track_gap -
+ this.label_arc_height,
// Compute range of track starting radii.
- tracks_start_radii = d3.range(radius_start, min_dimension / 2, dataset_arc_height + this.track_gap);
-
+ tracks_start_radii = d3.range(radius_start, min_dimension / 2, dataset_arc_height + track_gap);
+
// Map from track start to bounds.
var self = this;
return _.map(tracks_start_radii, function(radius) {
@@ -317,7 +321,7 @@
else {
// Added circular track.
- // Recompute and update track bounds.
+ // Recompute and update circular track bounds.
var new_track_bounds = this.get_tracks_bounds();
_.each(this.circular_views, function(track_view, i) {
track_view.update_radius_bounds(new_track_bounds[i]);
@@ -341,9 +345,13 @@
this.circular_views.push(track_view);
// Update label track.
+ /*
+ FIXME: should never have to update label track because vis always expands to fit area
+ within label track.
var track_bounds = new_track_bounds[ new_track_bounds.length-1 ];
track_bounds[1] = track_bounds[0];
this.label_track_view.update_radius_bounds(track_bounds);
+ */
}
},
@@ -369,6 +377,12 @@
_.each(this.circular_views, function(track_view, i) {
track_view.update_radius_bounds(new_track_bounds[i]);
});
+
+ // Update chords tracks.
+ _.each(this.chords_views, function(track_view) {
+ track_view.update_radius_bounds(new_track_bounds[0]);
+ });
+
}
});
@@ -381,11 +395,11 @@
/* ----------------------- Public Methods ------------------------- */
initialize: function(options) {
- this.bg_stroke = 'ccc';
+ this.bg_stroke = 'ddd';
// Fill color when loading data.
- this.loading_bg_fill = '000';
+ this.loading_bg_fill = 'ffc';
// Fill color when data has been loaded.
- this.bg_fill = 'ccc';
+ this.bg_fill = 'ddd';
this.total_gap = options.total_gap;
this.track = options.track;
this.radius_bounds = options.radius_bounds;
@@ -898,7 +912,7 @@
// For max, use 98% quantile in attempt to avoid very large values. However, this max may be 0
// for sparsely populated data, so use max in that case.
- return [ _.min(values), this._quantile(values, 0.98) || _.max(values) ];
+ return [ _.min(values), this._quantile(values, 0.5) || _.max(values) ];
}
});
@@ -987,6 +1001,7 @@
// Add Circster-specific config options.
vis.get('config').add([
{ key: 'arc_dataset_height', label: 'Arc Dataset Height', type: 'int', value: 25, view: 'circster' },
+ { key: 'track_gap', label: 'Gap Between Tracks', type: 'int', value: 5, view: 'circster' },
{ key: 'total_gap', label: 'Gap [0-1]', type: 'float', value: 0.4, view: 'circster', hidden: true }
]);
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.
1
0
commit/galaxy-central: carlfeberhard: Visualizations framework: allow for saving and loading registry visualizations, allow better merging of kwargs, config, saved config, add config utilities to common/templates; Scatterplot: allow saving as visualization, more mvc-based, increase max zoom, refactor and cleanup; Charts, Scatterplot: remove visualization data_source
by commits-noreply@bitbucket.org 07 Feb '14
by commits-noreply@bitbucket.org 07 Feb '14
07 Feb '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/a253d56a0dd6/
Changeset: a253d56a0dd6
User: carlfeberhard
Date: 2014-02-07 17:24:32
Summary: Visualizations framework: allow for saving and loading registry visualizations, allow better merging of kwargs, config, saved config, add config utilities to common/templates; Scatterplot: allow saving as visualization, more mvc-based, increase max zoom, refactor and cleanup; Charts, Scatterplot: remove visualization data_source
Affected #: 14 files
diff -r a11c52513e4472237c9070a06ff11f123af83c06 -r a253d56a0dd6be3816581a0c7ed0ada191a1693b config/plugins/visualizations/charts/config/charts.xml
--- a/config/plugins/visualizations/charts/config/charts.xml
+++ b/config/plugins/visualizations/charts/config/charts.xml
@@ -7,15 +7,9 @@
<test type="isinstance" test_attr="datatype" result_type="datatype">tabular.Tabular</test><to_param param_attr="id">dataset_id</to_param></data_source>
- <data_source>
- <model_class>Visualization</model_class>
- <test test_attr="type">charts</test>
- <to_param param_attr="id">visualization_id</to_param>
- </data_source></data_sources><params><param type="dataset" var_name_in_template="hda" required="true">dataset_id</param>
- <param type="visualization" var_name_in_template="visualization">visualization_id</param></params><template>charts.mako</template></visualization>
diff -r a11c52513e4472237c9070a06ff11f123af83c06 -r a253d56a0dd6be3816581a0c7ed0ada191a1693b config/plugins/visualizations/common/templates/config_utils.mako
--- /dev/null
+++ b/config/plugins/visualizations/common/templates/config_utils.mako
@@ -0,0 +1,46 @@
+<%def name="add_config_defaults( defaults )">
+## overwrite default_config_dict with config (if any) then assign to config
+<%
+ for key, default in defaults.items():
+ if key not in config or config[ key ] is None:
+ config[ key ] = default
+%>
+</%def>
+
+<%def name="config_form( config_dict )">
+## render form for everything in possible config
+</%def>
+
+<%def name="link_to_change_config( link_contents, new_settings, target='' )">
+<%
+ # assumes there's a config var
+ url_for_args = {
+ 'controller' : 'visualization',
+ 'action' : 'render',
+ 'visualization_name' : visualization_name,
+ 'title' : title
+ }
+ url_for_args.update( config )
+ url_for_args.update( new_settings )
+ if visualization_id:
+ url_for_args[ 'id' ] = visualization_id
+%>
+ <a href="${h.url_for( **url_for_args )}" target="${target}">${link_contents}</a>
+</%def>
+
+<%def name="save_button( text='Save' )">
+<%
+ # still a GET
+ url_for_args = {
+ 'controller' : 'visualization',
+ 'action' : 'saved',
+ 'type' : visualization_name,
+ 'title' : title,
+ 'config' : h.to_json_string( config )
+ }
+ # save to existing visualization
+ if visualization_id:
+ url_for_args[ 'id' ] = visualization_id
+%>
+ <form action="${h.url_for( **url_for_args )}" method="post"><input type="submit" value="${text}" /></form>
+</%def>
diff -r a11c52513e4472237c9070a06ff11f123af83c06 -r a253d56a0dd6be3816581a0c7ed0ada191a1693b config/plugins/visualizations/common/templates/visualization_base.mako
--- /dev/null
+++ b/config/plugins/visualizations/common/templates/visualization_base.mako
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+<% _=n_ %>
+
+%if embedded:
+ ${self.as_embedded()}
+%else:
+ ${self.as_page()}
+%endif
+
+## render this inside another page or via ajax
+<%def name="as_embedded()">
+ ${self.stylesheets()}
+ ${self.javascripts()}
+ ${self.get_body()}
+</%def>
+
+## render this as it's own page
+<%def name="as_page()">
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>${self.title()}</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ ${self.metas()}
+ ${self.stylesheets()}
+ ${self.javascripts()}
+ </head>
+ <body>
+ ${self.get_body()}
+ </body>
+</html>
+</%def>
+##TODO: late_javascripts
+
+## Default body
+<%def name="get_body()"></%def>
+
+## Default title
+<%def name="title()">${visualization_name}</%def>
+
+## Additional metas can be defined by templates inheriting from this one.
+<%def name="metas()"></%def>
+
+## Default stylesheets
+<%def name="stylesheets()">
+${h.css('base')}
+</%def>
+
+## Default javascripts
+<%def name="javascripts()">
+${h.js(
+ "libs/jquery/jquery",
+ "libs/jquery/jquery.migrate"
+)}
+
+<script type="text/javascript">
+ // console protection
+ window.console = window.console || {
+ log : function(){},
+ debug : function(){},
+ info : function(){},
+ warn : function(){},
+ error : function(){},
+ assert : function(){}
+ };
+</script>
+</%def>
diff -r a11c52513e4472237c9070a06ff11f123af83c06 -r a253d56a0dd6be3816581a0c7ed0ada191a1693b config/plugins/visualizations/scatterplot/config/scatterplot.xml
--- a/config/plugins/visualizations/scatterplot/config/scatterplot.xml
+++ b/config/plugins/visualizations/scatterplot/config/scatterplot.xml
@@ -7,15 +7,9 @@
<test type="isinstance" test_attr="datatype" result_type="datatype">tabular.Tabular</test><to_param param_attr="id">dataset_id</to_param></data_source>
- <data_source>
- <model_class>Visualization</model_class>
- <test test_attr="type">scatterplot</test>
- <to_param param_attr="id">visualization_id</to_param>
- </data_source></data_sources><params><param type="dataset" var_name_in_template="hda" required="true">dataset_id</param>
- <param type="visualization" var_name_in_template="visualization">visualization_id</param></params><template>scatterplot.mako</template></visualization>
diff -r a11c52513e4472237c9070a06ff11f123af83c06 -r a253d56a0dd6be3816581a0c7ed0ada191a1693b config/plugins/visualizations/scatterplot/src/handlebars/editor.handlebars
--- a/config/plugins/visualizations/scatterplot/src/handlebars/editor.handlebars
+++ b/config/plugins/visualizations/scatterplot/src/handlebars/editor.handlebars
@@ -10,14 +10,20 @@
<a title="Use this tab to change how the chart is drawn"
href="#chart-control" data-toggle="tab" >Chart Controls</a></li>
- {{! both stats and chart start as disabled since there's no info yet }}
+ {{! chart starts as disabled since there's no info yet }}
<li class="disabled"><a title="This tab will display the chart"
href="#chart-display" data-toggle="tab">Chart</a></li>
+ {{! ... }}
+ <li class="file-controls">
+<!-- <button class="copy-btn btn btn-default"
+ title="Save this as a new visualization">Save to new</button>-->
+ <button class="save-btn btn btn-default">Save</button>
+ </li></ul>
- {{! data form, chart config form, stats, and chart all get their own tab }}
+ {{! data form, chart config form, chart all get their own tab }}
<div class="tab-content">
{{! ---------------------------- tab for data settings form }}
<div id="data-control" class="scatterplot-config-control tab-pane active">
diff -r a11c52513e4472237c9070a06ff11f123af83c06 -r a253d56a0dd6be3816581a0c7ed0ada191a1693b config/plugins/visualizations/scatterplot/src/scatterplot-config-editor.js
--- a/config/plugins/visualizations/scatterplot/src/scatterplot-config-editor.js
+++ b/config/plugins/visualizations/scatterplot/src/scatterplot-config-editor.js
@@ -1,24 +1,22 @@
/* =============================================================================
todo:
- Remove 'chart' names
- Make this (the config control/editor) and the ScatterplotView (in scatterplot.js) both
- views onto a visualization/revision model
+ import button(display), func(model) - when user doesn't match
Move margins into wid/hi calcs (so final svg dims are w/h)
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 of config
- Auto render if given data and/or config
+ Allow setting perPage in config
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)
Semantic HTML (figure, caption)
- Save as visualization, load from visualization
Save as SVG/png
Does it work w/ Galaxy.Frame?
Embedding
Small multiples
Drag & Drop other splots onto current (redraw with new axis and differentiate the datasets)
+ Remove 'chart' names
+ Somehow link out from info box?
Subclass on specific datatypes? (vcf, cuffdiff, etc.)
What can be common/useful to other visualizations?
@@ -40,138 +38,190 @@
if( !this.model ){
this.model = new Visualization({ type: 'scatterplot' });
}
- console.log( this + '.initialize, attributes:', attributes );
+ this.log( this + '.initialize, attributes:', attributes );
if( !attributes || !attributes.dataset ){
throw new Error( "ScatterplotConfigEditor requires a dataset" );
}
this.dataset = attributes.dataset;
- console.log( 'dataset:', this.dataset );
+ this.log( 'dataset:', this.dataset );
-//TODO: ScatterplotView -> ScatterplotDisplay, this.plotView -> this.display
- this.plotView = new ScatterplotView({
+ this.display = new ScatterplotDisplay({
dataset : attributes.dataset,
model : this.model
-//TODO: if data
});
},
// ------------------------------------------------------------------------- CONTROLS RENDERING
render : function(){
//console.log( this + '.render' );
-
// render the tab controls, areas and loading indicator
- this.$el.append( ScatterplotConfigEditor.templates.mainLayout({}));
+ 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();
// render the tab content
- this.$el.find( '#data-control' ).append( this._render_dataControl() );
- this._render_chartControls( this.$el.find( '#chart-control' ) );
- //this.$statsDisplay = this.$el.find( '.tab-pane#stats-display' );
+ this._render_dataControl();
+ this._render_chartControls();
this._render_chartDisplay();
- //TODO: auto render if given both x, y column choices in query for page
+ // set up behaviours
- // set up behaviours
- this.$el.find( '[title]' ).tooltip();
-
- // uncomment any of the following to have that tab show on initial load (for testing)
- //this.$el.find( 'ul.nav' ).find( 'a[href="#data-control"]' ).tab( 'show' );
- //this.$el.find( 'ul.nav' ).find( 'a[href="#chart-control"]' ).tab( 'show' );
- //this.$el.find( 'ul.nav' ).find( 'a[href="#stats-display"]' ).tab( 'show' );
- //this.$el.find( 'ul.nav' ).find( 'a[href="#chart-display"]' ).tab( 'show' );
+ // 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 ) ){
+ this.renderChart();
+ }
return this;
},
- _render_dataControl : function(){
- // controls for which columns are used to plot datapoints (and ids/additional info to attach if desired)
- var dataset = this.dataset;
+ /** controls for which columns are used to plot datapoints (and ids/additional info to attach if desired) */
+ _render_dataControl : function( $where ){
+ //TODO: better handling of missing column names, column types
+ $where = $where || this.$el;
+ var editor = this,
+ dataset = this.dataset,
+ column_names = dataset.metadata_column_names || [],
+ config = this.model.get( 'config' );
//console.log( 'metadata_column_types:', this.dataset.metadata_column_types );
//console.log( 'metadata_column_names:', this.dataset.metadata_column_names );
- var allColumns = _.map( dataset.metadata_column_types, function( type, i ){
- var column = { index: i, type: type, name: ( 'column ' + ( i + 1 ) ) };
- if( dataset.metadata_column_names && dataset.metadata_column_names[ i ] ){
- column.name = dataset.metadata_column_names[ i ];
- }
- return column;
- });
- var numericColumns = _.filter( allColumns, function( column, i ){
- return ( ( column.type === 'int' ) || ( column.type === 'float' ) );
- });
- if( numericColumns < 2 ){
+//TODO: to peek based control
+ var numericColumns = [],
+ allColumns = _.map( dataset.metadata_column_types, function( type, i ){
+ // save column data for select rendering, adding metadata name if available in dataset
+ var column = { index: i, type: type, name: ( column_names[ i ] || ( 'column ' + ( i + 1 )) ) };
+ // also add column to numerics if numeric type
+ if( ( column.type === 'int' ) || ( column.type === 'float' ) ){
+ numericColumns.push( column );
+ }
+ return column;
+ });
+ if( numericColumns.length < 2 ){
numericColumns = allColumns;
}
//console.log( 'allColumns:', allColumns );
//console.log( 'numericColumns:', numericColumns );
// render the html
- var $dataControl = this.$el.find( '.tab-pane#data-control' );
+ var $dataControl = $where.find( '.tab-pane#data-control' );
$dataControl.html( ScatterplotConfigEditor.templates.dataControl({
allColumns : allColumns,
numericColumns : numericColumns
}));
- // preset to column selectors if they were passed in the config in the query string
- $dataControl.find( '[name="xColumn"]' ).val( this.plotView.config.xColumn || numericColumns[0].index );
- $dataControl.find( '[name="yColumn"]' ).val( this.plotView.config.yColumn || numericColumns[1].index );
- if( this.plotView.config.idColumn !== undefined ){
+//TODO: column selection boilerplate
+ // preset to column selectors if they were passed in the config in the query string; set up events
+ var newConfig = {
+ xColumn : ( _.isFinite( config.xColumn ) )? ( config.xColumn ): ( numericColumns[0].index ),
+ yColumn : ( _.isFinite( config.yColumn ) )? ( config.yColumn ): ( numericColumns[1].index ),
+ idColumn : allColumns[0].index
+ };
+ // use an idColumn from the config or attempt to get one different from the numeric
+ if( _.isFinite( config.idColumn ) ){
+ newConfig.idColumn = config.idColumn;
+ } else {
+ if( allColumns.length > 2 ){
+ var uniqueCol = _.find( allColumns, function( column, i ){
+ return i !== newConfig.xColumn && i !== newConfig.yColumn;
+ });
+ newConfig.idColumn = uniqueCol.index;
+ }
+ }
+ config = this.model.set( 'config', newConfig, { silent: true }).get( 'config' );
+
+ $dataControl.find( '[name="xColumn"]' ).val( config.xColumn ).on( 'change', function(){
+ editor.model.set( 'config', { xColumn: Number( $( this ).val() ) });
+ });
+ $dataControl.find( '[name="yColumn"]' ).val( config.yColumn ).on( 'change', function(){
+ editor.model.set( 'config', { yColumn: Number( $( this ).val() ) });
+ });
+ $dataControl.find( 'select[name="idColumn"]' ).val( config.idColumn ).on( 'change', function(){
+ editor.model.set( 'config', { idColumn: Number( $( this ).val() ) });
+ });
+ if( config.idColumn !== undefined ){
$dataControl.find( '#include-id-checkbox' ).prop( 'checked', true ).trigger( 'change' );
- $dataControl.find( 'select[name="idColumn"]' ).val( this.plotView.config.idColumn );
}
+ $dataControl.find( '[title]' ).tooltip();
return $dataControl;
},
- _render_chartControls : function( $chartControls ){
- // tab content to control how the chart is rendered (data glyph size, chart size, etc.)
- $chartControls.html( ScatterplotConfigEditor.templates.chartControl( this.plotView.config ) );
+ /** tab content to control how the chart is rendered (data glyph size, chart size, etc.) */
+ _render_chartControls : function( $where ){
+ $where = $where || this.$el;
+ var editor = this,
+ config = this.model.get( 'config' ),
+ $chartControls = $where.find( '#chart-control' );
+
+ // ---- skeleton/form for controls
+ $chartControls.html( ScatterplotConfigEditor.templates.chartControl( config ) );
//console.debug( '$chartControl:', $chartControls );
- // set up behaviours, js on sliders
- //console.debug( 'numeric sliders:', $chartControls.find( '.numeric-slider-input' ) );
- // what to do when the slider changes: update display and update chartConfig
- var view = this,
- // limits for controls (by control/chartConfig id)
- //TODO: move into TwoVarScatterplot
- controlRanges = {
+ // ---- slider controls
+ // limits for controls (by control/chartConfig id)
+ //TODO: as class attribute
+ var controlRanges = {
'datapointSize' : { min: 2, max: 10, step: 1 },
'width' : { min: 200, max: 800, step: 20 },
'height' : { min: 200, max: 800, step: 20 }
};
function onSliderChange(){
- var $this = $( this );
- $this.siblings( '.slider-output' ).text( $this.slider( 'value' ) );
+ // set the model config when changed and update the slider output text
+ var $this = $( this ),
+ //note: returns a number nicely enough
+ 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 );
}
+
+ //console.debug( 'numeric sliders:', $chartControls.find( '.numeric-slider-input' ) );
$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 : view.plotView.config[ 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 ] );
});
-//TODO: to more common area (like render)?
+
+ // ---- 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';
// set label inputs to current x, y metadata_column_names (if any)
- if( this.dataset.metadata_column_names ){
- //var colNames = this.dataset.metadata_column_names;
- //$chartControls.find( 'input[name="X-axis-label"]' ).val( colNames );
- //$chartControls.find( 'input[name="Y-axis-label"]' ).val( colNames );
-//TODO: on change of x, y data controls
- }
+ $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() });
+ });
//console.debug( '$chartControls:', $chartControls );
+ $chartControls.find( '[title]' ).tooltip();
return $chartControls;
},
- _render_chartDisplay : function(){
- // render the tab content where the chart is displayed (but not the chart itself)
- var $chartDisplay = this.$el.find( '.tab-pane#chart-display' );
- this.plotView.setElement( $chartDisplay );
- this.plotView.render();
+ /** render the tab content where the chart is displayed (but not the chart itself) */
+ _render_chartDisplay : function( $where ){
+ $where = $where || this.$el;
+ var $chartDisplay = $where.find( '.tab-pane#chart-display' );
+ this.display.setElement( $chartDisplay );
+ this.display.render();
+
+ $chartDisplay.find( '[title]' ).tooltip();
return $chartDisplay;
},
@@ -179,7 +229,22 @@
events : {
'change #include-id-checkbox' : 'toggleThirdColumnSelector',
'click #data-control .render-button' : 'renderChart',
- 'click #chart-control .render-button' : 'renderChart'
+ 'click #chart-control .render-button' : 'renderChart',
+ 'click .save-btn' : 'saveVisualization',
+ //'click .copy-btn' : function(e){ this.model.save(); }
+ },
+
+ 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 );
+ })
+ .then( function(){
+ editor.render();
+ });
},
toggleThirdColumnSelector : function(){
@@ -192,42 +257,9 @@
//console.log( this + '.renderChart' );
// fetch the data, (re-)render the chart
this.$el.find( '.nav li.disabled' ).removeClass( 'disabled' );
- this.updateConfigWithDataSettings();
- this.updateConfigWithChartSettings();
this.$el.find( 'ul.nav' ).find( 'a[href="#chart-display"]' ).tab( 'show' );
- this.plotView.fetchData();
- //console.debug( this.plotView.$el );
- },
-
- // ------------------------------------------------------------------------- GET DATA/CHART SETTINGS
- updateConfigWithDataSettings : function(){
- // parse the column values for both indeces (for the data fetch) and names (for the chart)
- var $dataControls = this.$el.find( '#data-control' );
- var settings = {
- xColumn : Number( $dataControls.find( '[name="xColumn"]' ).val() ),
- yColumn : Number( $dataControls.find( '[name="yColumn"]' ).val() )
- };
- if( $dataControls.find( '#include-id-checkbox' ).prop( 'checked' ) ){
- settings.idColumn = $dataControls.find( '[name="idColumn"]' ).val();
- }
- //console.log( '\t data settings:', settings );
- return _.extend( this.plotView.config, settings );
- },
-
- updateConfigWithChartSettings : function(){
- // gets the user-selected chartConfig from the chart settings panel
- var plotView = this.plotView,
- $chartControls = this.$el.find( '#chart-control' );
- // use a loop of config keys to get the form values for these sliders
- [ 'datapointSize', 'width', 'height' ].forEach( function( v, i ){
- plotView.config[ v ] = $chartControls.find( '.numeric-slider-input[data-config-key="' + v + '"]' )
- .find( '.slider' ).slider( 'value' );
- });
- // update axes labels using chartSettings inputs (if not at defaults), otherwise the selects' colName
- plotView.config.x.label = $chartControls.find( 'input[name="X-axis-label"]' ).val();
- plotView.config.y.label = $chartControls.find( 'input[name="Y-axis-label"]' ).val();
- //console.log( '\t chartSettings:', settings );
- return plotView.config;
+ this.display.fetchData();
+ //console.debug( this.display.$el );
},
toString : function(){
diff -r a11c52513e4472237c9070a06ff11f123af83c06 -r a253d56a0dd6be3816581a0c7ed0ada191a1693b config/plugins/visualizations/scatterplot/src/scatterplot-display.js
--- a/config/plugins/visualizations/scatterplot/src/scatterplot-display.js
+++ b/config/plugins/visualizations/scatterplot/src/scatterplot-display.js
@@ -6,242 +6,242 @@
* computing and displaying data stats
* controls for pagination of data (if needed)
*/
-var ScatterplotView = Backbone.View.extend({
- //TODO: should be a view on visualization(revision) model
+var ScatterplotDisplay = Backbone.View.extend({
- defaults : {
- metadata : {
- dataLines : undefined
- },
-
- pagination : {
- currPage : 0,
- perPage : 3000
- },
-
- width : 400,
- height : 400,
-
- margin : {
- top : 16,
- right : 16,
- bottom : 40,
- left : 54
- },
-
- x : {
- ticks : 10,
- label : 'X'
- },
- y : {
- ticks : 10,
- label : 'Y'
- },
-
- datapointSize : 4,
- animDuration : 500
+ initialize : function( attributes ){
+ this.data = null,
+ this.dataset = attributes.dataset;
+ this.calcNumPages();
},
- initialize : function( attributes ){
- this.config = _.extend( _.clone( this.defaults ), attributes.config || {});
- this.dataset = attributes.dataset;
- //console.debug( this + '.config:', this.config );
- },
-
- updateConfig : function( newConfig ){
- //console.log( this + '.updateConfig:', newConfig );
- this.config = this.config || {};
- //TODO: validate here
- _.extend( this.config, newConfig );
- //TODO: implement rerender flag
+ 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' );
+ }
},
fetchData : function(){
//TODO: doesn't work bc it's rendered in render()...
this.showLoadingIndicator( 'getting data' );
//console.debug( 'currPage', this.config.pagination.currPage );
- var view = this;
+ var view = this,
+ config = this.model.get( 'config' ),
//TODO: very tied to datasets - should be generalized eventually
xhr = jQuery.getJSON( '/api/datasets/' + this.dataset.id, {
data_type : 'raw_data',
provider : 'dataset-column',
- limit : this.config.pagination.perPage,
- offset : ( this.config.pagination.currPage * this.config.pagination.perPage )
+ limit : config.pagination.perPage,
+ offset : ( config.pagination.currPage * config.pagination.perPage )
});
xhr.done( function( data ){
- view.renderData( data.data );
+ view.data = data.data;
+ 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 );
- console.error( xhr, status, message );
- });
- xhr.always( function(){
- view.hideLoadingIndicator();
});
return xhr;
},
- render : function( data ){
- this.$el.addClass( 'scatterplot-display' ).html([
- '<div class="controls clear"></div>',
+ showLoadingIndicator : function(){
+ // display the loading indicator over the tab panels if hidden, update message (if passed)
+ this.$el.find( '.scatterplot-data-info' ).html([
'<div class="loading-indicator">',
'<span class="fa fa-spinner fa-spin"></span>',
- '<span class="loading-indicator-message"></span>',
+ '<span class="loading-indicator-message">loading...</span>',
+ '</div>'
+ ].join( '' ));
+ },
+
+ template : function(){
+ var html = [
+ '<div class="controls clear">',
+ '<div class="left">',
+ '</div>',
+ '<div class="right">',
+ '<p class="scatterplot-data-info"></p>',
+ '<button class="stats-toggle-btn">Stats</button>',
+ '<button class="rerender-btn">Redraw</button>',
+ '</div>',
'</div>',
'<svg/>', //TODO: id
'<div class="stats-display"></div>'
- ].join( '' ));
- this.$el.children().hide();
+ ].join( '' );
+ return html;
+ },
- if( data ){
- this.renderData( data );
+ render : function(){
+ this.$el.addClass( 'scatterplot-display' ).html( this.template() );
+ if( this.data ){
+ this.renderData();
}
return this;
},
- showLoadingIndicator : function( message, speed ){
- // display the loading indicator over the tab panels if hidden, update message (if passed)
-//TODO: move loading indicator into data-info-text
- message = message || '';
- speed = speed || 'fast';
- var $indicator = this.$el.find( '.loading-indicator' );
-
- if( message ){ $indicator.find( '.loading-indicator-message' ).text( message ); }
- if( !$indicator.is( ':visible' ) ){
- this.toggleStats( false );
- $indicator.css({ left: ( this.config.width / 2 ), top: this.config.height / 2 }).show();
- }
+ renderData : function(){
+ this.renderLeftControls();
+ this.renderRightControls();
+ this.renderPlot( this.data );
+ this.getStats();
},
- hideLoadingIndicator : function( speed ){
- speed = speed || 'fast';
- this.$el.find( '.loading-indicator' ).hide();
+ renderLeftControls : function(){
+ if( this.lineCount ){
+ this.$el.find( '.controls .left' ).empty().append( this.renderPagination() );
+ } else {
+ this.$el.find( '.controls .left' ).empty().append( this.renderPrevNext() );
+ }
+ return this;
},
- renderData : function( data ){
- this.$el.find( '.controls' ).empty().append( this.renderControls( data ) ).show();
- this.renderPlot( data );
- this.getStats( data );
+ renderRightControls : function(){
+ var view = this;
+ this.setLineInfo( this.data );
+ // clear prev. handlers due to closure around data
+ this.$el.find( '.stats-toggle-btn' )
+ .off().click( function(){
+ view.toggleStats();
+ });
+ this.$el.find( '.rerender-btn' )
+ .off().click( function(){
+ view.resetZoom();
+ view.renderPlot( this.data );
+ });
},
- renderControls : function( data ){
- var view = this;
- var $left = $( '<div class="left"></div>' ),
- $right = $( '<div class="right"></div>' );
-
- $left.append([
- this.renderPrevNext( data ),
- this.renderPagination( data )
- ]);
- $right.append([
- this.renderLineInfo( data ),
- $( '<button>Stats</button>' ).addClass( 'stats-toggle-btn' )
- .click( function(){
- view.toggleStats();
- }),
- $( '<button>Redraw</button>' ).addClass( 'rerender-btn' )
- .click( function(){
- view.renderPlot( data );
- })
- ]);
- return [ $left, $right ];
+ /** render and show the d3 plot into the svg node of the view */
+ renderPlot : function(){
+ var view = this,
+ $svg = this.$el.find( 'svg' );
+ // turn off stats, clear previous svg, and make it visible
+ this.toggleStats( false );
+ $svg.off().empty().show()
+ // set up listeners for events from plot
+ .on( 'zoom.scatterplot', function( ev, zoom ){
+ //TODO: possibly throttle this
+ //console.debug( 'zoom.scatterplot', zoom.scale, zoom.translate );
+ 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 );
},
- renderLineInfo : function( data ){
- var totalLines = this.dataset.metadata_data_lines || 'an unknown number of',
- lineStart = ( this.config.pagination.currPage * this.config.pagination.perPage ),
- lineEnd = lineStart + data.length;
- return $( '<p/>' ).addClass( 'scatterplot-data-info' )
- .text([ 'Displaying lines', lineStart + 1, 'to', lineEnd, 'of', totalLines, 'lines' ].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 || '' );
+ }
+
+ return this;
},
- renderPrevNext : function( data ){
- // this is cra-zazy
- if( !data
- || ( this.config.pagination.currPage === 0 && data.length < this.config.pagination.perPage ) ){ return null; }
+ 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;
+ },
- function makePage$Li( text ){
- return $([ '<li><a href="javascript:void(0);">', text, '</a></li>' ].join( '' ));
- }
-//TODO: cache numPages/numLines in config
+ // ------------------------------------------------------------------------ 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,
- dataLines = this.dataset.metadata_data_lines,
- numPages = ( dataLines )?( Math.ceil( dataLines / this.config.pagination.perPage ) ):( undefined );
- //console.debug( 'data:', this.dataset.metadata_data_lines, 'numPages:', numPages );
+ $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(); });
- // prev next buttons
- var $prev = makePage$Li( 'Prev' ).click( function(){
- if( view.config.pagination.currPage > 0 ){
- view.config.pagination.currPage -= 1;
- view.fetchData();
- }
- }),
- $next = makePage$Li( 'Next' ).click( function(){
- if( !numPages || view.config.pagination.currPage < ( numPages - 1 ) ){
- view.config.pagination.currPage += 1;
- view.fetchData();
- }
- }),
- $prevNextList = $( '<ul/>' ).addClass( 'pagination data-prev-next' )
- .append([ $prev, $next ]);
-
- if( view.config.pagination.currPage === 0 ){
+ // disable if it either end
+ if( config.pagination.currPage === 0 ){
$prev.addClass( 'disabled' );
}
- if( numPages && view.config.pagination.currPage === ( numPages - 1 ) ){
+ if( this.numPages && config.pagination.currPage === ( this.numPages - 1 ) ){
$next.addClass( 'disabled' );
}
- return $prevNextList;
+ return $( '<ul/>' ).addClass( 'pagination data-prev-next' ).append([ $prev, $next ]);
},
- renderPagination : function( data ){
- // this is cra-zazy
- if( !data
- || ( this.config.pagination.currPage === 0 && data.length < this.config.pagination.perPage ) ){ return null; }
+ /** 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; }
- function makePage$Li( text ){
- return $([ '<li><a href="javascript:void(0);">', text, '</a></li>' ].join( '' ));
- }
-//TODO: cache numPages/numLines in config
var view = this,
- dataLines = this.dataset.metadata_data_lines,
- numPages = ( dataLines )?( Math.ceil( dataLines / this.config.pagination.perPage ) ):( undefined );
- //console.debug( 'data:', this.dataset.metadata_data_lines, 'numPages:', numPages );
+ $pagesList = $( '<ul/>' ).addClass( 'pagination data-pages' );
+ pageNumClick = function( ev ){
+ view.goToPage( $( this ).data( 'page' ) );
+ };
- // page numbers (as separate control)
- //var $paginationContainer = $( '<div/>' ).addClass( 'pagination-container' ),
- var $pagesList = $( '<ul/>' ).addClass( 'pagination data-pages' );
- function pageNumClick( ev ){
- view.config.pagination.currPage = $( this ).data( 'page' );
- view.fetchData();
- }
- for( var i=0; i<numPages; i+=1 ){
- // add page data for later event handling
- var $pageLi = makePage$Li( i + 1 ).attr( 'data-page', i ).click( pageNumClick );
- if( i === this.config.pagination.currPage ){
+ 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 );
}
- // placing the pages list in an extra container allows us to set a max-width and scroll if overflow
- //$paginationContainer.append( $pagesList );
- //return $paginationContainer;
return $pagesList;
},
- renderPlot : function( data ){
- this.toggleStats( false );
- var $svg = this.$el.find( 'svg' );
- $svg.off().empty().show();
- scatterplot( $svg.get( 0 ), this.config, data );
- },
-
- getStats : function( data ){
- var view = this;
+ // ------------------------------------------------------------------------ statistics display
+ /** create a webworker to calc stats for data given */
+ getStats : function(){
+ if( !this.data ){ return; }
+ var view = this,
+ config = this.model.get( 'config' ),
meanWorker = new Worker( '/plugins/visualizations/scatterplot/static/worker-stats.js' );
meanWorker.postMessage({
- data : data,
- keys : [ this.config.xColumn, this.config.yColumn ]
+ data : this.data,
+ keys : [ config.xColumn, config.yColumn ]
});
meanWorker.onerror = function( event ){
meanWorker.terminate();
@@ -254,9 +254,9 @@
renderStats : function( stats, error ){
//console.debug( 'renderStats:', stats, error );
//console.debug( JSON.stringify( stats, null, ' ' ) );
- var $statsTable = this.$el.find( '.stats-display' );
-
- var xLabel = this.config.x.label, yLabel = this.config.y.label,
+ var config = this.model.get( 'config' ),
+ $statsTable = this.$el.find( '.stats-display' ),
+ xLabel = config.x.label, yLabel = config.y.label,
$table = $( '<table/>' ).addClass( 'table' )
.append([ '<thead><th></th><th>', xLabel, '</th><th>', yLabel, '</th></thead>' ].join( '' ))
.append( _.map( stats, function( stat, key ){
diff -r a11c52513e4472237c9070a06ff11f123af83c06 -r a253d56a0dd6be3816581a0c7ed0ada191a1693b config/plugins/visualizations/scatterplot/src/scatterplot.js
--- a/config/plugins/visualizations/scatterplot/src/scatterplot.js
+++ b/config/plugins/visualizations/scatterplot/src/scatterplot.js
@@ -46,8 +46,9 @@
var zoom = d3.behavior.zoom()
.x( interpolaterFns.x )
.y( interpolaterFns.y )
- .scaleExtent([ 1, 10 ]);
-//TODO: you can prog. set the zoom and pan with zoom.scale( val ) and zoom.translate([ x, y ])...
+ .scaleExtent([ 1, 30 ])
+ .scale( config.scale || 1 )
+ .translate( config.translate || [ 0, 0 ] );
//console.debug( renderTo );
var svg = d3.select( renderTo )
@@ -99,7 +100,7 @@
//console.log( 'axis.y.g:', axis.y.g );
// ................................ axis labels
- var padding = 4;
+ var padding = 6;
// x-axis label
axis.x.label = svg.append( 'text' )
.attr( 'class', 'axis-label' )
@@ -176,18 +177,17 @@
.enter().append( 'svg:circle' )
.classed( "glyph", true )
.attr( "cx", function( d, i ){ return interpolaterFns.x( getX( d, i ) ); })
- // give them a 'entry' position and style
- .attr( "cy", config.height )
+ .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( "cy", function( d, i ){ return interpolaterFns.y( getY( d, i ) ); })
.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' )
@@ -200,29 +200,34 @@
return false;
}).style( 'display', 'none' );
}
+ _redrawDatapointsClipped();
// .................................................................... behaviors
function zoomed( scale, translateX, translateY ){
- //console.debug( 'zoom', this, scale, translateX, translateY, arguments );
+ //console.debug( 'zoom', this, zoom.scale(), zoom.translate() );
+
// re-render axis, grid, and datapoints
+ $( '.chart-info-box' ).remove();
axis.redraw();
_redrawDatapointsClipped();
grid = renderGrid();
- $( '.chart-info-box' ).remove();
- $( svg.node() ).trigger( 'zoom.scatterplot', [] );
+
+ $( 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 );
-
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 $([
'<div class="chart-info-box" style="position: absolute">',
- (( config.idColumn )?( '<div>' + d[ config.idColumn ] + '</div>' ):( '' )),
+ (( config.idColumn !== undefined )?( '<div>' + d[ config.idColumn ] + '</div>' ):( '' )),
'<div>', getX( d ), '</div>',
'<div>', getY( d ), '</div>',
'</div>'
diff -r a11c52513e4472237c9070a06ff11f123af83c06 -r a253d56a0dd6be3816581a0c7ed0ada191a1693b config/plugins/visualizations/scatterplot/static/scatterplot-edit.js
--- a/config/plugins/visualizations/scatterplot/static/scatterplot-edit.js
+++ b/config/plugins/visualizations/scatterplot/static/scatterplot-edit.js
@@ -1,1 +1,1 @@
-function scatterplot(a,b,c){function d(){var a={v:{},h:{}};return a.v.lines=p.selectAll("line.v-grid-line").data(m.x.ticks(q.x.fn.ticks()[0])),a.v.lines.enter().append("svg:line").classed("grid-line v-grid-line",!0),a.v.lines.attr("x1",m.x).attr("x2",m.x).attr("y1",0).attr("y2",b.height),a.v.lines.exit().remove(),a.h.lines=p.selectAll("line.h-grid-line").data(m.y.ticks(q.y.fn.ticks()[0])),a.h.lines.enter().append("svg:line").classed("grid-line h-grid-line",!0),a.h.lines.attr("x1",0).attr("x2",b.width).attr("y1",m.y).attr("y2",m.y),a.h.lines.exit().remove(),a}function e(){return t.attr("cx",function(a,b){return m.x(j(a,b))}).attr("cy",function(a,b){return m.y(k(a,b))}).style("display","block").filter(function(){var a=d3.select(this).attr("cx"),c=d3.select(this).attr("cy");return 0>a||a>b.width?!0:0>c||c>b.height?!0:!1}).style("display","none")}function f(){q.redraw(),e(),s=d(),$(".chart-info-box").remove(),$(o.node()).trigger("zoom.scatterplot",[])}function g(a,c,d){return c+=8,$(['<div class="chart-info-box" style="position: absolute">',b.idColumn?"<div>"+d[b.idColumn]+"</div>":"","<div>",j(d),"</div>","<div>",k(d),"</div>","</div>"].join("")).css({top:a,left:c,"z-index":2})}var h=function(a,b){return"translate("+a+","+b+")"},i=function(a,b,c){return"rotate("+a+","+b+","+c+")"},j=function(a){return a[b.xColumn]},k=function(a){return a[b.yColumn]},l={x:{extent:d3.extent(c,j)},y:{extent:d3.extent(c,k)}},m={x:d3.scale.linear().domain(l.x.extent).range([0,b.width]),y:d3.scale.linear().domain(l.y.extent).range([b.height,0])},n=d3.behavior.zoom().x(m.x).y(m.y).scaleExtent([1,10]),o=d3.select(a).attr("class","scatterplot").attr("width","100%").attr("height",b.height+(b.margin.top+b.margin.bottom)),p=o.append("g").attr("class","content").attr("transform",h(b.margin.left,b.margin.top)).call(n);p.append("rect").attr("class","zoom-rect").attr("width",b.width).attr("height",b.height).style("fill","transparent");var q={x:{},y:{}};q.x.fn=d3.svg.axis().orient("bottom").scale(m.x).ticks(b.x.ticks).tickFormat(d3.format("s")),q.y.fn=d3.svg.axis().orient("left").scale(m.y).ticks(b.y.ticks).tickFormat(d3.format("s")),q.x.g=p.append("g").attr("class","x axis").attr("transform",h(0,b.height)).call(q.x.fn),q.y.g=p.append("g").attr("class","y axis").call(q.y.fn);var r=4;q.x.label=o.append("text").attr("class","axis-label").text(b.x.label).attr("text-anchor","middle").attr("dominant-baseline","text-after-edge").attr("x",b.width/2+b.margin.left).attr("y",b.height+b.margin.bottom+b.margin.top-r),q.y.label=o.append("text").attr("class","axis-label").text(b.y.label).attr("text-anchor","middle").attr("dominant-baseline","text-before-edge").attr("x",r).attr("y",b.height/2).attr("transform",i(-90,r,b.height/2)),q.redraw=function(){o.select(".x.axis").call(q.x.fn),o.select(".y.axis").call(q.y.fn)};var s=d(),t=p.selectAll(".glyph").data(c).enter().append("svg:circle").classed("glyph",!0).attr("cx",function(a,b){return m.x(j(a,b))}).attr("cy",b.height).attr("r",0);t.transition().duration(b.animDuration).attr("cy",function(a,b){return m.y(k(a,b))}).attr("r",b.datapointSize),n.on("zoom",f),t.on("mouseover",function(a,c){var d=d3.select(this);d.style("fill","red").style("fill-opacity",1),p.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",d.attr("cx")-b.datapointSize).attr("y1",d.attr("cy")).attr("x2",0).attr("y2",d.attr("cy")).classed("hoverline",!0),d.attr("cy")<b.height&&p.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",d.attr("cx")).attr("y1",+d.attr("cy")+b.datapointSize).attr("x2",d.attr("cx")).attr("y2",b.height).classed("hoverline",!0);var e=this.getBoundingClientRect();$("body").append(g(e.top,e.right,a)),$(o.node()).trigger("mouseover-datapoint.scatterplot",[this,a,c])}),t.on("mouseout",function(){d3.select(this).style("fill","black").style("fill-opacity",.2),p.selectAll(".hoverline").remove(),$(".chart-info-box").remove()})}this.scatterplot=this.scatterplot||{},this.scatterplot.chartcontrol=Handlebars.template(function(a,b,c,d,e){this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var f,g="",h="function",i=this.escapeExpression;return g+='<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 data-config-key="datapointSize" class="form-input numeric-slider-input">\n <label for="datapointSize">Size of data point: </label>\n <div class="slider-output">',(f=c.datapointSize)?f=f.call(b,{hash:{},data:e}):(f=b.datapointSize,f=typeof f===h?f.apply(b):f),g+=i(f)+'</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 data-config-key="width" class="form-input numeric-slider-input">\n <label for="width">Chart width: </label>\n <div class="slider-output">',(f=c.width)?f=f.call(b,{hash:{},data:e}):(f=b.width,f=typeof f===h?f.apply(b):f),g+=i(f)+'</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 data-config-key="height" class="form-input numeric-slider-input">\n <label for="height">Chart height: </label>\n <div class="slider-output">',(f=c.height)?f=f.call(b,{hash:{},data:e}):(f=b.height,f=typeof f===h?f.apply(b):f),g+=i(f)+'</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 data-config-key="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="'+i((f=b.x,f=null==f||f===!1?f:f.label,typeof f===h?f.apply(b):f))+'" />\n <p class="form-help help-text-small"></p>\n</div>\n\n<div data-config-key="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="'+i((f=b.y,f=null==f||f===!1?f:f.label,typeof f===h?f.apply(b):f))+'" />\n <p class="form-help help-text-small"></p>\n</div>\n\n<button class="render-button btn btn-primary active">Draw</button>\n'}),this.scatterplot.datacontrol=Handlebars.template(function(a,b,c,d,e){function f(a,b){var d,e="";return e+='\n <option value="',(d=c.index)?d=d.call(a,{hash:{},data:b}):(d=a.index,d=typeof d===j?d.apply(a):d),e+=k(d)+'">',(d=c.name)?d=d.call(a,{hash:{},data:b}):(d=a.name,d=typeof d===j?d.apply(a):d),e+=k(d)+"</option>\n "}function g(){return'checked="true"'}this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var h,i="",j="function",k=this.escapeExpression,l=this;return 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\n<div class="column-select">\n <label>Data column for X: </label>\n <select name="xColumn">\n ',h=c.each.call(b,b.numericColumns,{hash:{},inverse:l.noop,fn:l.program(1,f,e),data:e}),(h||0===h)&&(i+=h),i+='\n </select>\n</div>\n<div class="column-select">\n <label>Data column for Y: </label>\n <select name="yColumn">\n ',h=c.each.call(b,b.numericColumns,{hash:{},inverse:l.noop,fn:l.program(1,f,e),data:e}),(h||0===h)&&(i+=h),i+='\n </select>\n</div>\n\n\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="idColumn">\n ',h=c.each.call(b,b.allColumns,{hash:{},inverse:l.noop,fn:l.program(1,f,e),data:e}),(h||0===h)&&(i+=h),i+='\n </select>\n</div>\n\n\n<div id="first-line-header" style="display: none;">\n <p>Possible headers: ',(h=c.possibleHeaders)?h=h.call(b,{hash:{},data:e}):(h=b.possibleHeaders,h=typeof h===j?h.apply(b):h),i+=k(h)+'\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 ',h=c["if"].call(b,b.usePossibleHeaders,{hash:{},inverse:l.noop,fn:l.program(3,g,e),data:e}),(h||0===h)&&(i+=h),i+='/>\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<button class="render-button btn btn-primary active">Draw</button>\n'}),this.scatterplot.editor=Handlebars.template(function(a,b,c,d,e){this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var f="";return f+='<div class="scatterplot-editor tabbable tabs-left">\n \n <ul class="nav nav-tabs">\n \n <li class="active">\n <a title="Use this tab to change which data are used"\n href="#data-control" data-toggle="tab">Data Controls</a>\n </li>\n <li>\n <a title="Use this tab to change how the chart is drawn"\n href="#chart-control" data-toggle="tab" >Chart Controls</a>\n </li>\n \n <li class="disabled">\n <a title="This tab will display the chart"\n href="#chart-display" data-toggle="tab">Chart</a>\n </li>\n </ul>\n\n \n <div class="tab-content">\n \n <div id="data-control" class="scatterplot-config-control tab-pane active">\n \n </div>\n \n \n <div id="chart-control" class="scatterplot-config-control tab-pane">\n \n </div>\n\n \n <div id="chart-display" class="scatterplot-display tab-pane"></div>\n\n </div>\n</div>\n'});var ScatterplotConfigEditor=Backbone.View.extend(LoggableMixin).extend({className:"scatterplot-control-form",initialize:function(a){if(this.model||(this.model=new Visualization({type:"scatterplot"})),console.log(this+".initialize, attributes:",a),!a||!a.dataset)throw new Error("ScatterplotConfigEditor requires a dataset");this.dataset=a.dataset,console.log("dataset:",this.dataset),this.plotView=new ScatterplotView({dataset:a.dataset,model:this.model})},render:function(){return this.$el.append(ScatterplotConfigEditor.templates.mainLayout({})),this.$el.find("#data-control").append(this._render_dataControl()),this._render_chartControls(this.$el.find("#chart-control")),this._render_chartDisplay(),this.$el.find("[title]").tooltip(),this},_render_dataControl:function(){var a=this.dataset,b=_.map(a.metadata_column_types,function(b,c){var d={index:c,type:b,name:"column "+(c+1)};return a.metadata_column_names&&a.metadata_column_names[c]&&(d.name=a.metadata_column_names[c]),d}),c=_.filter(b,function(a){return"int"===a.type||"float"===a.type});2>c&&(c=b);var d=this.$el.find(".tab-pane#data-control");return d.html(ScatterplotConfigEditor.templates.dataControl({allColumns:b,numericColumns:c})),d.find('[name="xColumn"]').val(this.plotView.config.xColumn||c[0].index),d.find('[name="yColumn"]').val(this.plotView.config.yColumn||c[1].index),void 0!==this.plotView.config.idColumn&&(d.find("#include-id-checkbox").prop("checked",!0).trigger("change"),d.find('select[name="idColumn"]').val(this.plotView.config.idColumn)),d},_render_chartControls:function(a){function b(){var a=$(this);a.siblings(".slider-output").text(a.slider("value"))}a.html(ScatterplotConfigEditor.templates.chartControl(this.plotView.config));var c=this,d={datapointSize:{min:2,max:10,step:1},width:{min:200,max:800,step:20},height:{min:200,max:800,step:20}};return a.find(".numeric-slider-input").each(function(){var a=$(this),e=a.attr("data-config-key"),f=_.extend(d[e],{value:c.plotView.config[e],change:b,slide:b});a.find(".slider").slider(f)}),this.dataset.metadata_column_names,a},_render_chartDisplay:function(){var a=this.$el.find(".tab-pane#chart-display");return this.plotView.setElement(a),this.plotView.render(),a},events:{"change #include-id-checkbox":"toggleThirdColumnSelector","click #data-control .render-button":"renderChart","click #chart-control .render-button":"renderChart"},toggleThirdColumnSelector:function(){this.$el.find('select[name="idColumn"]').parent().toggle()},renderChart:function(){this.$el.find(".nav li.disabled").removeClass("disabled"),this.updateConfigWithDataSettings(),this.updateConfigWithChartSettings(),this.$el.find("ul.nav").find('a[href="#chart-display"]').tab("show"),this.plotView.fetchData()},updateConfigWithDataSettings:function(){var a=this.$el.find("#data-control"),b={xColumn:Number(a.find('[name="xColumn"]').val()),yColumn:Number(a.find('[name="yColumn"]').val())};return a.find("#include-id-checkbox").prop("checked")&&(b.idColumn=a.find('[name="idColumn"]').val()),_.extend(this.plotView.config,b)},updateConfigWithChartSettings:function(){var a=this.plotView,b=this.$el.find("#chart-control");return["datapointSize","width","height"].forEach(function(c){a.config[c]=b.find('.numeric-slider-input[data-config-key="'+c+'"]').find(".slider").slider("value")}),a.config.x.label=b.find('input[name="X-axis-label"]').val(),a.config.y.label=b.find('input[name="Y-axis-label"]').val(),a.config},toString:function(){return"ScatterplotConfigEditor("+(this.dataset?this.dataset.id:"")+")"}});ScatterplotConfigEditor.templates={mainLayout:scatterplot.editor,dataControl:scatterplot.datacontrol,chartControl:scatterplot.chartcontrol};var ScatterplotView=Backbone.View.extend({defaults:{metadata:{dataLines:void 0},pagination:{currPage:0,perPage:3e3},width:400,height:400,margin:{top:16,right:16,bottom:40,left:54},x:{ticks:10,label:"X"},y:{ticks:10,label:"Y"},datapointSize:4,animDuration:500},initialize:function(a){this.config=_.extend(_.clone(this.defaults),a.config||{}),this.dataset=a.dataset},updateConfig:function(a){this.config=this.config||{},_.extend(this.config,a)},fetchData:function(){this.showLoadingIndicator("getting data");var a=this;return xhr=jQuery.getJSON("/api/datasets/"+this.dataset.id,{data_type:"raw_data",provider:"dataset-column",limit:this.config.pagination.perPage,offset:this.config.pagination.currPage*this.config.pagination.perPage}),xhr.done(function(b){a.renderData(b.data)}),xhr.fail(function(a,b,c){alert("Error loading data:\n"+a.responseText),console.error(a,b,c)}),xhr.always(function(){a.hideLoadingIndicator()}),xhr},render:function(a){return this.$el.addClass("scatterplot-display").html(['<div class="controls clear"></div>','<div class="loading-indicator">','<span class="fa fa-spinner fa-spin"></span>','<span class="loading-indicator-message"></span>',"</div>","<svg/>",'<div class="stats-display"></div>'].join("")),this.$el.children().hide(),a&&this.renderData(a),this},showLoadingIndicator:function(a,b){a=a||"",b=b||"fast";var c=this.$el.find(".loading-indicator");a&&c.find(".loading-indicator-message").text(a),c.is(":visible")||(this.toggleStats(!1),c.css({left:this.config.width/2,top:this.config.height/2}).show())},hideLoadingIndicator:function(a){a=a||"fast",this.$el.find(".loading-indicator").hide()},renderData:function(a){this.$el.find(".controls").empty().append(this.renderControls(a)).show(),this.renderPlot(a),this.getStats(a)},renderControls:function(a){var b=this,c=$('<div class="left"></div>'),d=$('<div class="right"></div>');return c.append([this.renderPrevNext(a),this.renderPagination(a)]),d.append([this.renderLineInfo(a),$("<button>Stats</button>").addClass("stats-toggle-btn").click(function(){b.toggleStats()}),$("<button>Redraw</button>").addClass("rerender-btn").click(function(){b.renderPlot(a)})]),[c,d]},renderLineInfo:function(a){var b=this.dataset.metadata_data_lines||"an unknown number of",c=this.config.pagination.currPage*this.config.pagination.perPage,d=c+a.length;return $("<p/>").addClass("scatterplot-data-info").text(["Displaying lines",c+1,"to",d,"of",b,"lines"].join(" "))},renderPrevNext:function(a){function b(a){return $(['<li><a href="javascript:void(0);">',a,"</a></li>"].join(""))}if(!a||0===this.config.pagination.currPage&&a.length<this.config.pagination.perPage)return null;var c=this,d=this.dataset.metadata_data_lines,e=d?Math.ceil(d/this.config.pagination.perPage):void 0,f=b("Prev").click(function(){c.config.pagination.currPage>0&&(c.config.pagination.currPage-=1,c.fetchData())}),g=b("Next").click(function(){(!e||c.config.pagination.currPage<e-1)&&(c.config.pagination.currPage+=1,c.fetchData())}),h=$("<ul/>").addClass("pagination data-prev-next").append([f,g]);return 0===c.config.pagination.currPage&&f.addClass("disabled"),e&&c.config.pagination.currPage===e-1&&g.addClass("disabled"),h},renderPagination:function(a){function b(a){return $(['<li><a href="javascript:void(0);">',a,"</a></li>"].join(""))}function c(){d.config.pagination.currPage=$(this).data("page"),d.fetchData()}if(!a||0===this.config.pagination.currPage&&a.length<this.config.pagination.perPage)return null;for(var d=this,e=this.dataset.metadata_data_lines,f=e?Math.ceil(e/this.config.pagination.perPage):void 0,g=$("<ul/>").addClass("pagination data-pages"),h=0;f>h;h+=1){var i=b(h+1).attr("data-page",h).click(c);h===this.config.pagination.currPage&&i.addClass("active"),g.append(i)}return g},renderPlot:function(a){this.toggleStats(!1);var b=this.$el.find("svg");b.off().empty().show(),scatterplot(b.get(0),this.config,a)},getStats:function(a){var b=this;meanWorker=new Worker("/plugins/visualizations/scatterplot/static/worker-stats.js"),meanWorker.postMessage({data:a,keys:[this.config.xColumn,this.config.yColumn]}),meanWorker.onerror=function(){meanWorker.terminate()},meanWorker.onmessage=function(a){b.renderStats(a.data)}},renderStats:function(a){var b=this.$el.find(".stats-display"),c=this.config.x.label,d=this.config.y.label,e=$("<table/>").addClass("table").append(["<thead><th></th><th>",c,"</th><th>",d,"</th></thead>"].join("")).append(_.map(a,function(a,b){return $(["<tr><td>",b,"</td><td>",a[0],"</td><td>",a[1],"</td></tr>"].join(""))}));b.empty().append(e)},toggleStats:function(a){var b=this.$el.find(".stats-display");a=void 0===a?b.is(":hidden"):a,a?(this.$el.find("svg").hide(),b.show(),this.$el.find(".controls .stats-toggle-btn").text("Plot")):(b.hide(),this.$el.find("svg").show(),this.$el.find(".controls .stats-toggle-btn").text("Stats"))},toString:function(){return"ScatterplotView()"}}),ScatterplotModel=Visualization.extend({defaults:{type:"scatterplot",config:{metadata:{dataLines:void 0},pagination:{currPage:0,perPage:3e3},width:400,height:400,margin:{top:16,right:16,bottom:40,left:54},x:{ticks:10,label:"X"},y:{ticks:10,label:"Y"},datapointSize:4,animDuration:500}}});
\ No newline at end of file
+function scatterplot(a,b,c){function d(){var a={v:{},h:{}};return a.v.lines=p.selectAll("line.v-grid-line").data(m.x.ticks(q.x.fn.ticks()[0])),a.v.lines.enter().append("svg:line").classed("grid-line v-grid-line",!0),a.v.lines.attr("x1",m.x).attr("x2",m.x).attr("y1",0).attr("y2",b.height),a.v.lines.exit().remove(),a.h.lines=p.selectAll("line.h-grid-line").data(m.y.ticks(q.y.fn.ticks()[0])),a.h.lines.enter().append("svg:line").classed("grid-line h-grid-line",!0),a.h.lines.attr("x1",0).attr("x2",b.width).attr("y1",m.y).attr("y2",m.y),a.h.lines.exit().remove(),a}function e(){return t.attr("cx",function(a,b){return m.x(j(a,b))}).attr("cy",function(a,b){return m.y(k(a,b))}).style("display","block").filter(function(){var a=d3.select(this).attr("cx"),c=d3.select(this).attr("cy");return 0>a||a>b.width?!0:0>c||c>b.height?!0:!1}).style("display","none")}function f(){$(".chart-info-box").remove(),q.redraw(),e(),s=d(),$(o.node()).trigger("zoom.scatterplot",{scale:n.scale(),translate:n.translate()})}function g(a,c,d){return c+=8,$(['<div class="chart-info-box" style="position: absolute">',void 0!==b.idColumn?"<div>"+d[b.idColumn]+"</div>":"","<div>",j(d),"</div>","<div>",k(d),"</div>","</div>"].join("")).css({top:a,left:c,"z-index":2})}var h=function(a,b){return"translate("+a+","+b+")"},i=function(a,b,c){return"rotate("+a+","+b+","+c+")"},j=function(a){return a[b.xColumn]},k=function(a){return a[b.yColumn]},l={x:{extent:d3.extent(c,j)},y:{extent:d3.extent(c,k)}},m={x:d3.scale.linear().domain(l.x.extent).range([0,b.width]),y:d3.scale.linear().domain(l.y.extent).range([b.height,0])},n=d3.behavior.zoom().x(m.x).y(m.y).scaleExtent([1,30]).scale(b.scale||1).translate(b.translate||[0,0]),o=d3.select(a).attr("class","scatterplot").attr("width","100%").attr("height",b.height+(b.margin.top+b.margin.bottom)),p=o.append("g").attr("class","content").attr("transform",h(b.margin.left,b.margin.top)).call(n);p.append("rect").attr("class","zoom-rect").attr("width",b.width).attr("height",b.height).style("fill","transparent");var q={x:{},y:{}};q.x.fn=d3.svg.axis().orient("bottom").scale(m.x).ticks(b.x.ticks).tickFormat(d3.format("s")),q.y.fn=d3.svg.axis().orient("left").scale(m.y).ticks(b.y.ticks).tickFormat(d3.format("s")),q.x.g=p.append("g").attr("class","x axis").attr("transform",h(0,b.height)).call(q.x.fn),q.y.g=p.append("g").attr("class","y axis").call(q.y.fn);var r=6;q.x.label=o.append("text").attr("class","axis-label").text(b.x.label).attr("text-anchor","middle").attr("dominant-baseline","text-after-edge").attr("x",b.width/2+b.margin.left).attr("y",b.height+b.margin.bottom+b.margin.top-r),q.y.label=o.append("text").attr("class","axis-label").text(b.y.label).attr("text-anchor","middle").attr("dominant-baseline","text-before-edge").attr("x",r).attr("y",b.height/2).attr("transform",i(-90,r,b.height/2)),q.redraw=function(){o.select(".x.axis").call(q.x.fn),o.select(".y.axis").call(q.y.fn)};var s=d(),t=p.selectAll(".glyph").data(c).enter().append("svg:circle").classed("glyph",!0).attr("cx",function(a,b){return m.x(j(a,b))}).attr("cy",function(a,b){return m.y(k(a,b))}).attr("r",0);t.transition().duration(b.animDuration).attr("r",b.datapointSize),e(),n.on("zoom",f),t.on("mouseover",function(a,c){var d=d3.select(this);d.style("fill","red").style("fill-opacity",1),p.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",d.attr("cx")-b.datapointSize).attr("y1",d.attr("cy")).attr("x2",0).attr("y2",d.attr("cy")).classed("hoverline",!0),d.attr("cy")<b.height&&p.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",d.attr("cx")).attr("y1",+d.attr("cy")+b.datapointSize).attr("x2",d.attr("cx")).attr("y2",b.height).classed("hoverline",!0);var e=this.getBoundingClientRect();$("body").append(g(e.top,e.right,a)),$(o.node()).trigger("mouseover-datapoint.scatterplot",[this,a,c])}),t.on("mouseout",function(){d3.select(this).style("fill","black").style("fill-opacity",.2),p.selectAll(".hoverline").remove(),$(".chart-info-box").remove()})}this.scatterplot=this.scatterplot||{},this.scatterplot.chartcontrol=Handlebars.template(function(a,b,c,d,e){this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var f,g="",h="function",i=this.escapeExpression;return g+='<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 data-config-key="datapointSize" class="form-input numeric-slider-input">\n <label for="datapointSize">Size of data point: </label>\n <div class="slider-output">',(f=c.datapointSize)?f=f.call(b,{hash:{},data:e}):(f=b.datapointSize,f=typeof f===h?f.apply(b):f),g+=i(f)+'</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 data-config-key="width" class="form-input numeric-slider-input">\n <label for="width">Chart width: </label>\n <div class="slider-output">',(f=c.width)?f=f.call(b,{hash:{},data:e}):(f=b.width,f=typeof f===h?f.apply(b):f),g+=i(f)+'</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 data-config-key="height" class="form-input numeric-slider-input">\n <label for="height">Chart height: </label>\n <div class="slider-output">',(f=c.height)?f=f.call(b,{hash:{},data:e}):(f=b.height,f=typeof f===h?f.apply(b):f),g+=i(f)+'</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 data-config-key="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="'+i((f=b.x,f=null==f||f===!1?f:f.label,typeof f===h?f.apply(b):f))+'" />\n <p class="form-help help-text-small"></p>\n</div>\n\n<div data-config-key="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="'+i((f=b.y,f=null==f||f===!1?f:f.label,typeof f===h?f.apply(b):f))+'" />\n <p class="form-help help-text-small"></p>\n</div>\n\n<button class="render-button btn btn-primary active">Draw</button>\n'}),this.scatterplot.datacontrol=Handlebars.template(function(a,b,c,d,e){function f(a,b){var d,e="";return e+='\n <option value="',(d=c.index)?d=d.call(a,{hash:{},data:b}):(d=a.index,d=typeof d===j?d.apply(a):d),e+=k(d)+'">',(d=c.name)?d=d.call(a,{hash:{},data:b}):(d=a.name,d=typeof d===j?d.apply(a):d),e+=k(d)+"</option>\n "}function g(){return'checked="true"'}this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var h,i="",j="function",k=this.escapeExpression,l=this;return 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\n<div class="column-select">\n <label>Data column for X: </label>\n <select name="xColumn">\n ',h=c.each.call(b,b.numericColumns,{hash:{},inverse:l.noop,fn:l.program(1,f,e),data:e}),(h||0===h)&&(i+=h),i+='\n </select>\n</div>\n<div class="column-select">\n <label>Data column for Y: </label>\n <select name="yColumn">\n ',h=c.each.call(b,b.numericColumns,{hash:{},inverse:l.noop,fn:l.program(1,f,e),data:e}),(h||0===h)&&(i+=h),i+='\n </select>\n</div>\n\n\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="idColumn">\n ',h=c.each.call(b,b.allColumns,{hash:{},inverse:l.noop,fn:l.program(1,f,e),data:e}),(h||0===h)&&(i+=h),i+='\n </select>\n</div>\n\n\n<div id="first-line-header" style="display: none;">\n <p>Possible headers: ',(h=c.possibleHeaders)?h=h.call(b,{hash:{},data:e}):(h=b.possibleHeaders,h=typeof h===j?h.apply(b):h),i+=k(h)+'\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 ',h=c["if"].call(b,b.usePossibleHeaders,{hash:{},inverse:l.noop,fn:l.program(3,g,e),data:e}),(h||0===h)&&(i+=h),i+='/>\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<button class="render-button btn btn-primary active">Draw</button>\n'}),this.scatterplot.editor=Handlebars.template(function(a,b,c,d,e){this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var f="";return f+='<div class="scatterplot-editor tabbable tabs-left">\n \n <ul class="nav nav-tabs">\n \n <li class="active">\n <a title="Use this tab to change which data are used"\n href="#data-control" data-toggle="tab">Data Controls</a>\n </li>\n <li>\n <a title="Use this tab to change how the chart is drawn"\n href="#chart-control" data-toggle="tab" >Chart Controls</a>\n </li>\n \n <li class="disabled">\n <a title="This tab will display the chart"\n href="#chart-display" data-toggle="tab">Chart</a>\n </li>\n \n <li class="file-controls">\n<!-- <button class="copy-btn btn btn-default"\n title="Save this as a new visualization">Save to new</button>-->\n <button class="save-btn btn btn-default">Save</button>\n </li>\n </ul>\n\n \n <div class="tab-content">\n \n <div id="data-control" class="scatterplot-config-control tab-pane active">\n \n </div>\n \n \n <div id="chart-control" class="scatterplot-config-control tab-pane">\n \n </div>\n\n \n <div id="chart-display" class="scatterplot-display tab-pane"></div>\n\n </div>\n</div>\n'});var ScatterplotConfigEditor=Backbone.View.extend(LoggableMixin).extend({className:"scatterplot-control-form",initialize:function(a){if(this.model||(this.model=new Visualization({type:"scatterplot"})),this.log(this+".initialize, attributes:",a),!a||!a.dataset)throw new Error("ScatterplotConfigEditor requires a dataset");this.dataset=a.dataset,this.log("dataset:",this.dataset),this.display=new ScatterplotDisplay({dataset:a.dataset,model:this.model})},render:function(){this.$el.empty().append(ScatterplotConfigEditor.templates.mainLayout({})),this.model.id&&(this.$el.find(".copy-btn").show(),this.$el.find(".save-btn").text("Update saved")),this.$el.find("[title]").tooltip(),this._render_dataControl(),this._render_chartControls(),this._render_chartDisplay();var a=this.model.get("config");return this.model.id&&_.isFinite(a.xColumn)&&_.isFinite(a.yColumn)&&this.renderChart(),this},_render_dataControl:function(a){a=a||this.$el;var b=this,c=this.dataset,d=c.metadata_column_names||[],e=this.model.get("config"),f=[],g=_.map(c.metadata_column_types,function(a,b){var c={index:b,type:a,name:d[b]||"column "+(b+1)};return("int"===c.type||"float"===c.type)&&f.push(c),c});f.length<2&&(f=g);var h=a.find(".tab-pane#data-control");h.html(ScatterplotConfigEditor.templates.dataControl({allColumns:g,numericColumns:f}));var i={xColumn:_.isFinite(e.xColumn)?e.xColumn:f[0].index,yColumn:_.isFinite(e.yColumn)?e.yColumn:f[1].index,idColumn:g[0].index};if(_.isFinite(e.idColumn))i.idColumn=e.idColumn;else if(g.length>2){var j=_.find(g,function(a,b){return b!==i.xColumn&&b!==i.yColumn});i.idColumn=j.index}return e=this.model.set("config",i,{silent:!0}).get("config"),h.find('[name="xColumn"]').val(e.xColumn).on("change",function(){b.model.set("config",{xColumn:Number($(this).val())})}),h.find('[name="yColumn"]').val(e.yColumn).on("change",function(){b.model.set("config",{yColumn:Number($(this).val())})}),h.find('select[name="idColumn"]').val(e.idColumn).on("change",function(){b.model.set("config",{idColumn:Number($(this).val())})}),void 0!==e.idColumn&&h.find("#include-id-checkbox").prop("checked",!0).trigger("change"),h.find("[title]").tooltip(),h},_render_chartControls:function(a){function b(){var a=$(this),b=a.slider("value");c.model.set("config",_.object([[a.parent().data("config-key"),b]])),a.siblings(".slider-output").text(b)}a=a||this.$el;var c=this,d=this.model.get("config"),e=a.find("#chart-control");e.html(ScatterplotConfigEditor.templates.chartControl(d));var f={datapointSize:{min:2,max:10,step:1},width:{min:200,max:800,step:20},height:{min:200,max:800,step:20}};e.find(".numeric-slider-input").each(function(){var a=$(this),c=a.attr("data-config-key"),e=_.extend(f[c],{value:d[c],change:b,slide:b});a.find(".slider").slider(e),a.children(".slider-output").text(d[c])});var g=this.dataset.metadata_column_names||[],h=d.xLabel||g[d.xColumn]||"X",i=d.yLabel||g[d.yColumn]||"Y";return e.find('input[name="X-axis-label"]').val(h).on("change",function(){c.model.set("config",{xLabel:$(this).val()})}),e.find('input[name="Y-axis-label"]').val(i).on("change",function(){c.model.set("config",{yLabel:$(this).val()})}),e.find("[title]").tooltip(),e},_render_chartDisplay:function(a){a=a||this.$el;var b=a.find(".tab-pane#chart-display");return this.display.setElement(b),this.display.render(),b.find("[title]").tooltip(),b},events:{"change #include-id-checkbox":"toggleThirdColumnSelector","click #data-control .render-button":"renderChart","click #chart-control .render-button":"renderChart","click .save-btn":"saveVisualization"},saveVisualization:function(){var a=this;this.model.save().fail(function(b,c,d){console.error(b,c,d),a.trigger("save:error",view),alert("Error loading data:\n"+b.responseText)}).then(function(){a.render()})},toggleThirdColumnSelector:function(){this.$el.find('select[name="idColumn"]').parent().toggle()},renderChart:function(){this.$el.find(".nav li.disabled").removeClass("disabled"),this.$el.find("ul.nav").find('a[href="#chart-display"]').tab("show"),this.display.fetchData()},toString:function(){return"ScatterplotConfigEditor("+(this.dataset?this.dataset.id:"")+")"}});ScatterplotConfigEditor.templates={mainLayout:scatterplot.editor,dataControl:scatterplot.datacontrol,chartControl:scatterplot.chartcontrol};var ScatterplotDisplay=Backbone.View.extend({initialize:function(a){this.data=null,this.dataset=a.dataset,this.calcNumPages()},calcNumPages:function(){var a=this.model.get("config");this.lineCount=this.dataset.metadata_data_lines,this.numPages=this.lineCount?Math.ceil(this.lineCount/a.pagination.perPage):void 0,this.lineCount&&void 0!==this.numPages||console.warn("no data total found")},fetchData:function(){this.showLoadingIndicator("getting data");var a=this,b=this.model.get("config"),c=jQuery.getJSON("/api/datasets/"+this.dataset.id,{data_type:"raw_data",provider:"dataset-column",limit:b.pagination.perPage,offset:b.pagination.currPage*b.pagination.perPage});return c.done(function(b){a.data=b.data,a.trigger("data:fetched",a),a.renderData()}),c.fail(function(b,c,d){console.error(b,c,d),a.trigger("data:error",a),alert("Error loading data:\n"+b.responseText)}),c},showLoadingIndicator:function(){this.$el.find(".scatterplot-data-info").html(['<div class="loading-indicator">','<span class="fa fa-spinner fa-spin"></span>','<span class="loading-indicator-message">loading...</span>',"</div>"].join(""))},template:function(){var a=['<div class="controls clear">','<div class="left">',"</div>",'<div class="right">','<p class="scatterplot-data-info"></p>','<button class="stats-toggle-btn">Stats</button>','<button class="rerender-btn">Redraw</button>',"</div>","</div>","<svg/>",'<div class="stats-display"></div>'].join("");return a},render:function(){return this.$el.addClass("scatterplot-display").html(this.template()),this.data&&this.renderData(),this},renderData:function(){this.renderLeftControls(),this.renderRightControls(),this.renderPlot(this.data),this.getStats()},renderLeftControls:function(){return this.lineCount?this.$el.find(".controls .left").empty().append(this.renderPagination()):this.$el.find(".controls .left").empty().append(this.renderPrevNext()),this},renderRightControls:function(){var a=this;this.setLineInfo(this.data),this.$el.find(".stats-toggle-btn").off().click(function(){a.toggleStats()}),this.$el.find(".rerender-btn").off().click(function(){a.resetZoom(),a.renderPlot(this.data)})},renderPlot:function(){var a=this,b=this.$el.find("svg");this.toggleStats(!1),b.off().empty().show().on("zoom.scatterplot",function(b,c){a.model.set("config",c)}),scatterplot(b.get(0),this.model.get("config"),this.data)},setLineInfo:function(a,b){if(a){var c=this.model.get("config"),d=this.lineCount||"an unknown total",e=c.pagination.currPage*c.pagination.perPage,f=e+a.length;this.$el.find(".controls p.scatterplot-data-info").text([e+1,"to",f,"of",d].join(" "))}else this.$el.find(".controls p.scatterplot-data-info").html(b||"");return this},resetZoom:function(a,b){return a=void 0!==a?a:1,b=void 0!==b?b:[0,0],this.model.set("config",{scale:a,translate:b}),this},goToPage:function(a){var b=this.model.get("config").pagination;return 0>=a&&(a=0),this.numPages&&a>=this.numPages&&(a=this.numPages-1),a===b.currPage?this:(b.currPage=a,this.model.set("config",{pagination:b}),this.resetZoom(),this.fetchData(),this)},nextPage:function(){var a=this.model.get("config").pagination.currPage;return this.goToPage(a+1)},prevPage:function(){var a=this.model.get("config").pagination.currPage;return this.goToPage(a-1)},renderPrevNext:function(){var a=this.model.get("config");if(!this.data)return null;if(0===a.pagination.currPage&&this.data.length<a.pagination.perPage)return null;var b=this,c=$('<li><a href="javascript:void(0);">Prev</a></li>').click(function(){b.prevPage()}),d=$('<li><a href="javascript:void(0);">Next</a></li>').click(function(){b.nextPage()});return 0===a.pagination.currPage&&c.addClass("disabled"),this.numPages&&a.pagination.currPage===this.numPages-1&&d.addClass("disabled"),$("<ul/>").addClass("pagination data-prev-next").append([c,d])},renderPagination:function(){var a=this.model.get("config");if(!this.data)return null;if(!this.numPages)return null;if(0===a.pagination.currPage&&this.data.length<a.pagination.perPage)return null;var b=this,c=$("<ul/>").addClass("pagination data-pages");pageNumClick=function(){b.goToPage($(this).data("page"))};for(var d=0;d<this.numPages;d+=1){var e=$(['<li><a href="javascript:void(0);">',d+1,"</a></li>"].join("")).attr("data-page",d).click(pageNumClick);d===a.pagination.currPage&&e.addClass("active"),c.append(e)}return c},getStats:function(){if(this.data){var a=this,b=this.model.get("config"),c=new Worker("/plugins/visualizations/scatterplot/static/worker-stats.js");c.postMessage({data:this.data,keys:[b.xColumn,b.yColumn]}),c.onerror=function(){c.terminate()},c.onmessage=function(b){a.renderStats(b.data)}}},renderStats:function(a){var b=this.model.get("config"),c=this.$el.find(".stats-display"),d=b.x.label,e=b.y.label,f=$("<table/>").addClass("table").append(["<thead><th></th><th>",d,"</th><th>",e,"</th></thead>"].join("")).append(_.map(a,function(a,b){return $(["<tr><td>",b,"</td><td>",a[0],"</td><td>",a[1],"</td></tr>"].join(""))}));c.empty().append(f)},toggleStats:function(a){var b=this.$el.find(".stats-display");a=void 0===a?b.is(":hidden"):a,a?(this.$el.find("svg").hide(),b.show(),this.$el.find(".controls .stats-toggle-btn").text("Plot")):(b.hide(),this.$el.find("svg").show(),this.$el.find(".controls .stats-toggle-btn").text("Stats"))},toString:function(){return"ScatterplotView()"}}),ScatterplotModel=Visualization.extend({defaults:{type:"scatterplot",config:{pagination:{currPage:0,perPage:3e3},width:400,height:400,margin:{top:16,right:16,bottom:40,left:54},x:{ticks:10,label:"X"},y:{ticks:10,label:"Y"},datapointSize:4,animDuration:500,scale:1,translate:[0,0]}}});
\ No newline at end of file
diff -r a11c52513e4472237c9070a06ff11f123af83c06 -r a253d56a0dd6be3816581a0c7ed0ada191a1693b config/plugins/visualizations/scatterplot/static/scatterplot.css
--- a/config/plugins/visualizations/scatterplot/static/scatterplot.css
+++ b/config/plugins/visualizations/scatterplot/static/scatterplot.css
@@ -39,6 +39,22 @@
font-size: smaller;
}
+.scatterplot-editor .file-controls {
+ float: right;
+ /*margin-right: 16px;*/
+}
+
+.file-controls .btn {
+ height: 24px;
+ padding: 0px 10px 0px 10px;
+ line-height: 24px;
+}
+
+.file-controls .copy-btn {
+ display: none;
+}
+
+
/* ============================================ config controls */
.scatterplot-config-control {
max-width: 768px;
@@ -84,8 +100,6 @@
/* ============================================ plot display */
/* -------------------------------------------- load indicators */
.scatterplot-display .loading-indicator {
- position: absolute;
- margin: 10px 0px 0px 10px;
color: grey;
}
diff -r a11c52513e4472237c9070a06ff11f123af83c06 -r a253d56a0dd6be3816581a0c7ed0ada191a1693b config/plugins/visualizations/scatterplot/templates/scatterplot.mako
--- a/config/plugins/visualizations/scatterplot/templates/scatterplot.mako
+++ b/config/plugins/visualizations/scatterplot/templates/scatterplot.mako
@@ -1,18 +1,8 @@
<%
- hda_dict = trans.security.encode_dict_ids( hda.to_dict() )
-
- config = query_args
- title = "Scatterplot of '" + hda.name + "'"
- info = hda.info
-
- visualization = context.get( 'visualization' )
- if visualization is not None:
- config = visualization.latest_revision.config
- config.update( query_args )
- title = visualization.title
- info = config.get( 'description', info )
-
- config[ 'type' ] = 'scatterplot'
+ default_title = "Scatterplot of '" + hda.name + "'"
+ info = hda.name
+ if hda.info:
+ info += ' : ' + hda.info
# optionally bootstrap data from dprov
##data = list( hda.datatype.dataset_column_dataprovider( hda, limit=10000 ) )
@@ -23,7 +13,7 @@
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-<title>${hda.name} | ${visualization_name}</title>
+<title>${title or default_title} | ${visualization_display_name}</title>
## ----------------------------------------------------------------------------
<link type="text/css" rel="Stylesheet" media="screen" href="/static/style/base.css">
@@ -52,23 +42,29 @@
%if not embedded:
## dataset info: only show if on own page
<div class="chart-header">
- <h2>${title}</h2>
+ <h2>${title or default_title}</h2><p>${info}</p></div><div class="scatterplot-editor"></div><script type="text/javascript">
$(function(){
- var model = new ScatterplotModel( ${h.to_json_string( config )} ),
- hdaJson = ${h.to_json_string( hda_dict )},
+ var model = new ScatterplotModel({
+ id : ${h.to_json_string( visualization_id )} || undefined,
+ title : "${title or default_title}",
+ config : ${h.to_json_string( config, indent=2 )}
+ }),
+ hdaJson = ${h.to_json_string( trans.security.encode_dict_ids( hda.to_dict() ), indent=2 )},
editor = new ScatterplotConfigEditor({
el : $( '.scatterplot-editor' ).attr( 'id', 'scatterplot-editor-hda-' + hdaJson.id ),
model : model,
dataset : hdaJson
}).render();
window.editor = editor;
- // uncomment to auto render for development
- //$( '.render-button:visible' ).click();
+
+ model.on( 'change:title', function(){
+ $( '.chart-header h2' ).text( model.get( 'title' ) );
+ })
});
</script>
diff -r a11c52513e4472237c9070a06ff11f123af83c06 -r a253d56a0dd6be3816581a0c7ed0ada191a1693b config/plugins/visualizations/visualization_base.mako
--- a/config/plugins/visualizations/visualization_base.mako
+++ /dev/null
@@ -1,67 +0,0 @@
-# -*- coding: utf-8 -*-
-<% _=n_ %>
-
-%if embedded:
- ${self.as_embedded()}
-%else:
- ${self.as_page()}
-%endif
-
-## render this inside another page or via ajax
-<%def name="as_embedded()">
- ${self.stylesheets()}
- ${self.javascripts()}
- ${self.get_body()}
-</%def>
-
-## render this as it's own page
-<%def name="as_page()">
-<!DOCTYPE HTML>
-<html>
- <head>
- <title>${self.title()}</title>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- ${self.metas()}
- ${self.stylesheets()}
- ${self.javascripts()}
- </head>
- <body>
- ${self.get_body()}
- </body>
-</html>
-</%def>
-##TODO: late_javascripts
-
-## Default body
-<%def name="get_body()"></%def>
-
-## Default title
-<%def name="title()">${visualization_name}</%def>
-
-## Additional metas can be defined by templates inheriting from this one.
-<%def name="metas()"></%def>
-
-## Default stylesheets
-<%def name="stylesheets()">
-${h.css('base')}
-</%def>
-
-## Default javascripts
-<%def name="javascripts()">
-${h.js(
- "libs/jquery/jquery",
- "libs/jquery/jquery.migrate"
-)}
-
-<script type="text/javascript">
- // console protection
- window.console = window.console || {
- log : function(){},
- debug : function(){},
- info : function(){},
- warn : function(){},
- error : function(){},
- assert : function(){}
- };
-</script>
-</%def>
diff -r a11c52513e4472237c9070a06ff11f123af83c06 -r a253d56a0dd6be3816581a0c7ed0ada191a1693b lib/galaxy/visualization/registry.py
--- a/lib/galaxy/visualization/registry.py
+++ b/lib/galaxy/visualization/registry.py
@@ -223,7 +223,7 @@
def is_object_applicable( self, trans, target_object, data_source_tests ):
"""
Run a visualization's data_source tests to find out if
- it be applied to the target_object.
+ it can be applied to the target_object.
"""
#log.debug( 'is_object_applicable( self, trans, %s, %s )', target_object, data_source_tests )
for test in data_source_tests:
@@ -306,9 +306,9 @@
Both `params` and `param_modifiers` default to an empty dictionary.
"""
- visualization = self.plugins.get( visualization_name )
- expected_params = visualization.config.get( 'params', {} )
- param_modifiers = visualization.config.get( 'param_modifiers', {} )
+ plugin = self.plugins.get( visualization_name )
+ expected_params = plugin.config.get( 'params', {} )
+ param_modifiers = plugin.config.get( 'param_modifiers', {} )
return ( expected_params, param_modifiers )
def query_dict_to_resources( self, trans, controller, visualization_name, query_dict ):
@@ -322,6 +322,16 @@
trans, controller, param_confs, query_dict, param_modifiers )
return resources
+ def query_dict_to_config( self, trans, controller, visualization_name, query_dict ):
+ """
+ Given a query string dict (i.e. kwargs) from a controller action, parse
+ and return any key/value pairs found in the plugin's `params` section.
+ """
+ plugin = self.plugins.get( visualization_name )
+ param_confs = plugin.config.get( 'params', {} )
+ config = self.resource_parser.parse_config( trans, controller, param_confs, query_dict )
+ return config
+
# ------------------------------------------------------------------- parsing the config file
class ParsingException( ValueError ):
@@ -559,37 +569,38 @@
for test_elem in xml_tree_list:
test_type = test_elem.get( 'type', 'eq' )
- test_result = test_elem.text
+ test_result = test_elem.text.strip() if test_elem.text else None
if not test_type or not test_result:
log.warn( 'Skipping test. Needs both type attribute and text node to be parsed: '
+ '%s, %s' %( test_type, test_elem.text ) )
continue
+ test_result = test_result.strip()
# test_attr can be a dot separated chain of object attributes (e.g. dataset.datatype) - convert to list
#TODO: too dangerous - constrain these to some allowed list
#TODO: does this err if no test_attr - it should...
test_attr = test_elem.get( 'test_attr' )
test_attr = test_attr.split( self.ATTRIBUTE_SPLIT_CHAR ) if isinstance( test_attr, str ) else []
+ #log.debug( 'test_type: %s, test_attr: %s, test_result: %s', test_type, test_attr, test_result )
+
# build a lambda function that gets the desired attribute to test
getter = self._build_getattr_lambda( test_attr )
-
# result type should tell the registry how to convert the result before the test
test_result_type = test_elem.get( 'result_type', 'string' )
# test functions should be sent an object to test, and the parsed result expected from the test
-
- # is test_attr attribute an instance of result
if test_type == 'isinstance':
+ # is test_attr attribute an instance of result
#TODO: wish we could take this further but it would mean passing in the datatypes_registry
test_fn = lambda o, result: isinstance( getter( o ), result )
- # does the object itself have a datatype attr and does that datatype have the given dataprovider
elif test_type == 'has_dataprovider':
+ # does the object itself have a datatype attr and does that datatype have the given dataprovider
test_fn = lambda o, result: ( hasattr( getter( o ), 'has_dataprovider' )
and getter( o ).has_dataprovider( result ) )
- # default to simple (string) equilavance (coercing the test_attr to a string)
else:
+ # default to simple (string) equilavance (coercing the test_attr to a string)
test_fn = lambda o, result: str( getter( o ) ) == result
tests.append({
@@ -737,6 +748,16 @@
The keys used to store the new values can optionally be re-mapped to
new keys (e.g. dataset_id="NNN" -> hda=<HistoryDatasetAsscoation>).
"""
+ primitive_parsers = {
+ 'str' : lambda param: galaxy.util.sanitize_html.sanitize_html( param, 'utf-8' ),
+ 'bool' : lambda param: galaxy.util.string_as_bool( param ),
+ 'int' : lambda param: int( param ),
+ 'float' : lambda param: float( param ),
+ #'date' : lambda param: ,
+ 'json' : ( lambda param: galaxy.util.json.from_json_string(
+ galaxy.util.sanitize_html.sanitize_html( param ) ) ),
+ }
+
#TODO: kinda torn as to whether this belongs here or in controllers.visualization
# taking the (questionable) design path of passing a controller in
# (which is the responsible party for getting model, etc. resources )
@@ -749,8 +770,6 @@
If param is required and not present, raises a `KeyError`.
"""
#log.debug( 'parse_parameter_dictionary, query_params:\n%s', query_params )
- # first parse any params from any visualizations that were passed
- query_params = self.get_params_from_visualization_param( trans, controller, param_config_dict, query_params )
# parse the modifiers first since they modify the params coming next
#TODO: this is all really for hda_ldda - which we could replace with model polymorphism
@@ -782,12 +801,43 @@
if resource == None:
if param_config[ 'required' ]:
raise KeyError( 'required param %s not found in URL' %( param_name ) )
- resource = self.parse_parameter_default( trans, param_config )
+ resource = self.parse_parameter_default( trans, controller, param_config )
resources[ var_name_in_template ] = resource
return resources
+ def parse_config( self, trans, controller, param_config_dict, query_params ):
+ """
+ Return `query_params` dict parsing only JSON serializable params.
+ Complex params such as models, etc. are left as the original query value.
+ Keys in `query_params` not found in the `param_config_dict` will not be
+ returned.
+ """
+ #log.debug( 'parse_config, query_params:\n%s', query_params )
+ config = {}
+ for param_name, param_config in param_config_dict.items():
+ config_val = query_params.get( param_name, None )
+ if config_val is not None and param_config[ 'type' ] in self.primitive_parsers:
+ try:
+ config_val = self.parse_parameter( trans, controller, param_config, config_val )
+
+ except Exception, exception:
+ log.warn( 'Exception parsing visualization param from query: '
+ + '%s, %s, (%s) %s' %( param_name, config_val, str( type( exception ) ), str( exception ) ))
+ config_val = None
+
+ # here - we've either had no value in the query_params or there was a failure to parse
+ # so: if there's a default and it's not None, add it to the config
+ if config_val is None:
+ if param_config.get( 'default', None ) is None:
+ continue
+ config_val = self.parse_parameter_default( trans, controller, param_config )
+
+ config[ param_name ] = config_val
+
+ return config
+
#TODO: I would LOVE to rip modifiers out completely
def parse_parameter_modifiers( self, trans, controller, param_modifiers, query_params ):
"""
@@ -811,11 +861,11 @@
target_modifiers[ modifier_name ] = modifier
else:
#TODO: required attr?
- target_modifiers[ modifier_name ] = self.parse_parameter_default( trans, modifier_config )
+ target_modifiers[ modifier_name ] = self.parse_parameter_default( trans, controller, modifier_config )
return parsed_modifiers
- def parse_parameter_default( self, trans, param_config ):
+ def parse_parameter_default( self, trans, controller, param_config ):
"""
Parse any default values for the given param, defaulting the default
to `None`.
@@ -828,46 +878,8 @@
# otherwise, parse (currently param_config['default'] is a string just like query param and needs to be parsed)
# this saves us the trouble of parsing the default when the config file is read
# (and adding this code to the xml parser)
- return self.parse_parameter( trans, param_config, default )
+ return self.parse_parameter( trans, controller, param_config, default )
- def get_params_from_visualization_param( self, trans, controller, param_config_dict, query_params ):
- #log.debug( 'parse_visualization_params: %s', param_config_dict )
- #log.debug( ' : %s', query_params )
-
- # first, find the visualization in the parameters if any
- visualization = None
- #precondition: assume one visualization
- for param_name, param_config in param_config_dict.items():
- if param_config.get( 'type' ) == 'visualization':
- query_val = query_params.get( param_name )
- if query_val is None:
- continue
-
- #log.debug( 'found visualization param: %s, %s', param_name, query_val )
- visualization = self.parse_parameter( trans, controller, param_config, query_val )
- if visualization:
- break
-
- # if no vis is found, can't get any new params from it: return the original query_params
- if not visualization:
- #log.debug( 'visualization not found' )
- return query_params
- #log.debug( 'found visualization: %s', visualization )
-
- # next, attempt to copy any params from the visualizations config
- visualization_config = visualization.latest_revision.config
- #log.debug( '\t config: %s', visualization_config )
- params_from_visualization = {}
- for param_name, param_config in param_config_dict.items():
- if param_name in visualization_config:
- params_from_visualization[ param_name ] = visualization_config[ param_name ]
- #log.debug( 'params_from_visualization: %s', params_from_visualization )
-
- # layer the query_params over the params from the visualization, returning the combined
- params_from_visualization.update( query_params )
- return params_from_visualization
-
-#TODO: make parse_visualization separate
def parse_parameter( self, trans, controller, expected_param_data, query_param,
recurse=True, param_modifiers=None ):
"""
@@ -892,24 +904,11 @@
parsed_param.append( self._parse_param( trans, expected_param_data, query_param, recurse=False ) )
return parsed_param
- primitive_parsers = {
- 'str' : lambda param: galaxy.util.sanitize_html.sanitize_html( param, 'utf-8' ),
- 'bool' : lambda param: galaxy.util.string_as_bool( param ),
- 'int' : lambda param: int( param ),
- 'float' : lambda param: float( param ),
- #'date' : lambda param: ,
- 'json' : ( lambda param: galaxy.util.json.from_json_string(
- galaxy.util.sanitize_html.sanitize_html( param ) ) ),
- }
- parser = primitive_parsers.get( param_type, None )
- if parser:
+ if param_type in self.primitive_parsers:
#TODO: what about param modifiers on primitives?
- parsed_param = parser( query_param )
+ parsed_param = self.primitive_parsers[ param_type ]( query_param )
- #TODO: constrain_to
- # this gets complicated - for strings - relatively simple but still requires splitting and using in
- # for more complicated cases (ints, json) this gets weird quick
- #TODO:?? remove?
+ #TODO: constrain_to: this gets complicated - remove?
# db models
#TODO: subclass here?
diff -r a11c52513e4472237c9070a06ff11f123af83c06 -r a253d56a0dd6be3816581a0c7ed0ada191a1693b lib/galaxy/webapps/galaxy/controllers/visualization.py
--- a/lib/galaxy/webapps/galaxy/controllers/visualization.py
+++ b/lib/galaxy/webapps/galaxy/controllers/visualization.py
@@ -1,9 +1,10 @@
from __future__ import absolute_import
import os
+import copy
from sqlalchemy import desc, or_, and_
-from paste.httpexceptions import HTTPNotFound
+from paste.httpexceptions import HTTPNotFound, HTTPBadRequest
from galaxy import model, web
from galaxy.model.item_attrs import UsesAnnotations, UsesItemRatings
@@ -13,6 +14,9 @@
from galaxy.datatypes.interval import Bed
from galaxy.util.json import from_json_string
from galaxy.util.sanitize_html import sanitize_html
+from galaxy.util import bunch
+from galaxy import util
+from galaxy.visualization import registry
from galaxy.visualization.genomes import decode_dbkey
from galaxy.visualization.data_providers.phyloviz import PhylovizDataProvider
from galaxy.visualization.genomes import GenomeRegion
@@ -22,6 +26,27 @@
import logging
log = logging.getLogger( __name__ )
+# -- Misc.
+class OpenObject( dict ):
+ #TODO: move to util.data_structures
+ """
+ A dict that allows assignment and attribute retrieval using the dot
+ operator.
+
+ If an attribute isn't contained in the dict `None` is returned (no
+ KeyError).
+ JSON-serializable.
+ """
+ def __getitem__( self, key ):
+ if key not in self:
+ return None
+ return super( OpenObject, self ).__getitem__( key )
+
+ def __getattr__( self, key ):
+ return self.__getitem__( key )
+
+
+
#
# -- Grids --
#
@@ -168,9 +193,14 @@
"""
Returns dictionary used to create item link.
"""
- controller = "visualization"
- action = item.type
- return dict( controller=controller, action=action, id=item.id )
+ url_kwargs = dict( controller='visualization', id=item.id )
+ #TODO: hack to build link to saved visualization - need trans in this function instead in order to do
+ #link_data = trans.app.visualizations_registry.get_visualizations( trans, item )
+ if item.type in registry.VisualizationsRegistry.BUILT_IN_VISUALIZATIONS:
+ url_kwargs[ 'action' ] = item.type
+ else:
+ url_kwargs[ 'action' ] = 'saved'
+ return url_kwargs
# Grid definition
title = "Saved Visualizations"
@@ -179,6 +209,7 @@
default_filter = dict( title="All", deleted="False", tags="All", sharing="All" )
columns = [
grids.TextColumn( "Title", key="title", attach_popup=True, link=get_url_args ),
+ #TODO: should use display name when available
grids.TextColumn( "Type", key="type" ),
grids.TextColumn( "Dbkey", key="dbkey" ),
grids.IndividualTagsColumn( "Tags", key="tags", model_tag_association_class=model.VisualizationTagAssociation, filterable="advanced", grid_name="VisualizationListGrid" ),
@@ -623,7 +654,6 @@
Save a visualization; if visualization does not have an ID, a new
visualization is created. Returns JSON of visualization.
"""
-
# Get visualization attributes from kwargs or from config.
vis_config = from_json_string( vis_json )
vis_type = type or vis_config[ 'type' ]
@@ -686,10 +716,45 @@
#
# Visualizations.
#
+ @web.expose
+ @web.require_login( "use Galaxy visualizations", use_panels=True )
+ def saved( self, trans, id=None, revision=None, type=None, config=None, title=None, **kwargs ):
+ """
+ """
+ DEFAULT_VISUALIZATION_NAME = 'Unnamed Visualization'
+
+ # post to saved in order to save a visualization
+ #TODO: re-route this one to clear up signature
+ if trans.request.method == 'POST':
+ if type is None or config is None:
+ return HTTPBadRequest( 'A visualization type and config are required to save a visualization' )
+ if isinstance( config, basestring ):
+ config = from_json_string( config )
+ title = title or DEFAULT_VISUALIZATION_NAME
+ #TODO: allow saving to (updating) a specific revision - should be part of UsesVisualization
+ #TODO: would be easier if this returned the visualization directly
+ returned = self.save_visualization( trans, config, type, id, title )
+
+ # redirect to GET to prevent annoying 'Do you want to post again?' dialog on page reload
+ render_url = web.url_for( controller='visualization', action='saved', id=returned.get( 'vis_id' ) )
+ return trans.response.send_redirect( render_url )
+
+ if id is None:
+ return HTTPBadRequest( 'A valid visualization id is required to load a visualization' )
+
+ # render the saved visualization by passing to render, sending latest revision config
+ #TODO: allow loading a specific revision - should be part of UsesVisualization
+ visualization = self.get_visualization( trans, id, check_ownership=True, check_accessible=False )
+ config = copy.copy( visualization.latest_revision.config )
+
+ # re-add title to kwargs for passing to render
+ if title:
+ kwargs[ 'title' ] = title
+ return self.render( trans, visualization.type, visualization, config=config, **kwargs )
@web.expose
@web.require_login( "use Galaxy visualizations", use_panels=True )
- def render( self, trans, visualization_name, embedded=None, **kwargs ):
+ def render( self, trans, visualization_name, visualization=None, config=None, embedded=None, **kwargs ):
"""
Render the appropriate visualization template, parsing the `kwargs`
into appropriate variables and resources (such as ORM models)
@@ -697,6 +762,8 @@
URL: /visualization/show/{visualization_name}
"""
+ config = config or {}
+
# validate name vs. registry
registry = trans.app.visualizations_registry
if not registry:
@@ -708,20 +775,32 @@
returned = None
try:
- # convert query string to resources for template based on registry config
- #NOTE: passing in controller to keep resource lookup within the controller's responsibilities
- # (and not the ResourceParser)
- resources = registry.query_dict_to_resources( trans, self, visualization_name, kwargs )
+ # get the config for passing to the template from the kwargs dict, parsed using the plugin's params setting
+ config_from_kwargs = registry.query_dict_to_config( trans, self, visualization_name, kwargs )
+ config.update( config_from_kwargs )
+ config = OpenObject( **config )
+ # further parse config to resources (models, etc.) used in template based on registry config
+ resources = registry.query_dict_to_resources( trans, self, visualization_name, config )
+
+ # if a saved visualization, pass in the encoded visualization id or None if a new render
+ encoded_visualization_id = None
+ if visualization:
+ encoded_visualization_id = trans.security.encode_id( visualization.id )
+
+ visualization_display_name = plugin.config[ 'name' ]
+ title = visualization.latest_revision.title if visualization else kwargs.get( 'title', None )
# look up template and render
template_path = plugin.config[ 'template' ]
returned = registry.fill_template( trans, plugin, template_path,
- visualization_name=visualization_name, query_args=kwargs,
- embedded=embedded, shared_vars={}, **resources )
- #NOTE: passing *unparsed* kwargs as query_args
- #NOTE: shared_vars is a dictionary for shared data in the template
+ visualization_name=visualization_name, visualization_display_name=visualization_display_name,
+ title=title, saved_visualization=visualization, visualization_id=encoded_visualization_id,
+ embedded=embedded, query=kwargs, vars={}, config=config, **resources )
+ #NOTE: passing *unparsed* kwargs as query
+ #NOTE: vars is a dictionary for shared data in the template
# this feels hacky to me but it's what mako recommends:
# http://docs.makotemplates.org/en/latest/runtime.html
+ #TODO: should vars contain all the passed in arguments? is that even necessary?
#TODO: embedded
except Exception, exception:
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.
1
0
07 Feb '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/a11c52513e44/
Changeset: a11c52513e44
User: dan
Date: 2014-02-07 16:11:28
Summary: merge next-stable
Affected #: 0 files
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.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/aea2ff5acd68/
Changeset: aea2ff5acd68
Branch: next-stable
User: dan
Date: 2014-02-07 16:10:27
Summary: Generate tool_runner url using keywords in data_manager/index.mako.
Affected #: 1 file
diff -r aefe5cf99f92eaa3a401f3f7a9edf9dce4027ed1 -r aea2ff5acd6805ad10db0442cc159f7e4e67a541 templates/webapps/galaxy/data_manager/index.mako
--- a/templates/webapps/galaxy/data_manager/index.mako
+++ b/templates/webapps/galaxy/data_manager/index.mako
@@ -19,7 +19,7 @@
<ul>
%for data_manager_id, data_manager in data_managers.data_managers.iteritems():
<li>
- <a href="${ h.url_for( 'tool_runner?tool_id=%s' % ( data_manager.tool.id ) ) }"><strong>${ data_manager.name | h }</strong></a> - ${ data_manager.description | h }
+ <a href="${ h.url_for( controller='tool_runner', action='index', tool_id=data_manager.tool.id ) }"><strong>${ data_manager.name | h }</strong></a> - ${ data_manager.description | h }
</li><p/>
%endfor
https://bitbucket.org/galaxy/galaxy-central/commits/5641ce15708a/
Changeset: 5641ce15708a
User: dan
Date: 2014-02-07 16:10:27
Summary: Generate tool_runner url using keywords in data_manager/index.mako.
Affected #: 1 file
diff -r c162bff2090eb4a986e9059b45109fac172239dd -r 5641ce15708a6af203fcbfa0b99c37df9c18dbf3 templates/webapps/galaxy/data_manager/index.mako
--- a/templates/webapps/galaxy/data_manager/index.mako
+++ b/templates/webapps/galaxy/data_manager/index.mako
@@ -19,7 +19,7 @@
<ul>
%for data_manager_id, data_manager in data_managers.data_managers.iteritems():
<li>
- <a href="${ h.url_for( 'tool_runner?tool_id=%s' % ( data_manager.tool.id ) ) }"><strong>${ data_manager.name | h }</strong></a> - ${ data_manager.description | h }
+ <a href="${ h.url_for( controller='tool_runner', action='index', tool_id=data_manager.tool.id ) }"><strong>${ data_manager.name | h }</strong></a> - ${ data_manager.description | h }
</li><p/>
%endfor
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.
1
0
commit/galaxy-central: dannon: Add README to static/style to document requirements for building galaxy styles.
by commits-noreply@bitbucket.org 07 Feb '14
by commits-noreply@bitbucket.org 07 Feb '14
07 Feb '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/c162bff2090e/
Changeset: c162bff2090e
User: dannon
Date: 2014-02-07 15:43:14
Summary: Add README to static/style to document requirements for building galaxy styles.
Affected #: 1 file
diff -r 5e2a4255cf09e7423d8fc41c99a9386bca9e0851 -r c162bff2090eb4a986e9059b45109fac172239dd static/style/README
--- /dev/null
+++ b/static/style/README
@@ -0,0 +1,26 @@
+GALAXY STYLE BUILDING
+
+Requirements:
+node.js http://nodejs.org
+grunt http://gruntjs.com
+phantomjs http://phantomjs.org
+ *phantomjs can be replaced with other libraries if you know what you're
+ doing (it's used as an engine for sprite building), but it's the easiest to
+ get up and running.
+
+After node, grunt, and phantomjs are all installed (and on your path):
+
+ 1: navigate to the <galaxy>/static/style directory (where this file is)
+ 2: execute 'npm install'
+ 3: execute 'grunt'
+
+The 'npm install' command will install the required modules for building galaxy
+into the node_modules directory here. If phantomjs isn't correctly installed
+and on your path prior to running 'npm install', you'll see this error message:
+
+ Warning: Sorry, no spritesmith engine could be loaded for your machine.
+ Please be sure you have installed canvas or gm. Use --force to continue.
+
+If this happens, you'll need to rerun 'npm install' after 1) ensuring phantomjs
+is on your path and 2) removing the local node_modules directory (rm -rf
+node_modules).
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.
1
0