/*
controller.js -- The GBrowse controller object
Lincoln Stein <lincoln.stein@gmail.com>
Ben Faga <ben.faga@gmail.com>
$Id$
Indentation courtesy of Emacs javascript-mode
(http://mihai.bazon.net/projects/emacs-javascript-mode/javascript.el)
Method structure
- Class Utility Methods
- DOM Utility Methods
- Update Section Methods
- Kick-off Render Methods
- Retrieve Rendered Track Methods
- Track Configure Methods
- Plugin Methods
- Upload File Methods
*/
// Element Names
var track_listing_id = 'tracks_panel';
var overview_container_id = 'overview_panels';
var region_container_id = 'region_panels';
var detail_container_id = 'detail_panels';
var external_utility_div_id = 'external_utility_div';
var page_title_id = 'page_title';
var galaxy_form_id = 'galaxy_form';
var visible_span_id = 'span';
var search_form_objects_id = 'search_form_objects';
var userdata_table_id = 'userdata_table_div';
var custom_tracks_id = 'custom_tracks';
var community_tracks_id = 'community_tracks';
var snapshot_table_id = 'snapshots_page';
var GlobalDrag = {};
// Sorta Constants
var expired_limit = 1;
var GBrowseController = Class.create({
// Class Utility Methods ******************************************
set_url:
function(url) {
this.url = url;
},
initialize:
function () {
var url = document.URL;
var q = url.indexOf('?');
if (q >= 0) url = url.substr(0,q);
this.url = url;
this.gbtracks = new Hash(); // maps track ids to gbtrack objects
this.segment_observers = new Hash();
this.retrieve_tracks = new Hash();
this.ret_track_time_key = new Hash();
this.gbtrackname_to_id = new Hash(); // maps unique track ids to names
// segment_info holds the information used in rubber.js
this.segment_info;
this.last_update_key;
this.tabs;
//global config variables
this.globals = new Hash();
},
set_globals:
function(obj) {
for (var name in obj) {
this.globals.set(name, obj[name])
}
var me = this;
//generate *_url accessors
var mk_url_accessor = function( conf_name, acc_name) {
me[acc_name] = function(relpath) { return this.globals.get(conf_name) + '/' + relpath; }
};
mk_url_accessor( 'buttons', 'button_url' );
mk_url_accessor( 'balloons', 'balloon_url' );
mk_url_accessor( 'openid', 'openid_url' );
mk_url_accessor( 'js', 'js_url' );
mk_url_accessor( 'gbrowse_help', 'help_url' );
mk_url_accessor( 'stylesheet', 'stylesheet_url' );
},
reset_after_track_load:
// This may be a little overkill to run these after every track update but
// since there is no "We're completely done with all the track updates for the
// moment" hook, I don't know of another way to make sure the tracks become
// draggable again
function () {
if ( null != $(overview_container_id) ){
create_drag(overview_container_id,'track');
}
if ( null != $(region_container_id) ){
create_drag(region_container_id,'track');
}
if ( null != $(detail_container_id) ){
create_drag(detail_container_id,'track');
}
TrackPan.update_draggables();
updateRuler();
Controller.update_ghosts();
},
register_track:
function (track_id,track_name,track_type,track_section) {
if (this.gbtracks.get(track_id) != null)
return;
var gbtrack = new GBrowseTrack(track_id,track_name,track_type,track_section);
this.gbtracks.set(track_id,gbtrack);
if (this.gbtrackname_to_id.get(track_name) == null)
this.gbtrackname_to_id.set(track_name,new Hash());
this.gbtrackname_to_id.get(track_name).set(track_id,1);
if (track_type=="scale_bar"){
return gbtrack;
}
this.retrieve_tracks.set(track_id,true);
return gbtrack;
}, // end register_track
unregister_track:
function (track_name) {
var id_hash = this.gbtrackname_to_id.get(track_name);
if (id_hash != null) {
var ids = id_hash.keys();
for (var i=0;i<ids.length;i++) this.gbtracks.unset(ids[i]);
this.gbtrackname_to_id.unset(track_name);
}
}, // end unregister_track
unregister_gbtrack:
function (gbtrack) {
var id_hash = this.gbtrackname_to_id.get(gbtrack.track_name);
if (id_hash != null)
id_hash.unset(gbtrack.track_id);
if (this.gbtracks.get(gbtrack.track_id) != null)
this.gbtracks.unset(gbtrack.track_id);
},
// Pass an iterator to execute something on each track
// Call as this.each_track(function(track){}) to iterate over all gbtracks.
// Call as this.each_track('track_name',function(track){}) to iterate over
// all tracks named 'track_name'
each_track:
function () {
if (arguments.length >= 2) {
var track_name = arguments[0];
var iterator = arguments[1];
if (this.gbtracks.get(track_name) != null) {
iterator(this.gbtracks.get(track_name));
} else if (this.gbtrackname_to_id.get(track_name) != null) {
var ids = this.gbtrackname_to_id.get(track_name).keys();
for (var i=0;i<ids.length;i++)
iterator(this.gbtracks.get(ids[i])); // I don't know why each() doesn't work here
}
} else {
var iterator = arguments[0];
this.gbtracks.keys().each(
function(key) {
var track=this.gbtracks.get(key);
iterator(track);
}, this
);
}
}, //end each_track
track_exists:
function (track_name) {
return this.gbtrackname_to_id.get(track_name) != null;
},
// Sets the time key for the tracks so we know if one is outdated
set_last_update_keys:
function (track_keys) {
var last_update_key = create_time_key();
this.last_update_key = last_update_key;
var track_key_hash = new Hash;
for (var track_name in track_keys)
track_key_hash.set(track_name,1);
this.each_track(function(gbtrack) {
if (track_key_hash.get(gbtrack.track_name) != null) {
gbtrack.set_last_update_key(last_update_key);
}
});
}, // end set_last_update_keys
// Sets the time key for a single track
set_last_update_key:
function (gbtrack) {
var last_update_key = create_time_key();
gbtrack.set_last_update_key(this.last_update_key);
},
// Hides the detail tracks in case they shouldn't be displayed for some reason
hide_detail_tracks:
function () {
this.each_track(function(gbtrack) {
if (gbtrack.is_standard_track() && gbtrack.track_section == 'detail'){
$(gbtrack.track_image_id).setOpacity(0.2);
}
});
},
// DOM Utility Methods ********************************************
wipe_div:
function(div_id) {
$(div_id).innerHTML = '';
},
update_scale_bar:
function (bar_obj) {
var image_id = bar_obj.image_id;
var image = $(image_id);
image.setStyle({
background: "url(" + bar_obj.url + ") top left no-repeat",
width: bar_obj.width+'px',
height: bar_obj.height+'px',
display: 'block',
cursor: 'text'
});
image.setOpacity(1);
image.ancestors()[0].setStyle({width: bar_obj.width+'px'});
},
append_child_from_html:
function (child_html,parent_obj,onTop) {
//Append new html to the appropriate section This is a bit cludgy but we
//create a temp element, read the html into it and then move the div
//element back out. This keeps the other tracks intact.
if (onTop == null) onTop = false;
// var tmp_element = document.createElement("tmp_element");
// tmp_element.innerHTML = child_html;
var tracks = parent_obj.getElementsByClassName('track');
var first_track = tracks[0];
if (onTop && first_track != null)
first_track.insert({before: child_html});
else
parent_obj.insert(child_html);
},
// Update Section Methods *****************************************
update_sections:
function(section_names, param_str, scroll_there, spin, onSuccessFunc) {
if (param_str==null){
param_str = '';
}
if (scroll_there==null) {
scroll_there=false;
}
if (spin == null) {
spin = false;
}
var request_str = "action=update_sections" + param_str;
for (var i = 0; i < section_names.length; i++) {
if (spin)
$(section_names[i]).update(new Element("img",
{src: Controller.button_url('spinner.gif'),
alt: Controller.translate('WORKING')}) );
request_str += "§ion_names="+section_names[i];
}
new Ajax.Request(Controller.url, {
method: 'post',
parameters: request_str,
onSuccess: function(transport) {
var results = transport.responseJSON;
var section_html = results.section_html;
for (var section_name in section_html) {
$(section_name).setOpacity(1.0);
html = section_html[section_name];
$(section_name).innerHTML = html;
if (scroll_there)
new Effect.ScrollTo(section_name);
if ( (section_name==search_form_objects_id) && ($('autocomplete_choices') != null)
||(section_name==community_tracks_id) && ($('autocomplete_upload_filter') != null))
initAutocomplete();
if (section_name == page_title_id)
document.title = $(section_name).innerHTML;
if (onSuccessFunc != null) {
onSuccessFunc();
}
}
checkSummaries();
}
});
},
// General option setting used for grid, cache and tooltips ******
set_display_option:
function(option, value) {
var param = {action: 'set_display_option'};
param[option] = value;
new Ajax.Request(Controller.url,
{
method: 'post',
parameters: param,
onComplete: function (transport) {
Controller.refresh_tracks(); // causes an elegant panel refresh
}
}
);
},
// Signal Change to Server Methods ********************************
set_track_visibility:
function(track_id,visible) {
var gbtrack = this.gbtracks.get(track_id);
if (gbtrack == null) return;
var track_name = gbtrack.track_name;
this.each_track(track_id,function(gbtrack) {
new Ajax.Request(Controller.url,{
method: 'post',
parameters: {
action: 'set_track_visibility',
visible: visible,
track_name: track_name
},
onSuccess: function(transport) {
if (visible && gbtrack.get_last_update_key() == null || gbtrack.get_last_update_key() < Controller.last_update_key) {
Controller.rerender_track(gbtrack.track_id);
}
}
});
});
},
// Kick-off Render Methods ****************************************
reload_panels: // called only to reload a snapshot
function (segment_info) {
if (segment_info != null) {
this.segment_info = segment_info;
}
new Ajax.Request(Controller.url,{
method: 'post',
parameters: {
action: 'render_panels'
},
onSuccess: function(transport) {
$('panels').innerHTML = transport.responseText;
$('panels').innerHTML.evalScripts();
var color = overviewObject.background;
var unit = overviewObject.unit;
var divider = overviewObject.divider;
['overview','region','detail'].each(function(e) {
var m = $(e+'SelectMenu');
if (m) m.remove();
});
this.initialize_page();
set_dragcolors(color);
set_dragunits(unit,divider);
}
});
}, //end render_panels()
update_coordinates:
function (action, snapshot) {
if (snapshot == null){
snapshot = false;
}
// submit search form if the detail panel doesn't exist
if ( null == $(detail_container_id) ){
document.searchform.force_submit.value = 1;
document.searchform.submit();
}
this.busy();
TrackPan.grey_out_markers();
$('ruler_handle').setOpacity(0.3);
$('detail_scale_scale').setOpacity(0.3);
//Grey out image
this.each_track(function(gbtrack) {
if ($(gbtrack.track_image_id) != null)
$(gbtrack.track_image_id).setOpacity(0.3);
// else
// alert('REPORT THIS BUG: element '+gbtrack.track_image_id+' should not be null');
});
new Ajax.Request(Controller.url, {
method: 'post',
parameters: {
action: 'navigate', // 'action' triggers an async call
navigate: action, // 'navigate' is an argument passed to the async routine
view_start: Math.round(TrackPan.get_start()),
view_stop: Math.round(TrackPan.get_stop()),
snapshot: snapshot // Is true when a snapshot is being loaded, and false otherwise
},
onSuccess: function(transport) {
var results = transport.responseJSON;
Controller.segment_info = results.segment_info;
var track_keys = results.track_keys;
var overview_scale_bar_hash = results.overview_scale_bar;
var region_scale_bar_hash = results.region_scale_bar;
var detail_scale_bar_hash = results.detail_scale_bar;
Controller.set_last_update_keys(track_keys);
if (overview_scale_bar_hash) {
Controller.update_scale_bar(overview_scale_bar_hash);
$('overview_panels').setStyle({width: overview_scale_bar_hash.width+'px'});
}
if (region_scale_bar_hash) {
Controller.update_scale_bar(region_scale_bar_hash);
$('region_panels').setStyle({width: region_scale_bar_hash.width+'px'});
}
if (detail_scale_bar_hash) {
Controller.update_scale_bar(detail_scale_bar_hash);
$('detail_panels').setStyle({width: detail_scale_bar_hash.view_width+'px'});
var detail_width = Controller.segment_info.detail_width;
var details_pixel_ratio = Controller.segment_info.details_pixel_ratio;
var scale_width = Math.round(detail_scale_bar_hash.scale_size / details_pixel_ratio) - 1;
var scale_left = Math.round((Controller.segment_info.overview_width - scale_width) / 2) - 30;
var scale_div = $("detail_scale_scale");
if (scale_div) {
scale_div.innerHTML = detail_scale_bar_hash.scale_label +
"<span style='display: inline-block; margin-left:5px; margin-bottom:4px; border-left: 1px solid black; border-right: 1px solid black; height:8px'>" +
"<span style='display: inline-block; border-bottom: 1px solid black; height: 4px; width:" + scale_width + "px'</span></span>";
$('ruler_handle').setOpacity(1);
scale_div.setOpacity(1);
scale_div.setStyle({ left: scale_left+'px' });
}
}
// Update the segment sections
Controller.update_sections( Controller.segment_observers.keys());
$('details_msg').innerHTML = results.details_msg;
Controller.get_multiple_tracks(track_keys);
TrackPan.make_details_draggable();
if (results.display_details == 0)
Controller.hide_detail_tracks();
} // end onSuccess
}); // end Ajax.Request
}, // end update_coordinates
refresh_tracks:
function (snapshot) {
// The snapshot flag indicates that a snapshot is being loaded and refreshes the tracks accordingly
Controller.update_coordinates('left 0', snapshot);
}, // end refresh_tracks
scroll:
function (direction,length_units) {
if (!TrackPan.scroll(direction,length_units)) {
var view_length = (parseInt(Controller.segment_info.detail_stop) - parseInt(Controller.segment_info.detail_start)) / parseFloat(Controller.segment_info.details_mult);
this.update_coordinates(direction + ' ' + Math.round(length_units*view_length));
}
}, // end scroll
add_track:
function(track_name, onSuccessFunc, force) {
var track_names = new Array(track_name);
this.add_tracks(track_names,onSuccessFunc,force);
},
add_tracks:
function(track_names, onSuccessFunc, force, onTop) {
if (force == null)
force = false;
var request_str = "action=add_tracks";
var found_track = false;
track_names.reverse();
for (var i = 0; i < track_names.length; i++) {
var track_name = track_names[i];
if ( force || !this.track_exists(track_name) ) {
request_str += "&track_names="+encodeURIComponent(track_name);
found_track = true;
}
}
//track_names.reverse();
if (!found_track) return false;
this.busy();
new Ajax.Request(Controller.url, {
method: 'post',
parameters: request_str,
onSuccess: function(transport) {
if (onSuccessFunc!=null)
onSuccessFunc();
var results = transport.responseJSON;
var track_data = results.track_data;
var track_keys = new Object();
var get_tracks = false;
// for (var i = 0; i < track_names.length; i++) {
// var ret_track_id = track_names[i];
for (var ret_track_id in track_data) {
if (Controller.gbtracks.get(ret_track_id) != null)
continue; //oops already know this one
var this_track_data = track_data[ret_track_id];
var ret_gbtrack = Controller.register_track (
ret_track_id,
this_track_data.track_name,
'standard',
this_track_data.track_section
);
Controller.set_last_update_key(ret_gbtrack)
var html = this_track_data.track_html;
var panel_id = this_track_data.panel_id;
//Controller.append_child_from_html(html,$(panel_id),onTop);
// force true - experimental
Controller.append_child_from_html(html,$(panel_id),true);
if (this_track_data.display_details == 0) {
$(ret_gbtrack.track_image_id).setOpacity(0);
} else {
track_keys[ret_track_id]=this_track_data.track_key;
get_tracks = true;
}
} // end for (var ret_track_name...
if( get_tracks)
Controller.get_multiple_tracks(track_keys);
else
Controller.idle();
}
});
return true;
},
busy:
function() {
var bi = $('busy_indicator');
var top = document.body.scrollTop||document.documentElement.scrollTop;
bi.style.top =5+"px";
bi.style.left =5+"px";
bi.show();
},
idle:
function() {
var bi = $('busy_indicator');
bi.hide();
},
rerender_track:
function(track_id,scroll_there,nocache) {
if (scroll_there == null)
scroll_there = false;
if (nocache == null)
nocache = false;
this.busy();
this.each_track(track_id,function(gbtrack) {
$(gbtrack.track_image_id).setOpacity(0.3);
Controller.set_last_update_key(gbtrack);
new Ajax.Request(Controller.url,{
method: 'post',
parameters: {
action: 'rerender_track',
track_id: gbtrack.track_id,
nocache: nocache ? 1 : 0
},
onSuccess: function(transport) {
var results = transport.responseJSON;
var track_keys = results.track_keys;
if (results.display_details == 0) {
$(gbtrack.track_image_id).setOpacity(0);
} else {
time_key = create_time_key();
for (var track_id in track_keys) {
Controller.retrieve_tracks.set(gbtrack.track_id,true);
Controller.ret_track_time_key.set(gbtrack.track_id,time_key);
} // end for
Controller.get_remaining_tracks(track_keys,1000,1.1,time_key);
}
if (scroll_there)
new Effect.ScrollTo(gbtrack.track_div_id,{queue: 'end'});
} // end onSuccess
}); // end Ajax.Request
}); //end each_track()
}, // end rerender_track
scroll_to_matching_track:
function scroll_to_matching_track(partial_name) {
var tracks = $$('span.titlebar');
var first_track = tracks.find(function(n) {
var result = n.id.include(partial_name)
&& n.visible();
return result;
});
if (first_track != null) {
new Effect.ScrollTo(first_track,
{ queue:'end' }
);
}
},
delete_track:
function(track_name) {
this.each_track(track_name,function(gb) {
Controller.unregister_track(gb.track_name);
actually_remove(gb.track_div_id);
});
}, // end delete_track
// Retrieve Rendered Track Methods ********************************
get_multiple_tracks:
function (track_keys) {
time_key = create_time_key();
$H(track_keys).keys().each(
function(track_id) {
Controller.retrieve_tracks.set(track_id,true);
Controller.ret_track_time_key.set(track_id,time_key);
}
);
this.get_remaining_tracks(track_keys,1000,1.1,time_key);
},
// Time key is there to make sure separate calls don't trounce each other
// Only Update if the tracks time_key matches the method's
get_remaining_tracks:
function (track_keys,time_out,decay,time_key){
var track_ids = [];
var finished = true;
var track_key_str = '';
this.busy();
this.retrieve_tracks.keys().each(
function(track_id) {
if(Controller.retrieve_tracks.get(track_id)){
if (Controller.ret_track_time_key.get(track_id) == time_key){
track_ids.push(track_id);
track_key_str += '&tk_'+escape(track_id)+"="+track_keys[track_id];
finished = false;
}
}
}
);
if (finished) {
this.idle();
return;
}
new Ajax.Request(Controller.url, {
method: 'post',
parameters: $H({
action: 'retrieve_multiple',
track_ids: track_ids
}).toQueryString() + track_key_str,
onSuccess: function(transport) {
var continue_requesting = false;
var results = transport.responseJSON;
var track_html_hash = results.track_html;
for (var track_id in track_html_hash) {
track_html = track_html_hash[track_id];
var gbtrack = Controller.gbtracks.get(track_id);
if (Controller.ret_track_time_key.get(track_id) == time_key){
track_div = document.getElementById(gbtrack.track_div_id);
if (track_html.substring(0,18) == "<!-- AVAILABLE -->"){
track_div.innerHTML = track_html;
gbtrack.track_resolved();
Controller.retrieve_tracks.set(track_id,false);
} else if (track_html.substring(0,16) == "<!-- EXPIRED -->") {
Controller.retrieve_tracks.set(track_id,false);
if (gbtrack.expired_count >= expired_limit){
$(gbtrack.track_image_id).setOpacity(0);
} else {
gbtrack.increment_expired_count();
Controller.rerender_track(track_id);
}
} else if (track_html.substring(0,14) == "<!-- ERROR -->") {
gbtrack.track_resolved();
Controller.retrieve_tracks.set(track_id,false);
track_div.innerHTML = track_html;
} else if (track_html.substring(0,16) == "<!-- DEFUNCT -->") {
gbtrack.track_resolved();
Controller.retrieve_tracks.set(track_id,false);
$(gbtrack.track_image_id).setOpacity(0);
} else {
continue_requesting = true;
}
}
}
if (continue_requesting) {
setTimeout( function() {
Controller.get_remaining_tracks(track_keys,time_out*decay,decay,time_key)
} ,time_out);
} else {
var area = $$('area[inline!=1]');
area.invoke('observe', 'mousedown', Controller.feature_callback);
area.invoke('observe', 'mouseover', Controller.feature_callback);
Controller.idle();
}
Controller.reset_after_track_load();
} // end onSuccess
}); // end new Ajax.Request
}, // end get_remaining_tracks
// Track Configure Methods ****************************************
reconfigure_track:
function(track_id, form_element, mode) {
if (form_element==null)
form_element = $("track_config_form");
else
Element.extend(form_element);
if (mode==null)
mode='normal';
new Ajax.Request(Controller.url, {
method: 'post',
parameters: form_element.serialize() +"&"+ $H({
action: 'reconfigure_track',
track: track_id,
mode: mode
}).toQueryString(),
onSuccess: function(transport) {
var track_div_id = Controller.gbtracks.get(track_id).track_div_id;
Balloon.prototype.hideTooltip(1);
Controller.rerender_track(track_id,false,false);
Controller.update_sections(new Array(track_listing_id),null,null,true);
} // end onSuccess
});
},
toggle_subtrack_overlapping:
function(track_id,overlapping) {
new Ajax.Request(Controller.url, {
method: 'post',
parameters: {
action: 'track_overlapping',
track: track_id,
overlapping: overlapping }
})
},
filter_subtrack:
function(track_id, form_element) {
new Ajax.Request(Controller.url, {
method: 'post',
parameters: form_element.serialize() +"&"+ $H({
action: 'filter_subtrack',
track: track_id
}).toQueryString(),
onSuccess: function(transport) {
Balloon.prototype.hideTooltip(1);
Controller.rerender_track(track_id,true);
}, // end onSuccess
onFailure: function(transport) {
Balloon.prototype.hideTooltip(1);
}
});
},
// Plugin Methods *************************************************
configure_plugin:
function(div_id) {
var plugin_base = document.pluginform.plugin.value;
this.update_sections(new Array(div_id), '&plugin_base='+plugin_base,null,null,true);
new Effect.ScrollTo(div_id);
new Effect.BlindDown(div_id);
},
reconfigure_plugin:
function(plugin_action,plugin_track_id,pc_div_id,plugin_type,form_element,synchronous) {
if (form_element==null)
form_element = $("configure_plugin");
else
Element.extend(form_element);
if (synchronous == null)
synchronous = false;
new Ajax.Request(Controller.url, {
method: 'post',
asynchronous: !synchronous,
parameters: form_element.serialize() +"&"+ $H({
plugin_action: plugin_action,
action: 'reconfigure_plugin'
}).toQueryString(),
onSuccess: function(transport) {
if (pc_div_id != null) Controller.wipe_div(pc_div_id);
if (plugin_type == 'annotator'){
Controller.each_track(plugin_track_id,function(gbtrack) {
Controller.rerender_track(gbtrack.track_id,true);
});
}
else if (plugin_type == 'filter') {
Controller.update_coordinates("reload segment");
Controller.update_sections(new Array(track_listing_id),'',1,false);
}
else if (plugin_type == 'highlighter') {
Controller.update_coordinates("reload segment");
}
else if (plugin_type == 'trackfilter') {
var e = $(track_listing_id);
e.hide();
e.setOpacity(0.3);
e.show();
Controller.update_sections(new Array(track_listing_id),'',1,false);
}
} // end onSuccess
});
},
plugin_authenticate:
function(configuration_form,message_area) {
message_area.innerHTML='<img src="'+this.button_url('spinner.gif')+'" />'+Controller.translate('WORKING');
this.reconfigure_plugin('Configure',null,null,'authorizer',configuration_form,true);
var remember = $('authenticate_remember_me').getValue() == 'on';
new Ajax.Request(Controller.url, {
method: 'post',
parameters: {
action: 'plugin_authenticate'
},
onSuccess: function (t) {
var results = t.responseJSON;
if (results.userOK) {
Balloon.prototype.hideTooltip(1);
// the definition for this call is in login.js
login_get_account(results.username,results.sessionid,remember,false);
}
else
message_area.innerHTML='<div style="color:red">'+results.message+'</div>';
}
});
},
plugin_go:
function(plugin_base,plugin_type,plugin_action,source) {
var select_box = document.pluginform.plugin;
var track_name = 'plugin:'+select_box.options[select_box.selectedIndex].value;
if (plugin_type == 'annotator'){
ShowHideTrack(track_name,true);
} else if (plugin_type == 'dumper') {
var loc_str = "?plugin="+plugin_base+";plugin_action="+encodeURI(plugin_action);
loc_str += ';view_start=' + TrackPan.get_start();
loc_str += ';view_stop=' + TrackPan.get_stop();
if(source == 'config'){
var form_element = $("configure_plugin");
window.open(loc_str + ";" + form_element.serialize());
} else{
window.open(loc_str);
}
} else if (plugin_type == 'filter'){
$('configure_plugin_button').click();
// this.reconfigure_plugin(this.translate('CONFIGURE_PLUGIN'),
// track_name,
// plugin_configure_div,
// 'filter');
} else if (plugin_type == 'finder'){
input = document.createElement("input");
input.setAttribute("type", "hidden");
input.setAttribute("name", "plugin_action");
input.setAttribute("id","plugin_action");
input.setAttribute("value", plugin_action);
document.getElementById("configure_plugin").appendChild(input);
plugin = document.createElement("input");
plugin.setAttribute("type", "hidden");
plugin.setAttribute("name", "plugin");
plugin.setAttribute("id","plugin");
plugin.setAttribute("value", plugin_base);
document.getElementById("configure_plugin").appendChild(plugin);
document.configure_plugin.submit();
}
}, // end plugin_go
cancel_upload:
function(destination, upload_id) {
new Ajax.Updater(
destination,
document.URL,
{
method: 'post',
parameters: {
action: 'cancel_upload',
upload_id: upload_id
}
});
},
// Utility methods *********************************
show_error:
function (message, details) {
var outerdiv = $('errordiv');
var innerdiv = $('errormsg');
var detailsdiv = $('errordetails');
if (innerdiv != null) {
var caption = detailsdiv.visible() ? Controller.translate('HIDE_DETAILS') : Controller.translate('SHOW_DETAILS');
innerdiv.innerHTML = message +
' <span id="detailscaption" class="clickable" style="font-size:12pt" onClick="Controller.show_hide_errordetails()">'
+caption
+'</span>';
}
if (detailsdiv != null) {
detailsdiv.innerHTML = details;
}
if (outerdiv != null) {
scroll(0,0);
new Effect.BlindDown(outerdiv);
}
},
hide_error:
function () {
var outerdiv = $('errordiv');
var detailsdiv = $('errordetails');
if (outerdiv != null)
new Effect.BlindUp(outerdiv);
return false;
},
show_hide_errordetails:
function () {
var detailsdiv = $('errordetails');
var caption = $('detailscaption');
if (detailsdiv == null) return;
if (caption == null) return;
if (detailsdiv.visible()) {
caption.innerHTML=Controller.translate('SHOW_DETAILS');
detailsdiv.hide();
} else {
caption.innerHTML=Controller.translate('HIDE_DETAILS');
detailsdiv.show();
}
},
show_info_message:
function (action,width) {
if (width == null) width=300;
var dim = document.body.getDimensions();
var info = $('info_container');
if (info == null) {
info = new Element('div',{id:'info_container'});
info.setStyle({position: 'absolute',
zIndex: 100000,
display:'none'});
document.body.appendChild(info);
var abs_container = new Element('div',{id:'abs_info_container'});
info.appendChild(abs_container);
var content = new Element('div',{id:'info_content'});
abs_container.appendChild(content);
var button = new Element('input',{type:'button',
style:'float:right',
id:'info_button',
value:this.translate('OK')});
// button.insert(' '); // needed to close tag
button.observe('click',function(ev) {$('info_container').hide()});
content.insert({after:button});
}
// double containment necessary to avoid IE zindex bug!
$('info_container').setStyle( {
position: 'absolute',
backgroundColor: 'white',
top: '50px',
left: Math.round((dim.width-width)/4)+'px',
zIndex: 10000,
width: width+'px'
});
$('abs_info_container').setStyle( {
position: 'absolute',
backgroundColor: 'white',
top: '0px',
left: '0px',
border: 'double',
padding: '5px',
zIndex: 100001
});
new Ajax.Updater('info_content',
document.URL,{
parameters: { action: action } ,
onSuccess: function (t) { $('info_container').show()}
});
},
edit_upload_title:
function(upload_name, title_element) {
if (title_element == null)
return true;
Element.extend(title_element);
title_element.setStyle({
border: '2px',
cursor: 'text',
inset: 'black',
backgroundColor:'beige',
padding:'5px 5px 5px 5px'
});
// var r = document.createRange();
// r.selectNodeContents(title_element);
// window.getSelection().addRange(r);
Event.observe(title_element, 'keypress', this.set_upload_title);
Event.observe(title_element, 'blur', this.set_upload_title);
},
set_upload_title:
function(event) {
var title_element = event.findElement();
if (event.type == 'blur' || event.keyCode == Event.KEY_RETURN || event.keyCode == Event.KEY_ESC) {
var file = title_element.up("div.custom_track").id;
var title = title_element.innerHTML;
title_element.update(new Element("img", {src: Controller.button_url('spinner.gif'), alt: Controller.translate('WORKING')}) );
if (event.keyCode == Event.KEY_ESC) { // backing out changes
var sections = new Array(custom_tracks_id);
if (using_database()) sections.push(community_tracks_id);
Controller.update_sections(sections);
} else {
new Ajax.Request(Controller.url, {
method: 'post',
parameters:{
action: 'set_upload_title',
upload_id: file,
title: title
},
onSuccess: function(transport) {
var sections = new Array(custom_tracks_id, track_listing_id);
if (using_database()) sections.push(community_tracks_id);
Controller.update_sections(sections);
}
});
}
title_element.stopObserving('keypress');
title_element.stopObserving('blur');
title_element.blur();
return true;
}
return false;
},
edit_upload_track_key:
function(upload_name, upload_label, key_element) {
if (key_element == null)
return true;
Element.extend(key_element);
key_element.setStyle({
border: '2px',
cursor: 'text',
inset: 'black',
backgroundColor:'beige',
padding:'5px 5px 5px 5px'
});
key_element.upload_name = upload_name;
key_element.upload_label = upload_label;
key_element.onclick = null;
Event.stopObserving(key_element);
Event.observe(key_element, 'keypress', this.set_upload_track_key);
Event.observe(key_element, 'blur', this.set_upload_track_key);
},
set_upload_track_key:
function(event) {
var key_element = event.findElement();
var file = key_element.upload_name;
var label = key_element.upload_label;
var value = key_element.innerHTML.stripTags();
// the following code is cut-and-paste from set_upload_title and is largely redundant
if (event.type == 'blur' || event.keyCode == Event.KEY_RETURN || event.keyCode == Event.KEY_ESC) {
key_element.update(new Element("img", {src: Controller.button_url('spinner.gif'), alt: Controller.translate('WORKING')}) );
if (event.keyCode == Event.KEY_ESC) { // backing out
var sections = new Array(custom_tracks_id);
if (using_database()) sections.push(community_tracks_id);
Controller.update_sections(sections);
} else {
new Ajax.Request(Controller.url, {
method: 'post',
parameters:{
action: 'set_upload_track_key',
upload_id: file,
label: label,
key: value
},
onSuccess: function(transport) {
var new_key = transport.responseText;
var sections = new Array(custom_tracks_id, track_listing_id);
if (using_database()) sections.push(community_tracks_id);
Controller.update_sections(sections);
var titles = $(label+'_title').select('span.drag_region');
titles[0].innerHTML='<b>'+new_key+'</b>';
}
});
}
key_element.stopObserving('keypress');
key_element.stopObserving('blur');
key_element.blur();
return true;
}
return false;
},
edit_upload_description:
function(upload_name,container_element) {
if (container_element == null)
return true;
container_element.setStyle({
border: '2px',
cursor: 'text',
inset: 'black',
backgroundColor:'beige',
padding:'5px 5px 5px 5px'
});
Event.observe(container_element,'keypress',this.set_upload_description);
Event.observe(container_element,'blur',this.set_upload_description);
},
set_upload_description:
function(event) {
var description_box = event.findElement();
if (event.type=='blur' || event.keyCode==Event.KEY_RETURN) {
var file = description_box.up("div.custom_track").id;
var description = description_box.innerHTML;
description_box.update(new Element("img", {src: Controller.button_url('spinner.gif'), alt: Controller.translate('WORKING')}) );
new Ajax.Request(Controller.url, {
method: 'post',
parameters:{
action: 'set_upload_description',
upload_id: file,
description: description
},
onSuccess: function(transport) {
var sections = new Array(custom_tracks_id);
if (using_database())
sections.push(community_tracks_id);
Controller.update_sections(sections);
}
});
description_box.stopObserving('keypress');
description_box.stopObserving('blur');
description_box.blur();
return true;
}
if (event.keyCode==Event.KEY_ESC) {
description_box.update(new Element("img", {src: Controller.button_url('spinner.gif'), alt: "Working..."}) );
var sections = new Array(custom_tracks_id);
if (using_database())
sections.push(community_tracks_id);
Controller.update_sections(sections);
description_box.stopObserving('keypress');
description_box.stopObserving('blur');
description_box.blur();
return true;
}
return false;
},
// downloadUserTrackSource() is called to populate the user track edit field
// with source or configuration data for the track
downloadUserTrackSource:
function (destination, fileid, sourceFile) {
new Ajax.Request (
document.URL,
{
method: 'post',
parameters: {
userdata_download: sourceFile,
track: fileid
},
onSuccess: function (t) { $(destination).value=t.responseText }
}
);
},
// This is really messed up and should be moved to ajax_upload.js
_modifyUserTrackSource:
function (param, statusElement, displayWhenDone) {
var upload_id = 'upload_' + Math.floor(Math.random() * 99999);
param.upload_id = upload_id;
new Ajax.Request(Controller.url, {
method: 'post',
parameters: param,
onCreate: function() {
if ($(statusElement) != null) {
$(statusElement).update();
$(statusElement).insert(new Element("div", {id: upload_id + "_form"}));
$(statusElement).insert(new Element("div", {id: upload_id + "_status"}));
}
startAjaxUpload(upload_id);
},
onSuccess: function (transport) {
var r = transport.responseJSON;
Controller.add_tracks(r.tracks,null,false,true);
r.tracks.each(function(t) {
Controller.rerender_track(t,true,true);
});
var updater = Ajax_Status_Updater.get(upload_id);
if (updater != null)
updater.stop();
var sections = new Array(custom_tracks_id, track_listing_id);
if (using_database())
sections.push(community_tracks_id);
Controller.update_sections(sections);
if (displayWhenDone != null && displayWhenDone)
Controller.select_tab('main_page');
},
onComplete: function (transport) {
var updater = Ajax_Status_Updater.get(upload_id);
if (updater != null) updater.stop();
if (statusElement != null) cleanRemove(statusElement);
}
});
},
// uploadUserTrackSource() is called to submit a user track edit field
// to the server
uploadUserTrackSource:
function (sourceField, fileid, sourceFile, editElement) {
this._modifyUserTrackSource(
{
action: 'modifyUserData',
file: fileid,
sourceFile: sourceFile,
data: $F(sourceField)
},
editElement
);
},
// mirrorTrackSource() is called to mirror a URL to a track
mirrorTrackSource:
function (sourceURL, fileid, statusElement, displayWhenDone, forcejson) {
if (forcejson == null) forcejson=false;
this._modifyUserTrackSource(
{
action: 'upload_file',
mirror_url: sourceURL,
overwrite: 1,
forcejson: forcejson
},
statusElement,
displayWhenDone
);
},
// monitor_upload is redundant and needs to be refactored
// the idea is to register a new upload
monitor_upload:
function (upload_id,upload_name) {
new Ajax.Request(Controller.url, {
method: 'post',
parameters: {
action: 'register_upload',
upload_id: upload_id,
upload_name: upload_name
}
});
startAjaxUpload(upload_id);
},
select_tab:
function (tab_id,animate) {
if (this.tabs != null) {
this.tabs.select_tab(tab_id,animate);
}
},
wait_for_initialization:
function (html, callback) {
$('main').setOpacity(0.2);
var html = '<div id="dialog_123" style="position:absolute; left:50px; top:50px; border:5px double black; background: wheat; z-index:100">'
+ html
+'</div>';
$('main').insert({before:html});
if (callback) callback();
$('main').setOpacity(1.0);
$('dialog_123').remove();
},
ghost_track:
function (el) {
var d = el.ancestors().find(function (a) {return a.hasClassName("track")});
if (d.style.position == 'absolute') {
this.popin_ghost(d);
} else {
this.popout_ghost(d);
}
},
popin_ghost:
function(d) {
// ghost track pops back in...
var current_top = d.cumulativeOffset().top;
Sortable.destroy(d.id);
d._draggable.destroy();
d.style.position='relative';
d.style.left='0px';
d.style.top='0px';
d.style.height='auto';
d.style.outlineStyle="";
d.style.opacity=1.0;
d.select('img.pin_button').each(function(a) {
a.src=Controller.button_url('pop_out.png');
});
d.select('span.titlebar_pinned').each(function(a) {
a.removeClassName('titlebar_pinned');
a.addClassName('titlebar');
});
d.removeClassName('ghost');
var list = d.parentNode.select('div.track');
var tracks = list[0].select('div.track');
var overlapping_element = tracks[0];
var direction = {before:d};
tracks.each(function(a) {
var el_top = a.cumulativeOffset().top;
var el_bottom = el_top + a.getHeight();
if (el_top <= current_top) { // pops in here
var middle = (el_top+el_bottom)/2;
var dir = middle < current_top ? 'after' : 'before';
direction[dir]=d;
overlapping_element=a;
}
});
overlapping_element.insert(direction);
var container = d.parentNode;
var drag = create_drag(container);
Sortable.sortables[container.id].onUpdate();
},
popout_ghost:
function(d) {
// ghost track pops out
var container = d.parentNode;
var left = d.cumulativeOffset().left;
var top = d.cumulativeOffset().top;
console.log(container.id);
d.absolutize();
d.style.left=left+'px';
d.style.top =top+'px';
d.addClassName('ghost');
Sortable.destroy(container.id);
container.parentNode.insert(d);
create_drag(container.id,'track');
d.select('span.titlebar').each(function(a) {
a.removeClassName('titlebar');
a.addClassName('titlebar_pinned');
d.style.height = (d.getHeight() + a.getHeight()) + 'px';
});
d.style.outlineStyle="double";
d.style.opacity=0.90;
d.select('img.pin_button').each(function(a) {
a.src=Controller.button_url('pop_in.png');
});
d._draggable= new Draggable(d,{constraint:'vertical',
scroll: window,
zindex: 1000});
return true;
},
update_ghosts:
function() {
$$('div.ghost').each(function(d) {
d.select('span.titlebar').each(function(a) {
a.removeClassName('titlebar');
a.addClassName('titlebar_pinned');
d.style.height='auto';
});
});
},
// Looks up a key in the language table. If not found, checks the defaults table.
// If the translation contains %s, substitutes additional parameters for each occurance of %s (in order)
// Usage: Controller.translate(key, [...])
translate:
function () {
var key = arguments[0];
var result;
if (typeof language_table !== undefined && language_table) { //If the language table exists
if (language_table[key]) {
result = language_table[key];
} else if (default_language_table[key]) {
result = default_language_table[key];
} else {
alert('The key "' + key + '" was not found in the translation table.');
}
} else {
alert('The key "' + key + '" could not be translated because the translation table is not loaded.');
}
for (var i = 1; i < arguments.length; i++) {
result = result.replace(/(%s)/i, arguments[i]);
}
return result;
},
make_image_link:
function(type) {
var url = '?make_image=' + type;
url += ';view_start=' + TrackPan.get_start();
url += ';view_stop=' + TrackPan.get_stop();
url += ';view_width=' + TrackPan.width_no_pad;
window.open(url);
},
gbgff_link:
function(url) {
url += ';q=' + TrackPan.ref + ':' + TrackPan.get_start() + '..' + TrackPan.get_stop();
window.location.href = url;
},
bookmark_link:
function() {
var url = '?action=bookmark';
url += ';view_start=' + TrackPan.get_start();
url += ';view_stop=' + TrackPan.get_stop();
window.location.href = url;
},
get_sharing:
function(event, url) {
GBox.showTooltip(event, url);
Controller.update_sections(new Array(custom_tracks_id));
},
feature_callback:
function(event) {
// find track information
var element = event.element();
var track_element = element.up(1).id;
var track = track_element.sub(/^track_/,'');
var javascript;
new Ajax.Request(Controller.url, {
method: 'post',
asynchronous: false,
parameters:{
action: 'get_feature_info',
event_type: event.type,
track: track,
dbid: element.getAttribute('dbid'),
feature_id: element.getAttribute('fid')
},
onSuccess: function(transport) {
javascript = transport.responseText;
}
});
if (javascript != '')
eval(javascript);
}
});
var Controller = new GBrowseController; // singleton
function using_database() {
return ($(community_tracks_id))? true : false;
}
function initialize_page() {
if (Controller == null) Controller = new GBrowseController;
// This oddity prevents ?id=logout from appearing in the url box.
// Otherwise whenever user reloads he is logged out :-(
var index;
if ((index = location.href.indexOf('?id=logout')) >= 0)
location.href=location.href.substr(0,index);
checkSummaries();
// These statements initialize the tabbing
var tabs = $$("div.tabbody").collect( function(element) {
return element.id;
});
if (Controller.tabs == null)
Controller.tabs = new TabbedSection(tabs);
//event handlers
[page_title_id,visible_span_id,galaxy_form_id,search_form_objects_id].each(function(el) {
if ($(el) != null) {
Controller.segment_observers.set(el,1);
}
});
// The next statement is to avoid the scalebars from being "out of sync"
// when manually advancing the browser with its forward/backward buttons.
// Unfortunately it causes an infinite loop when there are multiple regions!
if ($(detail_container_id) != null)
Controller.refresh_tracks();
// These statements get the rubberbanding running.
Overview.prototype.initialize();
Region.prototype.initialize();
Details.prototype.initialize();
if ($('autocomplete_choices') != null)
initAutocomplete();
Sortable.create('snapshotTable',{tag:'div',only:'draggable'});
Controller.checkSnapshot()
}
// set the colors for the rubberband regions
function set_dragcolors(color) {
if (overviewObject != null)
overviewObject.background = color;
if (regionObject != null)
regionObject.background = color;
if (detailsObject != null)
detailsObject.background = color;
}
// set the units for the rubberband regions
function set_dragunits(unit,divider) {
if (unit == null) unit = 'bp';
if (divider == null) divider = 1;
if (overviewObject != null) {
overviewObject.unit = unit;
overviewObject.divider = divider;
}
if (regionObject != null) {
regionObject.unit = unit;
regionObject.divider = divider;
}
if (detailsObject != null) {
detailsObject.unit = unit;
detailsObject.divider = divider;
}
}
function create_time_key () {
time_obj = new Date();
return time_obj.getTime();
}
//prototype's remove function doesn't actually remove the element from
//reachability.
function actually_remove (element_name) {
var element = $(element_name);
if (element==null) return;
var parent = element.parentNode;
parent.removeChild(element);
}
function create_drag (div_name) {
GlobalDrag[div_name] = div_name;
return Sortable.create(
div_name,
{
tag: 'div',
constraint: 'vertical',
only: 'track',
handle: 'drag_region',
scroll: window,
onUpdate: function() {
var items = $(div_name).select('[class="track"]');
var ids = items.map(function(e){return e.id});
ids = ids.map(function(i) {return 'label[]='+escape(i.sub(/^track_/,''))});
var postData= ids.join('&')+';action=change_track_order';
new Ajax.Request(document.URL,{method:'post',postBody:postData});
}
}
);
}