diff --git a/client/galaxy/scripts/mvc/grid/grid-view.js b/client/galaxy/scripts/mvc/grid/grid-view.js
index 34d433e7d3b..234c16cd8e7 100644
--- a/client/galaxy/scripts/mvc/grid/grid-view.js
+++ b/client/galaxy/scripts/mvc/grid/grid-view.js
@@ -3,7 +3,11 @@
jQuery.ajaxSettings.traditional = true;
// dependencies
-define(['mvc/grid/grid-model', 'mvc/grid/grid-template', 'mvc/ui'], function(GridModel, Templates) {
+define([
+ 'mvc/grid/grid-model',
+ 'mvc/grid/grid-template',
+ "mvc/ui/popup-menu"
+], function(GridModel, Templates, PopupMenu) {
// grid view
return Backbone.View.extend({
@@ -661,4 +665,4 @@ return Backbone.View.extend({
}
});
-});
\ No newline at end of file
+});
diff --git a/client/galaxy/scripts/mvc/history/history-panel-edit.js b/client/galaxy/scripts/mvc/history/history-panel-edit.js
index 1de436f46e0..a9f40ca616d 100644
--- a/client/galaxy/scripts/mvc/history/history-panel-edit.js
+++ b/client/galaxy/scripts/mvc/history/history-panel-edit.js
@@ -8,6 +8,7 @@ define([
"mvc/tags",
"mvc/annotations",
"jq-plugins/ui/fa-icon-button",
+ "mvc/ui/popup-menu",
"utils/localization"
], function(
HPANEL,
@@ -19,6 +20,7 @@ define([
TAGS,
ANNOTATIONS,
faIconButton,
+ PopupMenu,
_l
){
/* =============================================================================
diff --git a/client/galaxy/scripts/mvc/history/options-menu.js b/client/galaxy/scripts/mvc/history/options-menu.js
index 1e358d180a0..1113f90d81e 100644
--- a/client/galaxy/scripts/mvc/history/options-menu.js
+++ b/client/galaxy/scripts/mvc/history/options-menu.js
@@ -1,7 +1,8 @@
define([
+ "mvc/ui/popup-menu",
"mvc/base-mvc",
"utils/localization"
-], function( BASE_MVC, _l ){
+], function( PopupMenu, BASE_MVC, _l ){
// ============================================================================
var menu = [
{
diff --git a/client/galaxy/scripts/mvc/ui.js b/client/galaxy/scripts/mvc/ui.js
index 3093d7ba85f..ab813d2df52 100644
--- a/client/galaxy/scripts/mvc/ui.js
+++ b/client/galaxy/scripts/mvc/ui.js
@@ -169,320 +169,3 @@ var create_icon_buttons_menu = function(config, global_config)
// return menu
return new IconButtonMenuView( {collection: buttons} );
};
-
-// =============================================================================
-/**
- *
- */
-var Grid = Backbone.Collection.extend({
-
-});
-
-/**
- *
- */
-var GridView = Backbone.View.extend({
-
-});
-
-// =============================================================================
-/**
- * view for a popup menu
- */
-var PopupMenu = Backbone.View.extend({
-//TODO: maybe better as singleton off the Galaxy obj
- /** Cache the desired button element and options, set up the button click handler
- * NOTE: attaches this view as HTML/jQ data on the button for later use.
- */
- initialize: function( $button, options ){
- // default settings
- this.$button = $button;
- if( !this.$button.size() ){
- this.$button = $( '
' );
- }
- this.options = options || [];
-
- // set up button click -> open menu behavior
- var menu = this;
- this.$button.click( function( event ){
- // if there's already a menu open, remove it
- $( '.popmenu-wrapper' ).remove();
- menu._renderAndShow( event );
- return false;
- });
- },
-
- // render the menu, append to the page body at the click position, and set up the 'click-away' handlers, show
- _renderAndShow: function( clickEvent ){
- this.render();
- this.$el.appendTo( 'body' ).css( this._getShownPosition( clickEvent )).show();
- this._setUpCloseBehavior();
- },
-
- // render the menu
- // this menu doesn't attach itself to the DOM ( see _renderAndShow )
- render: function(){
- // render the menu body absolute and hidden, fill with template
- this.$el.addClass( 'popmenu-wrapper' ).hide()
- .css({ position : 'absolute' })
- .html( this.template( this.$button.attr( 'id' ), this.options ));
-
- // set up behavior on each link/anchor elem
- if( this.options.length ){
- var menu = this;
- //precondition: there should be one option per li
- this.$el.find( 'li' ).each( function( i, li ){
- var option = menu.options[i];
-
- // if the option has 'func', call that function when the anchor is clicked
- if( option.func ){
- $( this ).children( 'a.popupmenu-option' ).click( function( event ){
- option.func.call( menu, event, option );
- // bubble up so that an option click will call the close behavior
- //return false;
- });
- }
- });
- }
- return this;
- },
-
- template : function( id, options ){
- return [
- ''
- ].join( '' );
- },
-
- _templateOptions : function( options ){
- if( !options.length ){
- return '(no options)';
- }
- return _.map( options, function( option ){
- if( option.divider ){
- return '';
- } else if( option.header ){
- return [ '', option.html, '' ].join( '' );
- }
- var href = option.href || 'javascript:void(0);',
- target = ( option.target )?( ' target="' + option.target + '"' ):( '' ),
- check = ( option.checked )?( '' ):( '' );
- return [
- ''
- ].join( '' );
- }).join( '' );
- },
-
- // get the absolute position/offset for the menu
- _getShownPosition : function( clickEvent ){
-
- // display menu horiz. centered on click...
- var menuWidth = this.$el.width();
- var x = clickEvent.pageX - menuWidth / 2 ;
-
- // adjust to handle horiz. scroll and window dimensions ( draw entirely on visible screen area )
- x = Math.min( x, $( document ).scrollLeft() + $( window ).width() - menuWidth - 5 );
- x = Math.max( x, $( document ).scrollLeft() + 5 );
- return {
- top: clickEvent.pageY,
- left: x
- };
- },
-
- // bind an event handler to all available frames so that when anything is clicked
- // the menu is removed from the DOM and the event handler unbinds itself
- _setUpCloseBehavior: function(){
- var menu = this;
-//TODO: alternately: focus hack, blocking overlay, jquery.blockui
-
- // function to close popup and unbind itself
- function closePopup( event ){
- $( document ).off( 'click.close_popup' );
- if( window.parent !== window ){
- try {
- $( window.parent.document ).off( "click.close_popup" );
- } catch( err ){}
- } else {
- try {
- $( 'iframe#galaxy_main' ).contents().off( "click.close_popup" );
- } catch( err ){}
- }
- menu.remove();
- }
-
- $( 'html' ).one( "click.close_popup", closePopup );
- if( window.parent !== window ){
- try {
- $( window.parent.document ).find( 'html' ).one( "click.close_popup", closePopup );
- } catch( err ){}
- } else {
- try {
- $( 'iframe#galaxy_main' ).contents().one( "click.close_popup", closePopup );
- } catch( err ){}
- }
- },
-
- // add a menu option/item at the given index
- addItem: function( item, index ){
- // append to end if no index
- index = ( index >= 0 ) ? index : this.options.length;
- this.options.splice( index, 0, item );
- return this;
- },
-
- // remove a menu option/item at the given index
- removeItem: function( index ){
- if( index >=0 ){
- this.options.splice( index, 1 );
- }
- return this;
- },
-
- // search for a menu option by its html
- findIndexByHtml: function( html ){
- for( var i = 0; i < this.options.length; i++ ){
- if( _.has( this.options[i], 'html' ) && ( this.options[i].html === html )){
- return i;
- }
- }
- return null;
- },
-
- // search for a menu option by its html
- findItemByHtml: function( html ){
- return this.options[( this.findIndexByHtml( html ))];
- },
-
- // string representation
- toString: function(){
- return 'PopupMenu';
- }
-});
-/** shortcut to new for when you don't need to preserve the ref */
-PopupMenu.create = function _create( $button, options ){
- return new PopupMenu( $button, options );
-};
-
-// -----------------------------------------------------------------------------
-// the following class functions are bridges from the original make_popupmenu and make_popup_menus
-// to the newer backbone.js PopupMenu
-
-/** Create a PopupMenu from simple map initial_options activated by clicking button_element.
- * Converts initial_options to object array used by PopupMenu.
- * @param {jQuery|DOMElement} button_element element which, when clicked, activates menu
- * @param {Object} initial_options map of key -> values, where
- * key is option text, value is fn to call when option is clicked
- * @returns {PopupMenu} the PopupMenu created
- */
-PopupMenu.make_popupmenu = function( button_element, initial_options ){
- var convertedOptions = [];
- _.each( initial_options, function( optionVal, optionKey ){
- var newOption = { html: optionKey };
-
- // keys with null values indicate: header
- if( optionVal === null ){ // !optionVal? (null only?)
- newOption.header = true;
-
- // keys with function values indicate: a menu option
- } else if( jQuery.type( optionVal ) === 'function' ){
- newOption.func = optionVal;
- }
- //TODO:?? any other special optionVals?
- // there was no divider option originally
- convertedOptions.push( newOption );
- });
- return new PopupMenu( $( button_element ), convertedOptions );
-};
-
-/** Find all anchors in $parent (using selector) and covert anchors into a PopupMenu options map.
- * @param {jQuery} $parent the element that contains the links to convert to options
- * @param {String} selector jq selector string to find links
- * @returns {Object[]} the options array to initialize a PopupMenu
- */
-//TODO: lose parent and selector, pass in array of links, use map to return options
-PopupMenu.convertLinksToOptions = function( $parent, selector ){
- $parent = $( $parent );
- selector = selector || 'a';
- var options = [];
- $parent.find( selector ).each( function( elem, i ){
- var option = {}, $link = $( elem );
-
- // convert link text to the option text (html) and the href into the option func
- option.html = $link.text();
- if( $link.attr( 'href' ) ){
- var linkHref = $link.attr( 'href' ),
- linkTarget = $link.attr( 'target' ),
- confirmText = $link.attr( 'confirm' );
-
- option.func = function(){
- // if there's a "confirm" attribute, throw up a confirmation dialog, and
- // if the user cancels - do nothing
- if( ( confirmText ) && ( !confirm( confirmText ) ) ){ return; }
-
- // if there's no confirm attribute, or the user accepted the confirm dialog:
- switch( linkTarget ){
- // relocate the center panel
- case '_parent':
- window.parent.location = linkHref;
- break;
-
- // relocate the entire window
- case '_top':
- window.top.location = linkHref;
- break;
-
- // relocate this panel
- default:
- window.location = linkHref;
- }
- };
- }
- options.push( option );
- });
- return options;
-};
-
-/** Create a single popupmenu from existing DOM button and anchor elements
- * @param {jQuery} $buttonElement the element that when clicked will open the menu
- * @param {jQuery} $menuElement the element that contains the anchors to convert into a menu
- * @param {String} menuElementLinkSelector jq selector string used to find anchors to be made into menu options
- * @returns {PopupMenu} the PopupMenu (Backbone View) that can render, control the menu
- */
-PopupMenu.fromExistingDom = function( $buttonElement, $menuElement, menuElementLinkSelector ){
- $buttonElement = $( $buttonElement );
- $menuElement = $( $menuElement );
- var options = PopupMenu.convertLinksToOptions( $menuElement, menuElementLinkSelector );
- // we're done with the menu (having converted it to an options map)
- $menuElement.remove();
- return new PopupMenu( $buttonElement, options );
-};
-
-/** Create all popupmenus within a document or a more specific element
- * @param {DOMElement} parent the DOM element in which to search for popupmenus to build (defaults to document)
- * @param {String} menuSelector jq selector string to find popupmenu menu elements (defaults to "div[popupmenu]")
- * @param {Function} buttonSelectorBuildFn the function to build the jq button selector.
- * Will be passed $menuElement, parent.
- * (Defaults to return '#' + $menuElement.attr( 'popupmenu' ); )
- * @returns {PopupMenu[]} array of popupmenus created
- */
-PopupMenu.make_popup_menus = function( parent, menuSelector, buttonSelectorBuildFn ){
- parent = parent || document;
- // orig. Glx popupmenu menus have a (non-std) attribute 'popupmenu'
- // which contains the id of the button that activates the menu
- menuSelector = menuSelector || 'div[popupmenu]';
- // default to (orig. Glx) matching button to menu by using the popupmenu attr of the menu as the id of the button
- buttonSelectorBuildFn = buttonSelectorBuildFn || function( $menuElement, parent ){
- return '#' + $menuElement.attr( 'popupmenu' );
- };
-
- // aggregate and return all PopupMenus
- var popupMenusCreated = [];
- $( parent ).find( menuSelector ).each( function(){
- var $menuElement = $( this ),
- $buttonElement = $( parent ).find( buttonSelectorBuildFn( $menuElement, parent ) );
- popupMenusCreated.push( PopupMenu.fromDom( $buttonElement, $menuElement ) );
- $buttonElement.addClass( 'popup' );
- });
- return popupMenusCreated;
-};
diff --git a/client/galaxy/scripts/mvc/ui/popup-menu.js b/client/galaxy/scripts/mvc/ui/popup-menu.js
new file mode 100644
index 00000000000..1e51addd5c6
--- /dev/null
+++ b/client/galaxy/scripts/mvc/ui/popup-menu.js
@@ -0,0 +1,311 @@
+define([
+ //jquery
+ //backbone
+], function(){
+// =============================================================================
+/**
+ * view for a popup menu
+ */
+var PopupMenu = Backbone.View.extend({
+//TODO: maybe better as singleton off the Galaxy obj
+ /** Cache the desired button element and options, set up the button click handler
+ * NOTE: attaches this view as HTML/jQ data on the button for later use.
+ */
+ initialize: function( $button, options ){
+ // default settings
+ this.$button = $button;
+ if( !this.$button.size() ){
+ this.$button = $( '' );
+ }
+ this.options = options || [];
+
+ // set up button click -> open menu behavior
+ var menu = this;
+ this.$button.click( function( event ){
+ // if there's already a menu open, remove it
+ $( '.popmenu-wrapper' ).remove();
+ menu._renderAndShow( event );
+ return false;
+ });
+ },
+
+ // render the menu, append to the page body at the click position, and set up the 'click-away' handlers, show
+ _renderAndShow: function( clickEvent ){
+ this.render();
+ this.$el.appendTo( 'body' ).css( this._getShownPosition( clickEvent )).show();
+ this._setUpCloseBehavior();
+ },
+
+ // render the menu
+ // this menu doesn't attach itself to the DOM ( see _renderAndShow )
+ render: function(){
+ // render the menu body absolute and hidden, fill with template
+ this.$el.addClass( 'popmenu-wrapper' ).hide()
+ .css({ position : 'absolute' })
+ .html( this.template( this.$button.attr( 'id' ), this.options ));
+
+ // set up behavior on each link/anchor elem
+ if( this.options.length ){
+ var menu = this;
+ //precondition: there should be one option per li
+ this.$el.find( 'li' ).each( function( i, li ){
+ var option = menu.options[i];
+
+ // if the option has 'func', call that function when the anchor is clicked
+ if( option.func ){
+ $( this ).children( 'a.popupmenu-option' ).click( function( event ){
+ option.func.call( menu, event, option );
+ // bubble up so that an option click will call the close behavior
+ //return false;
+ });
+ }
+ });
+ }
+ return this;
+ },
+
+ template : function( id, options ){
+ return [
+ ''
+ ].join( '' );
+ },
+
+ _templateOptions : function( options ){
+ if( !options.length ){
+ return '(no options)';
+ }
+ return _.map( options, function( option ){
+ if( option.divider ){
+ return '';
+ } else if( option.header ){
+ return [ '', option.html, '' ].join( '' );
+ }
+ var href = option.href || 'javascript:void(0);',
+ target = ( option.target )?( ' target="' + option.target + '"' ):( '' ),
+ check = ( option.checked )?( '' ):( '' );
+ return [
+ ''
+ ].join( '' );
+ }).join( '' );
+ },
+
+ // get the absolute position/offset for the menu
+ _getShownPosition : function( clickEvent ){
+
+ // display menu horiz. centered on click...
+ var menuWidth = this.$el.width();
+ var x = clickEvent.pageX - menuWidth / 2 ;
+
+ // adjust to handle horiz. scroll and window dimensions ( draw entirely on visible screen area )
+ x = Math.min( x, $( document ).scrollLeft() + $( window ).width() - menuWidth - 5 );
+ x = Math.max( x, $( document ).scrollLeft() + 5 );
+ return {
+ top: clickEvent.pageY,
+ left: x
+ };
+ },
+
+ // bind an event handler to all available frames so that when anything is clicked
+ // the menu is removed from the DOM and the event handler unbinds itself
+ _setUpCloseBehavior: function(){
+ var menu = this;
+//TODO: alternately: focus hack, blocking overlay, jquery.blockui
+
+ // function to close popup and unbind itself
+ function closePopup( event ){
+ $( document ).off( 'click.close_popup' );
+ if( window.parent !== window ){
+ try {
+ $( window.parent.document ).off( "click.close_popup" );
+ } catch( err ){}
+ } else {
+ try {
+ $( 'iframe#galaxy_main' ).contents().off( "click.close_popup" );
+ } catch( err ){}
+ }
+ menu.remove();
+ }
+
+ $( 'html' ).one( "click.close_popup", closePopup );
+ if( window.parent !== window ){
+ try {
+ $( window.parent.document ).find( 'html' ).one( "click.close_popup", closePopup );
+ } catch( err ){}
+ } else {
+ try {
+ $( 'iframe#galaxy_main' ).contents().one( "click.close_popup", closePopup );
+ } catch( err ){}
+ }
+ },
+
+ // add a menu option/item at the given index
+ addItem: function( item, index ){
+ // append to end if no index
+ index = ( index >= 0 ) ? index : this.options.length;
+ this.options.splice( index, 0, item );
+ return this;
+ },
+
+ // remove a menu option/item at the given index
+ removeItem: function( index ){
+ if( index >=0 ){
+ this.options.splice( index, 1 );
+ }
+ return this;
+ },
+
+ // search for a menu option by its html
+ findIndexByHtml: function( html ){
+ for( var i = 0; i < this.options.length; i++ ){
+ if( _.has( this.options[i], 'html' ) && ( this.options[i].html === html )){
+ return i;
+ }
+ }
+ return null;
+ },
+
+ // search for a menu option by its html
+ findItemByHtml: function( html ){
+ return this.options[( this.findIndexByHtml( html ))];
+ },
+
+ // string representation
+ toString: function(){
+ return 'PopupMenu';
+ }
+});
+/** shortcut to new for when you don't need to preserve the ref */
+PopupMenu.create = function _create( $button, options ){
+ return new PopupMenu( $button, options );
+};
+
+// -----------------------------------------------------------------------------
+// the following class functions are bridges from the original make_popupmenu and make_popup_menus
+// to the newer backbone.js PopupMenu
+
+/** Create a PopupMenu from simple map initial_options activated by clicking button_element.
+ * Converts initial_options to object array used by PopupMenu.
+ * @param {jQuery|DOMElement} button_element element which, when clicked, activates menu
+ * @param {Object} initial_options map of key -> values, where
+ * key is option text, value is fn to call when option is clicked
+ * @returns {PopupMenu} the PopupMenu created
+ */
+PopupMenu.make_popupmenu = function( button_element, initial_options ){
+ var convertedOptions = [];
+ _.each( initial_options, function( optionVal, optionKey ){
+ var newOption = { html: optionKey };
+
+ // keys with null values indicate: header
+ if( optionVal === null ){ // !optionVal? (null only?)
+ newOption.header = true;
+
+ // keys with function values indicate: a menu option
+ } else if( jQuery.type( optionVal ) === 'function' ){
+ newOption.func = optionVal;
+ }
+ //TODO:?? any other special optionVals?
+ // there was no divider option originally
+ convertedOptions.push( newOption );
+ });
+ return new PopupMenu( $( button_element ), convertedOptions );
+};
+
+/** Find all anchors in $parent (using selector) and covert anchors into a PopupMenu options map.
+ * @param {jQuery} $parent the element that contains the links to convert to options
+ * @param {String} selector jq selector string to find links
+ * @returns {Object[]} the options array to initialize a PopupMenu
+ */
+//TODO: lose parent and selector, pass in array of links, use map to return options
+PopupMenu.convertLinksToOptions = function( $parent, selector ){
+ $parent = $( $parent );
+ selector = selector || 'a';
+ var options = [];
+ $parent.find( selector ).each( function( elem, i ){
+ var option = {}, $link = $( elem );
+
+ // convert link text to the option text (html) and the href into the option func
+ option.html = $link.text();
+ if( $link.attr( 'href' ) ){
+ var linkHref = $link.attr( 'href' ),
+ linkTarget = $link.attr( 'target' ),
+ confirmText = $link.attr( 'confirm' );
+
+ option.func = function(){
+ // if there's a "confirm" attribute, throw up a confirmation dialog, and
+ // if the user cancels - do nothing
+ if( ( confirmText ) && ( !confirm( confirmText ) ) ){ return; }
+
+ // if there's no confirm attribute, or the user accepted the confirm dialog:
+ switch( linkTarget ){
+ // relocate the center panel
+ case '_parent':
+ window.parent.location = linkHref;
+ break;
+
+ // relocate the entire window
+ case '_top':
+ window.top.location = linkHref;
+ break;
+
+ // relocate this panel
+ default:
+ window.location = linkHref;
+ }
+ };
+ }
+ options.push( option );
+ });
+ return options;
+};
+
+/** Create a single popupmenu from existing DOM button and anchor elements
+ * @param {jQuery} $buttonElement the element that when clicked will open the menu
+ * @param {jQuery} $menuElement the element that contains the anchors to convert into a menu
+ * @param {String} menuElementLinkSelector jq selector string used to find anchors to be made into menu options
+ * @returns {PopupMenu} the PopupMenu (Backbone View) that can render, control the menu
+ */
+PopupMenu.fromExistingDom = function( $buttonElement, $menuElement, menuElementLinkSelector ){
+ $buttonElement = $( $buttonElement );
+ $menuElement = $( $menuElement );
+ var options = PopupMenu.convertLinksToOptions( $menuElement, menuElementLinkSelector );
+ // we're done with the menu (having converted it to an options map)
+ $menuElement.remove();
+ return new PopupMenu( $buttonElement, options );
+};
+
+/** Create all popupmenus within a document or a more specific element
+ * @param {DOMElement} parent the DOM element in which to search for popupmenus to build (defaults to document)
+ * @param {String} menuSelector jq selector string to find popupmenu menu elements (defaults to "div[popupmenu]")
+ * @param {Function} buttonSelectorBuildFn the function to build the jq button selector.
+ * Will be passed $menuElement, parent.
+ * (Defaults to return '#' + $menuElement.attr( 'popupmenu' ); )
+ * @returns {PopupMenu[]} array of popupmenus created
+ */
+PopupMenu.make_popup_menus = function( parent, menuSelector, buttonSelectorBuildFn ){
+ parent = parent || document;
+ // orig. Glx popupmenu menus have a (non-std) attribute 'popupmenu'
+ // which contains the id of the button that activates the menu
+ menuSelector = menuSelector || 'div[popupmenu]';
+ // default to (orig. Glx) matching button to menu by using the popupmenu attr of the menu as the id of the button
+ buttonSelectorBuildFn = buttonSelectorBuildFn || function( $menuElement, parent ){
+ return '#' + $menuElement.attr( 'popupmenu' );
+ };
+
+ // aggregate and return all PopupMenus
+ var popupMenusCreated = [];
+ $( parent ).find( menuSelector ).each( function(){
+ var $menuElement = $( this ),
+ $buttonElement = $( parent ).find( buttonSelectorBuildFn( $menuElement, parent ) );
+ popupMenusCreated.push( PopupMenu.fromDom( $buttonElement, $menuElement ) );
+ $buttonElement.addClass( 'popup' );
+ });
+ return popupMenusCreated;
+};
+
+
+// =============================================================================
+ return PopupMenu;
+});
+
diff --git a/static/scripts/mvc/grid/grid-view.js b/static/scripts/mvc/grid/grid-view.js
index 34d433e7d3b..234c16cd8e7 100644
--- a/static/scripts/mvc/grid/grid-view.js
+++ b/static/scripts/mvc/grid/grid-view.js
@@ -3,7 +3,11 @@
jQuery.ajaxSettings.traditional = true;
// dependencies
-define(['mvc/grid/grid-model', 'mvc/grid/grid-template', 'mvc/ui'], function(GridModel, Templates) {
+define([
+ 'mvc/grid/grid-model',
+ 'mvc/grid/grid-template',
+ "mvc/ui/popup-menu"
+], function(GridModel, Templates, PopupMenu) {
// grid view
return Backbone.View.extend({
@@ -661,4 +665,4 @@ return Backbone.View.extend({
}
});
-});
\ No newline at end of file
+});
diff --git a/static/scripts/mvc/history/history-panel-edit.js b/static/scripts/mvc/history/history-panel-edit.js
index 1de436f46e0..a9f40ca616d 100644
--- a/static/scripts/mvc/history/history-panel-edit.js
+++ b/static/scripts/mvc/history/history-panel-edit.js
@@ -8,6 +8,7 @@ define([
"mvc/tags",
"mvc/annotations",
"jq-plugins/ui/fa-icon-button",
+ "mvc/ui/popup-menu",
"utils/localization"
], function(
HPANEL,
@@ -19,6 +20,7 @@ define([
TAGS,
ANNOTATIONS,
faIconButton,
+ PopupMenu,
_l
){
/* =============================================================================
diff --git a/static/scripts/mvc/history/options-menu.js b/static/scripts/mvc/history/options-menu.js
index 1e358d180a0..1113f90d81e 100644
--- a/static/scripts/mvc/history/options-menu.js
+++ b/static/scripts/mvc/history/options-menu.js
@@ -1,7 +1,8 @@
define([
+ "mvc/ui/popup-menu",
"mvc/base-mvc",
"utils/localization"
-], function( BASE_MVC, _l ){
+], function( PopupMenu, BASE_MVC, _l ){
// ============================================================================
var menu = [
{
diff --git a/static/scripts/mvc/ui.js b/static/scripts/mvc/ui.js
index 3093d7ba85f..ab813d2df52 100644
--- a/static/scripts/mvc/ui.js
+++ b/static/scripts/mvc/ui.js
@@ -169,320 +169,3 @@ var create_icon_buttons_menu = function(config, global_config)
// return menu
return new IconButtonMenuView( {collection: buttons} );
};
-
-// =============================================================================
-/**
- *
- */
-var Grid = Backbone.Collection.extend({
-
-});
-
-/**
- *
- */
-var GridView = Backbone.View.extend({
-
-});
-
-// =============================================================================
-/**
- * view for a popup menu
- */
-var PopupMenu = Backbone.View.extend({
-//TODO: maybe better as singleton off the Galaxy obj
- /** Cache the desired button element and options, set up the button click handler
- * NOTE: attaches this view as HTML/jQ data on the button for later use.
- */
- initialize: function( $button, options ){
- // default settings
- this.$button = $button;
- if( !this.$button.size() ){
- this.$button = $( '' );
- }
- this.options = options || [];
-
- // set up button click -> open menu behavior
- var menu = this;
- this.$button.click( function( event ){
- // if there's already a menu open, remove it
- $( '.popmenu-wrapper' ).remove();
- menu._renderAndShow( event );
- return false;
- });
- },
-
- // render the menu, append to the page body at the click position, and set up the 'click-away' handlers, show
- _renderAndShow: function( clickEvent ){
- this.render();
- this.$el.appendTo( 'body' ).css( this._getShownPosition( clickEvent )).show();
- this._setUpCloseBehavior();
- },
-
- // render the menu
- // this menu doesn't attach itself to the DOM ( see _renderAndShow )
- render: function(){
- // render the menu body absolute and hidden, fill with template
- this.$el.addClass( 'popmenu-wrapper' ).hide()
- .css({ position : 'absolute' })
- .html( this.template( this.$button.attr( 'id' ), this.options ));
-
- // set up behavior on each link/anchor elem
- if( this.options.length ){
- var menu = this;
- //precondition: there should be one option per li
- this.$el.find( 'li' ).each( function( i, li ){
- var option = menu.options[i];
-
- // if the option has 'func', call that function when the anchor is clicked
- if( option.func ){
- $( this ).children( 'a.popupmenu-option' ).click( function( event ){
- option.func.call( menu, event, option );
- // bubble up so that an option click will call the close behavior
- //return false;
- });
- }
- });
- }
- return this;
- },
-
- template : function( id, options ){
- return [
- ''
- ].join( '' );
- },
-
- _templateOptions : function( options ){
- if( !options.length ){
- return '(no options)';
- }
- return _.map( options, function( option ){
- if( option.divider ){
- return '';
- } else if( option.header ){
- return [ '', option.html, '' ].join( '' );
- }
- var href = option.href || 'javascript:void(0);',
- target = ( option.target )?( ' target="' + option.target + '"' ):( '' ),
- check = ( option.checked )?( '' ):( '' );
- return [
- ''
- ].join( '' );
- }).join( '' );
- },
-
- // get the absolute position/offset for the menu
- _getShownPosition : function( clickEvent ){
-
- // display menu horiz. centered on click...
- var menuWidth = this.$el.width();
- var x = clickEvent.pageX - menuWidth / 2 ;
-
- // adjust to handle horiz. scroll and window dimensions ( draw entirely on visible screen area )
- x = Math.min( x, $( document ).scrollLeft() + $( window ).width() - menuWidth - 5 );
- x = Math.max( x, $( document ).scrollLeft() + 5 );
- return {
- top: clickEvent.pageY,
- left: x
- };
- },
-
- // bind an event handler to all available frames so that when anything is clicked
- // the menu is removed from the DOM and the event handler unbinds itself
- _setUpCloseBehavior: function(){
- var menu = this;
-//TODO: alternately: focus hack, blocking overlay, jquery.blockui
-
- // function to close popup and unbind itself
- function closePopup( event ){
- $( document ).off( 'click.close_popup' );
- if( window.parent !== window ){
- try {
- $( window.parent.document ).off( "click.close_popup" );
- } catch( err ){}
- } else {
- try {
- $( 'iframe#galaxy_main' ).contents().off( "click.close_popup" );
- } catch( err ){}
- }
- menu.remove();
- }
-
- $( 'html' ).one( "click.close_popup", closePopup );
- if( window.parent !== window ){
- try {
- $( window.parent.document ).find( 'html' ).one( "click.close_popup", closePopup );
- } catch( err ){}
- } else {
- try {
- $( 'iframe#galaxy_main' ).contents().one( "click.close_popup", closePopup );
- } catch( err ){}
- }
- },
-
- // add a menu option/item at the given index
- addItem: function( item, index ){
- // append to end if no index
- index = ( index >= 0 ) ? index : this.options.length;
- this.options.splice( index, 0, item );
- return this;
- },
-
- // remove a menu option/item at the given index
- removeItem: function( index ){
- if( index >=0 ){
- this.options.splice( index, 1 );
- }
- return this;
- },
-
- // search for a menu option by its html
- findIndexByHtml: function( html ){
- for( var i = 0; i < this.options.length; i++ ){
- if( _.has( this.options[i], 'html' ) && ( this.options[i].html === html )){
- return i;
- }
- }
- return null;
- },
-
- // search for a menu option by its html
- findItemByHtml: function( html ){
- return this.options[( this.findIndexByHtml( html ))];
- },
-
- // string representation
- toString: function(){
- return 'PopupMenu';
- }
-});
-/** shortcut to new for when you don't need to preserve the ref */
-PopupMenu.create = function _create( $button, options ){
- return new PopupMenu( $button, options );
-};
-
-// -----------------------------------------------------------------------------
-// the following class functions are bridges from the original make_popupmenu and make_popup_menus
-// to the newer backbone.js PopupMenu
-
-/** Create a PopupMenu from simple map initial_options activated by clicking button_element.
- * Converts initial_options to object array used by PopupMenu.
- * @param {jQuery|DOMElement} button_element element which, when clicked, activates menu
- * @param {Object} initial_options map of key -> values, where
- * key is option text, value is fn to call when option is clicked
- * @returns {PopupMenu} the PopupMenu created
- */
-PopupMenu.make_popupmenu = function( button_element, initial_options ){
- var convertedOptions = [];
- _.each( initial_options, function( optionVal, optionKey ){
- var newOption = { html: optionKey };
-
- // keys with null values indicate: header
- if( optionVal === null ){ // !optionVal? (null only?)
- newOption.header = true;
-
- // keys with function values indicate: a menu option
- } else if( jQuery.type( optionVal ) === 'function' ){
- newOption.func = optionVal;
- }
- //TODO:?? any other special optionVals?
- // there was no divider option originally
- convertedOptions.push( newOption );
- });
- return new PopupMenu( $( button_element ), convertedOptions );
-};
-
-/** Find all anchors in $parent (using selector) and covert anchors into a PopupMenu options map.
- * @param {jQuery} $parent the element that contains the links to convert to options
- * @param {String} selector jq selector string to find links
- * @returns {Object[]} the options array to initialize a PopupMenu
- */
-//TODO: lose parent and selector, pass in array of links, use map to return options
-PopupMenu.convertLinksToOptions = function( $parent, selector ){
- $parent = $( $parent );
- selector = selector || 'a';
- var options = [];
- $parent.find( selector ).each( function( elem, i ){
- var option = {}, $link = $( elem );
-
- // convert link text to the option text (html) and the href into the option func
- option.html = $link.text();
- if( $link.attr( 'href' ) ){
- var linkHref = $link.attr( 'href' ),
- linkTarget = $link.attr( 'target' ),
- confirmText = $link.attr( 'confirm' );
-
- option.func = function(){
- // if there's a "confirm" attribute, throw up a confirmation dialog, and
- // if the user cancels - do nothing
- if( ( confirmText ) && ( !confirm( confirmText ) ) ){ return; }
-
- // if there's no confirm attribute, or the user accepted the confirm dialog:
- switch( linkTarget ){
- // relocate the center panel
- case '_parent':
- window.parent.location = linkHref;
- break;
-
- // relocate the entire window
- case '_top':
- window.top.location = linkHref;
- break;
-
- // relocate this panel
- default:
- window.location = linkHref;
- }
- };
- }
- options.push( option );
- });
- return options;
-};
-
-/** Create a single popupmenu from existing DOM button and anchor elements
- * @param {jQuery} $buttonElement the element that when clicked will open the menu
- * @param {jQuery} $menuElement the element that contains the anchors to convert into a menu
- * @param {String} menuElementLinkSelector jq selector string used to find anchors to be made into menu options
- * @returns {PopupMenu} the PopupMenu (Backbone View) that can render, control the menu
- */
-PopupMenu.fromExistingDom = function( $buttonElement, $menuElement, menuElementLinkSelector ){
- $buttonElement = $( $buttonElement );
- $menuElement = $( $menuElement );
- var options = PopupMenu.convertLinksToOptions( $menuElement, menuElementLinkSelector );
- // we're done with the menu (having converted it to an options map)
- $menuElement.remove();
- return new PopupMenu( $buttonElement, options );
-};
-
-/** Create all popupmenus within a document or a more specific element
- * @param {DOMElement} parent the DOM element in which to search for popupmenus to build (defaults to document)
- * @param {String} menuSelector jq selector string to find popupmenu menu elements (defaults to "div[popupmenu]")
- * @param {Function} buttonSelectorBuildFn the function to build the jq button selector.
- * Will be passed $menuElement, parent.
- * (Defaults to return '#' + $menuElement.attr( 'popupmenu' ); )
- * @returns {PopupMenu[]} array of popupmenus created
- */
-PopupMenu.make_popup_menus = function( parent, menuSelector, buttonSelectorBuildFn ){
- parent = parent || document;
- // orig. Glx popupmenu menus have a (non-std) attribute 'popupmenu'
- // which contains the id of the button that activates the menu
- menuSelector = menuSelector || 'div[popupmenu]';
- // default to (orig. Glx) matching button to menu by using the popupmenu attr of the menu as the id of the button
- buttonSelectorBuildFn = buttonSelectorBuildFn || function( $menuElement, parent ){
- return '#' + $menuElement.attr( 'popupmenu' );
- };
-
- // aggregate and return all PopupMenus
- var popupMenusCreated = [];
- $( parent ).find( menuSelector ).each( function(){
- var $menuElement = $( this ),
- $buttonElement = $( parent ).find( buttonSelectorBuildFn( $menuElement, parent ) );
- popupMenusCreated.push( PopupMenu.fromDom( $buttonElement, $menuElement ) );
- $buttonElement.addClass( 'popup' );
- });
- return popupMenusCreated;
-};
diff --git a/static/scripts/mvc/ui/popup-menu.js b/static/scripts/mvc/ui/popup-menu.js
new file mode 100644
index 00000000000..1e51addd5c6
--- /dev/null
+++ b/static/scripts/mvc/ui/popup-menu.js
@@ -0,0 +1,311 @@
+define([
+ //jquery
+ //backbone
+], function(){
+// =============================================================================
+/**
+ * view for a popup menu
+ */
+var PopupMenu = Backbone.View.extend({
+//TODO: maybe better as singleton off the Galaxy obj
+ /** Cache the desired button element and options, set up the button click handler
+ * NOTE: attaches this view as HTML/jQ data on the button for later use.
+ */
+ initialize: function( $button, options ){
+ // default settings
+ this.$button = $button;
+ if( !this.$button.size() ){
+ this.$button = $( '' );
+ }
+ this.options = options || [];
+
+ // set up button click -> open menu behavior
+ var menu = this;
+ this.$button.click( function( event ){
+ // if there's already a menu open, remove it
+ $( '.popmenu-wrapper' ).remove();
+ menu._renderAndShow( event );
+ return false;
+ });
+ },
+
+ // render the menu, append to the page body at the click position, and set up the 'click-away' handlers, show
+ _renderAndShow: function( clickEvent ){
+ this.render();
+ this.$el.appendTo( 'body' ).css( this._getShownPosition( clickEvent )).show();
+ this._setUpCloseBehavior();
+ },
+
+ // render the menu
+ // this menu doesn't attach itself to the DOM ( see _renderAndShow )
+ render: function(){
+ // render the menu body absolute and hidden, fill with template
+ this.$el.addClass( 'popmenu-wrapper' ).hide()
+ .css({ position : 'absolute' })
+ .html( this.template( this.$button.attr( 'id' ), this.options ));
+
+ // set up behavior on each link/anchor elem
+ if( this.options.length ){
+ var menu = this;
+ //precondition: there should be one option per li
+ this.$el.find( 'li' ).each( function( i, li ){
+ var option = menu.options[i];
+
+ // if the option has 'func', call that function when the anchor is clicked
+ if( option.func ){
+ $( this ).children( 'a.popupmenu-option' ).click( function( event ){
+ option.func.call( menu, event, option );
+ // bubble up so that an option click will call the close behavior
+ //return false;
+ });
+ }
+ });
+ }
+ return this;
+ },
+
+ template : function( id, options ){
+ return [
+ ''
+ ].join( '' );
+ },
+
+ _templateOptions : function( options ){
+ if( !options.length ){
+ return '(no options)';
+ }
+ return _.map( options, function( option ){
+ if( option.divider ){
+ return '';
+ } else if( option.header ){
+ return [ '', option.html, '' ].join( '' );
+ }
+ var href = option.href || 'javascript:void(0);',
+ target = ( option.target )?( ' target="' + option.target + '"' ):( '' ),
+ check = ( option.checked )?( '' ):( '' );
+ return [
+ ''
+ ].join( '' );
+ }).join( '' );
+ },
+
+ // get the absolute position/offset for the menu
+ _getShownPosition : function( clickEvent ){
+
+ // display menu horiz. centered on click...
+ var menuWidth = this.$el.width();
+ var x = clickEvent.pageX - menuWidth / 2 ;
+
+ // adjust to handle horiz. scroll and window dimensions ( draw entirely on visible screen area )
+ x = Math.min( x, $( document ).scrollLeft() + $( window ).width() - menuWidth - 5 );
+ x = Math.max( x, $( document ).scrollLeft() + 5 );
+ return {
+ top: clickEvent.pageY,
+ left: x
+ };
+ },
+
+ // bind an event handler to all available frames so that when anything is clicked
+ // the menu is removed from the DOM and the event handler unbinds itself
+ _setUpCloseBehavior: function(){
+ var menu = this;
+//TODO: alternately: focus hack, blocking overlay, jquery.blockui
+
+ // function to close popup and unbind itself
+ function closePopup( event ){
+ $( document ).off( 'click.close_popup' );
+ if( window.parent !== window ){
+ try {
+ $( window.parent.document ).off( "click.close_popup" );
+ } catch( err ){}
+ } else {
+ try {
+ $( 'iframe#galaxy_main' ).contents().off( "click.close_popup" );
+ } catch( err ){}
+ }
+ menu.remove();
+ }
+
+ $( 'html' ).one( "click.close_popup", closePopup );
+ if( window.parent !== window ){
+ try {
+ $( window.parent.document ).find( 'html' ).one( "click.close_popup", closePopup );
+ } catch( err ){}
+ } else {
+ try {
+ $( 'iframe#galaxy_main' ).contents().one( "click.close_popup", closePopup );
+ } catch( err ){}
+ }
+ },
+
+ // add a menu option/item at the given index
+ addItem: function( item, index ){
+ // append to end if no index
+ index = ( index >= 0 ) ? index : this.options.length;
+ this.options.splice( index, 0, item );
+ return this;
+ },
+
+ // remove a menu option/item at the given index
+ removeItem: function( index ){
+ if( index >=0 ){
+ this.options.splice( index, 1 );
+ }
+ return this;
+ },
+
+ // search for a menu option by its html
+ findIndexByHtml: function( html ){
+ for( var i = 0; i < this.options.length; i++ ){
+ if( _.has( this.options[i], 'html' ) && ( this.options[i].html === html )){
+ return i;
+ }
+ }
+ return null;
+ },
+
+ // search for a menu option by its html
+ findItemByHtml: function( html ){
+ return this.options[( this.findIndexByHtml( html ))];
+ },
+
+ // string representation
+ toString: function(){
+ return 'PopupMenu';
+ }
+});
+/** shortcut to new for when you don't need to preserve the ref */
+PopupMenu.create = function _create( $button, options ){
+ return new PopupMenu( $button, options );
+};
+
+// -----------------------------------------------------------------------------
+// the following class functions are bridges from the original make_popupmenu and make_popup_menus
+// to the newer backbone.js PopupMenu
+
+/** Create a PopupMenu from simple map initial_options activated by clicking button_element.
+ * Converts initial_options to object array used by PopupMenu.
+ * @param {jQuery|DOMElement} button_element element which, when clicked, activates menu
+ * @param {Object} initial_options map of key -> values, where
+ * key is option text, value is fn to call when option is clicked
+ * @returns {PopupMenu} the PopupMenu created
+ */
+PopupMenu.make_popupmenu = function( button_element, initial_options ){
+ var convertedOptions = [];
+ _.each( initial_options, function( optionVal, optionKey ){
+ var newOption = { html: optionKey };
+
+ // keys with null values indicate: header
+ if( optionVal === null ){ // !optionVal? (null only?)
+ newOption.header = true;
+
+ // keys with function values indicate: a menu option
+ } else if( jQuery.type( optionVal ) === 'function' ){
+ newOption.func = optionVal;
+ }
+ //TODO:?? any other special optionVals?
+ // there was no divider option originally
+ convertedOptions.push( newOption );
+ });
+ return new PopupMenu( $( button_element ), convertedOptions );
+};
+
+/** Find all anchors in $parent (using selector) and covert anchors into a PopupMenu options map.
+ * @param {jQuery} $parent the element that contains the links to convert to options
+ * @param {String} selector jq selector string to find links
+ * @returns {Object[]} the options array to initialize a PopupMenu
+ */
+//TODO: lose parent and selector, pass in array of links, use map to return options
+PopupMenu.convertLinksToOptions = function( $parent, selector ){
+ $parent = $( $parent );
+ selector = selector || 'a';
+ var options = [];
+ $parent.find( selector ).each( function( elem, i ){
+ var option = {}, $link = $( elem );
+
+ // convert link text to the option text (html) and the href into the option func
+ option.html = $link.text();
+ if( $link.attr( 'href' ) ){
+ var linkHref = $link.attr( 'href' ),
+ linkTarget = $link.attr( 'target' ),
+ confirmText = $link.attr( 'confirm' );
+
+ option.func = function(){
+ // if there's a "confirm" attribute, throw up a confirmation dialog, and
+ // if the user cancels - do nothing
+ if( ( confirmText ) && ( !confirm( confirmText ) ) ){ return; }
+
+ // if there's no confirm attribute, or the user accepted the confirm dialog:
+ switch( linkTarget ){
+ // relocate the center panel
+ case '_parent':
+ window.parent.location = linkHref;
+ break;
+
+ // relocate the entire window
+ case '_top':
+ window.top.location = linkHref;
+ break;
+
+ // relocate this panel
+ default:
+ window.location = linkHref;
+ }
+ };
+ }
+ options.push( option );
+ });
+ return options;
+};
+
+/** Create a single popupmenu from existing DOM button and anchor elements
+ * @param {jQuery} $buttonElement the element that when clicked will open the menu
+ * @param {jQuery} $menuElement the element that contains the anchors to convert into a menu
+ * @param {String} menuElementLinkSelector jq selector string used to find anchors to be made into menu options
+ * @returns {PopupMenu} the PopupMenu (Backbone View) that can render, control the menu
+ */
+PopupMenu.fromExistingDom = function( $buttonElement, $menuElement, menuElementLinkSelector ){
+ $buttonElement = $( $buttonElement );
+ $menuElement = $( $menuElement );
+ var options = PopupMenu.convertLinksToOptions( $menuElement, menuElementLinkSelector );
+ // we're done with the menu (having converted it to an options map)
+ $menuElement.remove();
+ return new PopupMenu( $buttonElement, options );
+};
+
+/** Create all popupmenus within a document or a more specific element
+ * @param {DOMElement} parent the DOM element in which to search for popupmenus to build (defaults to document)
+ * @param {String} menuSelector jq selector string to find popupmenu menu elements (defaults to "div[popupmenu]")
+ * @param {Function} buttonSelectorBuildFn the function to build the jq button selector.
+ * Will be passed $menuElement, parent.
+ * (Defaults to return '#' + $menuElement.attr( 'popupmenu' ); )
+ * @returns {PopupMenu[]} array of popupmenus created
+ */
+PopupMenu.make_popup_menus = function( parent, menuSelector, buttonSelectorBuildFn ){
+ parent = parent || document;
+ // orig. Glx popupmenu menus have a (non-std) attribute 'popupmenu'
+ // which contains the id of the button that activates the menu
+ menuSelector = menuSelector || 'div[popupmenu]';
+ // default to (orig. Glx) matching button to menu by using the popupmenu attr of the menu as the id of the button
+ buttonSelectorBuildFn = buttonSelectorBuildFn || function( $menuElement, parent ){
+ return '#' + $menuElement.attr( 'popupmenu' );
+ };
+
+ // aggregate and return all PopupMenus
+ var popupMenusCreated = [];
+ $( parent ).find( menuSelector ).each( function(){
+ var $menuElement = $( this ),
+ $buttonElement = $( parent ).find( buttonSelectorBuildFn( $menuElement, parent ) );
+ popupMenusCreated.push( PopupMenu.fromDom( $buttonElement, $menuElement ) );
+ $buttonElement.addClass( 'popup' );
+ });
+ return popupMenusCreated;
+};
+
+
+// =============================================================================
+ return PopupMenu;
+});
+
diff --git a/static/scripts/packed/mvc/grid/grid-view.js b/static/scripts/packed/mvc/grid/grid-view.js
index 2ae987a3985..a3af7b1bd05 100644
--- a/static/scripts/packed/mvc/grid/grid-view.js
+++ b/static/scripts/packed/mvc/grid/grid-view.js
@@ -1 +1 @@
-jQuery.ajaxSettings.traditional=true;define(["mvc/grid/grid-model","mvc/grid/grid-template","mvc/ui"],function(b,a){return Backbone.View.extend({grid:null,initialize:function(c){this.setElement("#grid-container");if(c.use_panels){$("#center").css({padding:"10px",overflow:"auto"})}this.init_grid(c)},handle_refresh:function(c){if(c){if($.inArray("history",c)>-1){if(top.Galaxy&&top.Galaxy.currHistoryPanel){top.Galaxy.currHistoryPanel.loadCurrentHistory()}}}},init_grid:function(f){this.grid=new b(f);var e=this.grid.attributes;this.handle_refresh(e.refresh_frames);var d=this.grid.get("url_base");d=d.replace(/^.*\/\/[^\/]+/,"");this.grid.set("url_base",d);this.$el.html(a.grid(e));this.$el.find("#grid-table-header").html(a.header(e));this.$el.find("#grid-table-body").html(a.body(e));this.$el.find("#grid-table-footer").html(a.footer(e));if(e.message){this.$el.find("#grid-message").html(a.message(e));var c=this;if(e.use_hide_message){setTimeout(function(){c.$el.find("#grid-message").html("")},5000)}}this.init_grid_elements();this.init_grid_controls();init_refresh_on_change()},init_grid_controls:function(){var c=this;this.$el.find(".operation-button").each(function(){$(this).off();$(this).click(function(){c.submit_operation(this,operation.confirm);return false})});this.$el.find("input[type=text]").each(function(){$(this).off();$(this).click(function(){$(this).select()}).keyup(function(){$(this).css("font-style","normal")})});this.$el.find(".sort-link").each(function(){$(this).off();$(this).click(function(){c.set_sort_condition($(this).attr("sort_key"));return false})});this.$el.find(".text-filter-form").each(function(){$(this).off();$(this).submit(function(){var g=$(this).attr("column_key");var f=$("#input-"+g+"-filter");var h=f.val();f.val("");c.add_filter_condition(g,h);return false})});this.$el.find(".text-filter-val > a").each(function(){$(this).off();$(this).click(function(){$(this).parent().remove();c.remove_filter_condition($(this).attr("filter_key"),$(this).attr("filter_val"));return false})});this.$el.find(".categorical-filter > a").each(function(){$(this).off();$(this).click(function(){c.set_categorical_filter($(this).attr("filter_key"),$(this).attr("filter_val"));return false})});var e=this.$el.find("#input-tags-filter");if(e.length){e.autocomplete(this.grid.history_tag_autocomplete_url,{selectFirst:false,autoFill:false,highlight:false,mustMatch:false})}var d=this.$el.find("#input-name-filter");if(d.length){d.autocomplete(this.grid.history_name_autocomplete_url,{selectFirst:false,autoFill:false,highlight:false,mustMatch:false})}this.$el.find(".advanced-search-toggle").each(function(){$(this).off();$(this).click(function(){c.$el.find("#standard-search").slideToggle("fast");c.$el.find("#advanced-search").slideToggle("fast");return false})});this.$el.find("#check_all").off();this.$el.find("#check_all").on("click",function(){c.check_all_items()})},init_grid_elements:function(){this.$el.find(".grid").each(function(){var j=$(this).find("input.grid-row-select-checkbox");var i=$(this).find("span.grid-selected-count");var r=function(){i.text($(j).filter(":checked").length)};$(j).each(function(){$(this).change(r)});r()});if(this.$el.find(".community_rating_star").length!==0){this.$el.find(".community_rating_star").rating({})}var q=this.grid.attributes;var p=this;this.$el.find(".page-link > a").each(function(){$(this).click(function(){p.set_page($(this).attr("page_num"));return false})});this.$el.find(".use-inbound").each(function(){$(this).click(function(i){p.execute({href:$(this).attr("href"),inbound:true});return false})});this.$el.find(".use-outbound").each(function(){$(this).click(function(i){p.execute({href:$(this).attr("href")});return false})});var f=q.items.length;if(f==0){return}for(var k in q.items){var o=q.items[k];var l=this.$el.find("#grid-"+k+"-popup");l.off();var d=new PopupMenu(l);for(var h in q.operations){var e=q.operations[h];var m=e.label;var c=o.operation_config[m];var g=o.encode_id;if(c.allowed&&e.allow_popup){var n={html:e.label,href:c.url_args,target:c.target,confirmation_text:e.confirm,inbound:e.inbound};n.func=function(r){r.preventDefault();var j=$(r.target).html();var i=this.findItemByHtml(j);p.execute(i)};d.addItem(n)}}}},add_filter_condition:function(e,g){if(g===""){return false}this.grid.add_filter(e,g,true);var f=$(a.filter_element(e,g));var d=this;f.click(function(){$(this).remove();d.remove_filter_condition(e,g)});var c=this.$el.find("#"+e+"-filtering-criteria");c.append(f);this.go_page_one();this.execute()},remove_filter_condition:function(c,d){this.grid.remove_filter(c,d);this.go_page_one();this.execute()},set_sort_condition:function(g){var f=this.grid.get("sort_key");var e=g;if(f.indexOf(g)!==-1){if(f.substring(0,1)!=="-"){e="-"+g}}this.$el.find(".sort-arrow").remove();var d=(e.substring(0,1)=="-")?"↑":"↓";var c=$(""+d+"").addClass("sort-arrow");this.$el.find("#"+g+"-header").append(c);this.grid.set("sort_key",e);this.go_page_one();this.execute()},set_categorical_filter:function(e,g){var d=this.grid.get("categorical_filters")[e],f=this.grid.get("filters")[e];var c=this;this.$el.find("."+e+"-filter").each(function(){var k=$.trim($(this).text());var i=d[k];var j=i[e];if(j==g){$(this).empty();$(this).addClass("current-filter");$(this).append(k)}else{if(j==f){$(this).empty();var h=$(''+k+"");h.click(function(){c.set_categorical_filter(e,j)});$(this).removeClass("current-filter");$(this).append(h)}}});this.grid.add_filter(e,g);this.go_page_one();this.execute()},set_page:function(c){var d=this;this.$el.find(".page-link").each(function(){var i=$(this).attr("id"),g=parseInt(i.split("-")[2],10),e=d.grid.get("cur_page"),h;if(g===c){h=$(this).children().text();$(this).empty();$(this).addClass("inactive-link");$(this).text(h)}else{if(g===e){h=$(this).text();$(this).empty();$(this).removeClass("inactive-link");var f=$(''+h+"");f.click(function(){d.set_page(g)});$(this).append(f)}}});if(c==="all"){this.grid.set("cur_page",c)}else{this.grid.set("cur_page",parseInt(c,10))}this.execute()},submit_operation:function(d,g){var e=$(d).val();var f=this.$el.find('input[name="id"]:checked').length;if(!f>0){return false}var c=[];this.$el.find("input[name=id]:checked").each(function(){c.push($(this).val())});this.execute({operation:e,id:c,confirmation_text:g});return true},check_all_items:function(){var c=document.getElementById("check_all"),d=document.getElementsByTagName("input"),f=0,e;if(c.checked===true){for(e=0;e1){var i=j[1];var d=i.split("&");for(var h=0;h-1){if(top.Galaxy&&top.Galaxy.currHistoryPanel){top.Galaxy.currHistoryPanel.loadCurrentHistory()}}}},init_grid:function(g){this.grid=new c(g);var f=this.grid.attributes;this.handle_refresh(f.refresh_frames);var e=this.grid.get("url_base");e=e.replace(/^.*\/\/[^\/]+/,"");this.grid.set("url_base",e);this.$el.html(b.grid(f));this.$el.find("#grid-table-header").html(b.header(f));this.$el.find("#grid-table-body").html(b.body(f));this.$el.find("#grid-table-footer").html(b.footer(f));if(f.message){this.$el.find("#grid-message").html(b.message(f));var d=this;if(f.use_hide_message){setTimeout(function(){d.$el.find("#grid-message").html("")},5000)}}this.init_grid_elements();this.init_grid_controls();init_refresh_on_change()},init_grid_controls:function(){var d=this;this.$el.find(".operation-button").each(function(){$(this).off();$(this).click(function(){d.submit_operation(this,operation.confirm);return false})});this.$el.find("input[type=text]").each(function(){$(this).off();$(this).click(function(){$(this).select()}).keyup(function(){$(this).css("font-style","normal")})});this.$el.find(".sort-link").each(function(){$(this).off();$(this).click(function(){d.set_sort_condition($(this).attr("sort_key"));return false})});this.$el.find(".text-filter-form").each(function(){$(this).off();$(this).submit(function(){var h=$(this).attr("column_key");var g=$("#input-"+h+"-filter");var i=g.val();g.val("");d.add_filter_condition(h,i);return false})});this.$el.find(".text-filter-val > a").each(function(){$(this).off();$(this).click(function(){$(this).parent().remove();d.remove_filter_condition($(this).attr("filter_key"),$(this).attr("filter_val"));return false})});this.$el.find(".categorical-filter > a").each(function(){$(this).off();$(this).click(function(){d.set_categorical_filter($(this).attr("filter_key"),$(this).attr("filter_val"));return false})});var f=this.$el.find("#input-tags-filter");if(f.length){f.autocomplete(this.grid.history_tag_autocomplete_url,{selectFirst:false,autoFill:false,highlight:false,mustMatch:false})}var e=this.$el.find("#input-name-filter");if(e.length){e.autocomplete(this.grid.history_name_autocomplete_url,{selectFirst:false,autoFill:false,highlight:false,mustMatch:false})}this.$el.find(".advanced-search-toggle").each(function(){$(this).off();$(this).click(function(){d.$el.find("#standard-search").slideToggle("fast");d.$el.find("#advanced-search").slideToggle("fast");return false})});this.$el.find("#check_all").off();this.$el.find("#check_all").on("click",function(){d.check_all_items()})},init_grid_elements:function(){this.$el.find(".grid").each(function(){var j=$(this).find("input.grid-row-select-checkbox");var i=$(this).find("span.grid-selected-count");var s=function(){i.text($(j).filter(":checked").length)};$(j).each(function(){$(this).change(s)});s()});if(this.$el.find(".community_rating_star").length!==0){this.$el.find(".community_rating_star").rating({})}var r=this.grid.attributes;var q=this;this.$el.find(".page-link > a").each(function(){$(this).click(function(){q.set_page($(this).attr("page_num"));return false})});this.$el.find(".use-inbound").each(function(){$(this).click(function(i){q.execute({href:$(this).attr("href"),inbound:true});return false})});this.$el.find(".use-outbound").each(function(){$(this).click(function(i){q.execute({href:$(this).attr("href")});return false})});var g=r.items.length;if(g==0){return}for(var l in r.items){var p=r.items[l];var m=this.$el.find("#grid-"+l+"-popup");m.off();var e=new a(m);for(var k in r.operations){var f=r.operations[k];var n=f.label;var d=p.operation_config[n];var h=p.encode_id;if(d.allowed&&f.allow_popup){var o={html:f.label,href:d.url_args,target:d.target,confirmation_text:f.confirm,inbound:f.inbound};o.func=function(s){s.preventDefault();var j=$(s.target).html();var i=this.findItemByHtml(j);q.execute(i)};e.addItem(o)}}}},add_filter_condition:function(f,h){if(h===""){return false}this.grid.add_filter(f,h,true);var g=$(b.filter_element(f,h));var e=this;g.click(function(){$(this).remove();e.remove_filter_condition(f,h)});var d=this.$el.find("#"+f+"-filtering-criteria");d.append(g);this.go_page_one();this.execute()},remove_filter_condition:function(d,e){this.grid.remove_filter(d,e);this.go_page_one();this.execute()},set_sort_condition:function(h){var g=this.grid.get("sort_key");var f=h;if(g.indexOf(h)!==-1){if(g.substring(0,1)!=="-"){f="-"+h}}this.$el.find(".sort-arrow").remove();var e=(f.substring(0,1)=="-")?"↑":"↓";var d=$(""+e+"").addClass("sort-arrow");this.$el.find("#"+h+"-header").append(d);this.grid.set("sort_key",f);this.go_page_one();this.execute()},set_categorical_filter:function(f,h){var e=this.grid.get("categorical_filters")[f],g=this.grid.get("filters")[f];var d=this;this.$el.find("."+f+"-filter").each(function(){var l=$.trim($(this).text());var j=e[l];var k=j[f];if(k==h){$(this).empty();$(this).addClass("current-filter");$(this).append(l)}else{if(k==g){$(this).empty();var i=$(''+l+"");i.click(function(){d.set_categorical_filter(f,k)});$(this).removeClass("current-filter");$(this).append(i)}}});this.grid.add_filter(f,h);this.go_page_one();this.execute()},set_page:function(d){var e=this;this.$el.find(".page-link").each(function(){var j=$(this).attr("id"),h=parseInt(j.split("-")[2],10),f=e.grid.get("cur_page"),i;if(h===d){i=$(this).children().text();$(this).empty();$(this).addClass("inactive-link");$(this).text(i)}else{if(h===f){i=$(this).text();$(this).empty();$(this).removeClass("inactive-link");var g=$(''+i+"");g.click(function(){e.set_page(h)});$(this).append(g)}}});if(d==="all"){this.grid.set("cur_page",d)}else{this.grid.set("cur_page",parseInt(d,10))}this.execute()},submit_operation:function(e,h){var f=$(e).val();var g=this.$el.find('input[name="id"]:checked').length;if(!g>0){return false}var d=[];this.$el.find("input[name=id]:checked").each(function(){d.push($(this).val())});this.execute({operation:f,id:d,confirmation_text:h});return true},check_all_items:function(){var d=document.getElementById("check_all"),e=document.getElementsByTagName("input"),g=0,f;if(d.checked===true){for(f=0;f1){var j=k[1];var e=j.split("&");for(var i=0;i',s,""].join("")}o=o||this.$el;var m=this.collection.where({deleted:true}),q=this.collection.where({visible:false}),p=[];if(this.views.length){p.push([this.views.length,c("shown")].join(" "))}if(m.length){p.push((!this.showDeleted)?([m.length,n("toggle-deleted-link",c("deleted"))].join(" ")):(n("toggle-deleted-link",c("hide deleted"))))}if(q.length){p.push((!this.showHidden)?([q.length,n("toggle-hidden-link",c("hidden"))].join(" ")):(n("toggle-hidden-link",c("hide hidden"))))}return o.find("> .controls .subtitle").html(p.join(", "))},_renderTags:function(m){var n=this;this.tagsEditor=new k.TagsEditor({model:this.model,el:m.find(".controls .tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){n.toggleHDATagEditors(true,n.fxSpeed)},onhide:function(){n.toggleHDATagEditors(false,n.fxSpeed)},$activator:a({title:c("Edit history tags"),classes:"history-tag-btn",faIcon:"fa-tags"}).appendTo(m.find(".controls .actions"))})},_renderAnnotation:function(m){var n=this;this.annotationEditor=new b.AnnotationEditor({model:this.model,el:m.find(".controls .annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){n.toggleHDAAnnotationEditors(true,n.fxSpeed)},onhide:function(){n.toggleHDAAnnotationEditors(false,n.fxSpeed)},$activator:a({title:c("Edit history annotation"),classes:"history-annotate-btn",faIcon:"fa-comment"}).appendTo(m.find(".controls .actions"))})},_setUpBehaviors:function(m){m=m||this.$el;j.prototype._setUpBehaviors.call(this,m);if(!this.model){return}if(this.multiselectActions.length){this.actionsPopup=new PopupMenu(m.find(".list-action-popup-btn"),this.multiselectActions)}if((!Galaxy.currUser||Galaxy.currUser.isAnonymous())||(Galaxy.currUser.id!==this.model.get("user_id"))){return}var n=this,o=".controls .name";m.find(o).attr("title",c("Click to rename history")).tooltip({placement:"bottom"}).make_text_editable({on_finish:function(p){var q=n.model.get("name");if(p&&p!==q){n.$el.find(o).text(p);n.model.save({name:p}).fail(function(){n.$el.find(o).text(n.model.previous("name"))})}else{n.$el.find(o).text(q)}}})},_getActions:function(){var m=this,n=[{html:c("Hide datasets"),func:function(){var o=e.HistoryDatasetAssociation.prototype.hide;m.getSelectedModels().ajaxQueue(o)}},{html:c("Unhide datasets"),func:function(){var o=e.HistoryDatasetAssociation.prototype.unhide;m.getSelectedModels().ajaxQueue(o)}},{html:c("Delete datasets"),func:function(){var o=e.HistoryDatasetAssociation.prototype["delete"];m.getSelectedModels().ajaxQueue(o)}},{html:c("Undelete datasets"),func:function(){var o=e.HistoryDatasetAssociation.prototype.undelete;m.getSelectedModels().ajaxQueue(o)}}];if(m.purgeAllowed){n.push({html:c("Permanently delete datasets"),func:function(){if(confirm(c("This will permanently remove the data in your datasets. Are you sure?"))){var o=e.HistoryDatasetAssociation.prototype.purge;m.getSelectedModels().ajaxQueue(o)}}})}n.push({html:c("Build Dataset List"),func:function(){m.getSelectedModels().promoteToHistoryDatasetCollection(m.model,"list")}});n.push({html:c("Build Dataset Pair"),func:function(){m.getSelectedModels().promoteToHistoryDatasetCollection(m.model,"paired")}});n.push({html:c("Build List of Dataset Pairs"),func:_.bind(m._showPairedCollectionModal,m)});return n},_showPairedCollectionModal:function(){var m=this,n=m.getSelectedModels().toJSON().filter(function(o){return o.history_content_type==="dataset"&&o.state===l.OK});if(n.length){require(["mvc/collection/paired-collection-creator"],function(o){window.creator=o.pairedCollectionCreatorModal(n,{historyId:m.model.id})})}else{}},_attachItems:function(m){this.$list(m).append(this.views.reverse().map(function(n){return n.$el}));return this},_attachView:function(n){var m=this;m.views.unshift(n);m.$list().prepend(n.render(0).$el.hide());n.$el.slideDown(m.fxSpeed)},_getItemViewOptions:function(n){var m=j.prototype._getItemViewOptions.call(this,n);_.extend(m,{purgeAllowed:this.purgeAllowed,tagsEditorShown:(this.tagsEditor&&!this.tagsEditor.hidden),annotationEditorShown:(this.annotationEditor&&!this.annotationEditor.hidden)});return m},_handleHdaDeletionChange:function(m){if(m.get("deleted")&&!this.showDeleted){this.removeItemView(m)}this._renderCounts()},_handleHdaVisibleChange:function(m){if(m.hidden()&&!this.storage.showHidden){this.removeItemView(m)}this._renderCounts()},toggleHDATagEditors:function(m){var n=Array.prototype.slice.call(arguments,1);_.each(this.views,function(o){if(o.tagsEditor){o.tagsEditor.toggle.apply(o.tagsEditor,n)}})},toggleHDAAnnotationEditors:function(m){var n=Array.prototype.slice.call(arguments,1);_.each(this.views,function(o){if(o.annotationEditor){o.annotationEditor.toggle.apply(o.annotationEditor,n)}})},events:_.extend(_.clone(j.prototype.events),{"click .show-selectors-btn":"toggleSelectors","click .toggle-deleted-link":function(m){this.toggleShowDeleted()},"click .toggle-hidden-link":function(m){this.toggleShowHidden()}}),updateHistoryDiskSize:function(){this.$el.find(".history-size").text(this.model.get("nice_size"))},dropTargetOn:function(){if(this.dropTarget){return this}this.dropTarget=true;var n={dragenter:_.bind(this.dragenter,this),dragover:_.bind(this.dragover,this),dragleave:_.bind(this.dragleave,this),drop:_.bind(this.drop,this)};var o=this._renderDropTarget();this.$list().before([this._renderDropTargetHelp(),o]);for(var m in n){if(n.hasOwnProperty(m)){o.on(m,n[m])}}return this},_renderDropTarget:function(){return $("").addClass("history-drop-target").css({height:"64px",margin:"0px 10px 10px 10px",border:"1px dashed black","border-radius":"3px"})},_renderDropTargetHelp:function(){return $("").addClass("history-drop-target-help").css({margin:"10px 10px 4px 10px",color:"grey","font-size":"80%","font-style":"italic"}).text(c("Drag datasets here to copy them to the current history"))},dropTargetOff:function(){if(!this.dropTarget){return this}this.dropTarget=false;this.$(".history-drop-target").remove();this.$(".history-drop-target-help").remove();return this},dropTargetToggle:function(){if(this.dropTarget){this.dropTargetOff()}else{this.dropTargetOn()}return this},dragenter:function(m){m.preventDefault();m.stopPropagation();this.$(".history-drop-target").css("border","2px solid black")},dragover:function(m){m.preventDefault();m.stopPropagation()},dragleave:function(m){m.preventDefault();m.stopPropagation();this.$(".history-drop-target").css("border","1px dashed black")},drop:function(o){o.preventDefault();o.dataTransfer.dropEffect="move";var m=this,p=o.dataTransfer.getData("text");try{p=JSON.parse(p)}catch(n){this.warn("error parsing JSON from drop:",p)}this.trigger("droptarget:drop",o,p,m);return false},dataDropped:function(n){var m=this;if(_.isObject(n)&&n.model_class==="HistoryDatasetAssociation"&&n.id){return m.model.contents.copy(n.id)}return jQuery.when()},toString:function(){return"HistoryPanelEdit("+((this.model)?(this.model.get("name")):(""))+")"}});return{HistoryPanelEdit:f}});
\ No newline at end of file
+define(["mvc/history/history-panel","mvc/history/history-contents","mvc/dataset/states","mvc/history/hda-model","mvc/history/hda-li-edit","mvc/history/hdca-li-edit","mvc/tags","mvc/annotations","jq-plugins/ui/fa-icon-button","mvc/ui/popup-menu","utils/localization"],function(h,j,m,f,e,i,l,c,b,a,d){var k=h.HistoryPanel;var g=k.extend({HDAViewClass:e.HDAListItemEdit,HDCAViewClass:i.HDCAListItemEdit,initialize:function(n){n=n||{};k.prototype.initialize.call(this,n);this.tagsEditor=null;this.annotationEditor=null;this.purgeAllowed=n.purgeAllowed||false;this.annotationEditorShown=n.annotationEditorShown||false;this.tagsEditorShown=n.tagsEditorShown||false;this.multiselectActions=n.multiselectActions||this._getActions()},_setUpListeners:function(){k.prototype._setUpListeners.call(this);this.on("drop",function(n,o){this.dataDropped(o);this.dropTargetOff()})},_setUpCollectionListeners:function(){k.prototype._setUpCollectionListeners.call(this);this.collection.on("change:deleted",this._handleHdaDeletionChange,this);this.collection.on("change:visible",this._handleHdaVisibleChange,this);this.collection.on("change:purged",function(n){this.model.fetch()},this);return this},_setUpModelListeners:function(){k.prototype._setUpModelListeners.call(this);this.model.on("change:nice_size",this.updateHistoryDiskSize,this);return this},_buildNewRender:function(){var n=k.prototype._buildNewRender.call(this);if(!this.model){return n}if(Galaxy&&Galaxy.currUser&&Galaxy.currUser.id&&Galaxy.currUser.id===this.model.get("user_id")){this._renderTags(n);this._renderAnnotation(n)}return n},renderItems:function(o){var n=k.prototype.renderItems.call(this,o);this._renderCounts(o);return n},_renderCounts:function(p){function o(s,t){return['',t,""].join("")}p=p||this.$el;var n=this.collection.where({deleted:true}),r=this.collection.where({visible:false}),q=[];if(this.views.length){q.push([this.views.length,d("shown")].join(" "))}if(n.length){q.push((!this.showDeleted)?([n.length,o("toggle-deleted-link",d("deleted"))].join(" ")):(o("toggle-deleted-link",d("hide deleted"))))}if(r.length){q.push((!this.showHidden)?([r.length,o("toggle-hidden-link",d("hidden"))].join(" ")):(o("toggle-hidden-link",d("hide hidden"))))}return p.find("> .controls .subtitle").html(q.join(", "))},_renderTags:function(n){var o=this;this.tagsEditor=new l.TagsEditor({model:this.model,el:n.find(".controls .tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){o.toggleHDATagEditors(true,o.fxSpeed)},onhide:function(){o.toggleHDATagEditors(false,o.fxSpeed)},$activator:b({title:d("Edit history tags"),classes:"history-tag-btn",faIcon:"fa-tags"}).appendTo(n.find(".controls .actions"))})},_renderAnnotation:function(n){var o=this;this.annotationEditor=new c.AnnotationEditor({model:this.model,el:n.find(".controls .annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){o.toggleHDAAnnotationEditors(true,o.fxSpeed)},onhide:function(){o.toggleHDAAnnotationEditors(false,o.fxSpeed)},$activator:b({title:d("Edit history annotation"),classes:"history-annotate-btn",faIcon:"fa-comment"}).appendTo(n.find(".controls .actions"))})},_setUpBehaviors:function(n){n=n||this.$el;k.prototype._setUpBehaviors.call(this,n);if(!this.model){return}if(this.multiselectActions.length){this.actionsPopup=new a(n.find(".list-action-popup-btn"),this.multiselectActions)}if((!Galaxy.currUser||Galaxy.currUser.isAnonymous())||(Galaxy.currUser.id!==this.model.get("user_id"))){return}var o=this,p=".controls .name";n.find(p).attr("title",d("Click to rename history")).tooltip({placement:"bottom"}).make_text_editable({on_finish:function(q){var r=o.model.get("name");if(q&&q!==r){o.$el.find(p).text(q);o.model.save({name:q}).fail(function(){o.$el.find(p).text(o.model.previous("name"))})}else{o.$el.find(p).text(r)}}})},_getActions:function(){var n=this,o=[{html:d("Hide datasets"),func:function(){var p=f.HistoryDatasetAssociation.prototype.hide;n.getSelectedModels().ajaxQueue(p)}},{html:d("Unhide datasets"),func:function(){var p=f.HistoryDatasetAssociation.prototype.unhide;n.getSelectedModels().ajaxQueue(p)}},{html:d("Delete datasets"),func:function(){var p=f.HistoryDatasetAssociation.prototype["delete"];n.getSelectedModels().ajaxQueue(p)}},{html:d("Undelete datasets"),func:function(){var p=f.HistoryDatasetAssociation.prototype.undelete;n.getSelectedModels().ajaxQueue(p)}}];if(n.purgeAllowed){o.push({html:d("Permanently delete datasets"),func:function(){if(confirm(d("This will permanently remove the data in your datasets. Are you sure?"))){var p=f.HistoryDatasetAssociation.prototype.purge;n.getSelectedModels().ajaxQueue(p)}}})}o.push({html:d("Build Dataset List"),func:function(){n.getSelectedModels().promoteToHistoryDatasetCollection(n.model,"list")}});o.push({html:d("Build Dataset Pair"),func:function(){n.getSelectedModels().promoteToHistoryDatasetCollection(n.model,"paired")}});o.push({html:d("Build List of Dataset Pairs"),func:_.bind(n._showPairedCollectionModal,n)});return o},_showPairedCollectionModal:function(){var n=this,o=n.getSelectedModels().toJSON().filter(function(p){return p.history_content_type==="dataset"&&p.state===m.OK});if(o.length){require(["mvc/collection/paired-collection-creator"],function(p){window.creator=p.pairedCollectionCreatorModal(o,{historyId:n.model.id})})}else{}},_attachItems:function(n){this.$list(n).append(this.views.reverse().map(function(o){return o.$el}));return this},_attachView:function(o){var n=this;n.views.unshift(o);n.$list().prepend(o.render(0).$el.hide());o.$el.slideDown(n.fxSpeed)},_getItemViewOptions:function(o){var n=k.prototype._getItemViewOptions.call(this,o);_.extend(n,{purgeAllowed:this.purgeAllowed,tagsEditorShown:(this.tagsEditor&&!this.tagsEditor.hidden),annotationEditorShown:(this.annotationEditor&&!this.annotationEditor.hidden)});return n},_handleHdaDeletionChange:function(n){if(n.get("deleted")&&!this.showDeleted){this.removeItemView(n)}this._renderCounts()},_handleHdaVisibleChange:function(n){if(n.hidden()&&!this.storage.showHidden){this.removeItemView(n)}this._renderCounts()},toggleHDATagEditors:function(n){var o=Array.prototype.slice.call(arguments,1);_.each(this.views,function(p){if(p.tagsEditor){p.tagsEditor.toggle.apply(p.tagsEditor,o)}})},toggleHDAAnnotationEditors:function(n){var o=Array.prototype.slice.call(arguments,1);_.each(this.views,function(p){if(p.annotationEditor){p.annotationEditor.toggle.apply(p.annotationEditor,o)}})},events:_.extend(_.clone(k.prototype.events),{"click .show-selectors-btn":"toggleSelectors","click .toggle-deleted-link":function(n){this.toggleShowDeleted()},"click .toggle-hidden-link":function(n){this.toggleShowHidden()}}),updateHistoryDiskSize:function(){this.$el.find(".history-size").text(this.model.get("nice_size"))},dropTargetOn:function(){if(this.dropTarget){return this}this.dropTarget=true;var o={dragenter:_.bind(this.dragenter,this),dragover:_.bind(this.dragover,this),dragleave:_.bind(this.dragleave,this),drop:_.bind(this.drop,this)};var p=this._renderDropTarget();this.$list().before([this._renderDropTargetHelp(),p]);for(var n in o){if(o.hasOwnProperty(n)){p.on(n,o[n])}}return this},_renderDropTarget:function(){return $("").addClass("history-drop-target").css({height:"64px",margin:"0px 10px 10px 10px",border:"1px dashed black","border-radius":"3px"})},_renderDropTargetHelp:function(){return $("").addClass("history-drop-target-help").css({margin:"10px 10px 4px 10px",color:"grey","font-size":"80%","font-style":"italic"}).text(d("Drag datasets here to copy them to the current history"))},dropTargetOff:function(){if(!this.dropTarget){return this}this.dropTarget=false;this.$(".history-drop-target").remove();this.$(".history-drop-target-help").remove();return this},dropTargetToggle:function(){if(this.dropTarget){this.dropTargetOff()}else{this.dropTargetOn()}return this},dragenter:function(n){n.preventDefault();n.stopPropagation();this.$(".history-drop-target").css("border","2px solid black")},dragover:function(n){n.preventDefault();n.stopPropagation()},dragleave:function(n){n.preventDefault();n.stopPropagation();this.$(".history-drop-target").css("border","1px dashed black")},drop:function(p){p.preventDefault();p.dataTransfer.dropEffect="move";var n=this,q=p.dataTransfer.getData("text");try{q=JSON.parse(q)}catch(o){this.warn("error parsing JSON from drop:",q)}this.trigger("droptarget:drop",p,q,n);return false},dataDropped:function(o){var n=this;if(_.isObject(o)&&o.model_class==="HistoryDatasetAssociation"&&o.id){return n.model.contents.copy(o.id)}return jQuery.when()},toString:function(){return"HistoryPanelEdit("+((this.model)?(this.model.get("name")):(""))+")"}});return{HistoryPanelEdit:g}});
\ No newline at end of file
diff --git a/static/scripts/packed/mvc/history/options-menu.js b/static/scripts/packed/mvc/history/options-menu.js
index 4598843b130..094864c1fe4 100644
--- a/static/scripts/packed/mvc/history/options-menu.js
+++ b/static/scripts/packed/mvc/history/options-menu.js
@@ -1 +1 @@
-define(["mvc/base-mvc","utils/localization"],function(a,d){var e=[{html:d("History Lists"),header:true},{html:d("Saved Histories"),href:"history/list"},{html:d("Histories Shared with Me"),href:"history/list_shared"},{html:d("Current History"),header:true,anon:true},{html:d("Create New"),func:function(){if(Galaxy&&Galaxy.currHistoryPanel){Galaxy.currHistoryPanel.createNewHistory()}}},{html:d("Copy History"),href:"history/copy"},{html:d("Copy Datasets"),href:"dataset/copy_datasets"},{html:d("Share or Publish"),href:"history/sharing"},{html:d("Extract Workflow"),href:"workflow/build_from_current_history"},{html:d("Dataset Security"),href:"root/history_set_default_permissions"},{html:d("Resume Paused Jobs"),href:"history/resume_paused_jobs?current=True",anon:true},{html:d("Collapse Expanded Datasets"),func:function(){if(Galaxy&&Galaxy.currHistoryPanel){Galaxy.currHistoryPanel.collapseAll()}}},{html:d("Unhide Hidden Datasets"),confirm:d("Really unhide all hidden datasets?"),href:"history/unhide_datasets?current=True",anon:true},{html:d("Delete Hidden Datasets"),confirm:d("Really delete all hidden datasets?"),href:"history/delete_hidden_datasets",anon:true},{html:d("Purge Deleted Datasets"),confirm:d("Really delete all deleted datasets permanently? This cannot be undone."),href:"history/purge_deleted_datasets",purge:true,anon:true},{html:d("Show Structure"),href:"history/display_structured",anon:true},{html:d("Export Citations"),href:"history/citations",anon:true},{html:d("Export to File"),href:"history/export_archive?preview=True",anon:true},{html:d("Delete"),confirm:d("Really delete the current history?"),href:"history/delete_current"},{html:d("Delete Permanently"),confirm:d("Really delete the current history permanently? This cannot be undone."),href:"history/delete_current?purge=True",purge:true,anon:true},{html:d("Other Actions"),header:true},{html:d("Import from File"),href:"history/import_archive"}];function c(h,g,f){return _.clone(e).filter(function(i){if(h&&!i.anon){return false}if(!g&&i.purge){return false}if(i.href){i.href=f+i.href;i.target="galaxy_main"}if(i.confirm){i.func=function(){if(confirm(i.confirm)){galaxy_main.location=i.href}}}return true})}var b=function(i,g){g=g||{};var j=g.anonymous===undefined?true:g.anonymous,h=g.purgeAllowed||false,f=g.root||((Galaxy&&Galaxy.options)?Galaxy.options.root:"/"),k=c(j,h,f);return new PopupMenu(i,k)};return b});
\ No newline at end of file
+define(["mvc/ui/popup-menu","mvc/base-mvc","utils/localization"],function(b,a,e){var f=[{html:e("History Lists"),header:true},{html:e("Saved Histories"),href:"history/list"},{html:e("Histories Shared with Me"),href:"history/list_shared"},{html:e("Current History"),header:true,anon:true},{html:e("Create New"),func:function(){if(Galaxy&&Galaxy.currHistoryPanel){Galaxy.currHistoryPanel.createNewHistory()}}},{html:e("Copy History"),href:"history/copy"},{html:e("Copy Datasets"),href:"dataset/copy_datasets"},{html:e("Share or Publish"),href:"history/sharing"},{html:e("Extract Workflow"),href:"workflow/build_from_current_history"},{html:e("Dataset Security"),href:"root/history_set_default_permissions"},{html:e("Resume Paused Jobs"),href:"history/resume_paused_jobs?current=True",anon:true},{html:e("Collapse Expanded Datasets"),func:function(){if(Galaxy&&Galaxy.currHistoryPanel){Galaxy.currHistoryPanel.collapseAll()}}},{html:e("Unhide Hidden Datasets"),confirm:e("Really unhide all hidden datasets?"),href:"history/unhide_datasets?current=True",anon:true},{html:e("Delete Hidden Datasets"),confirm:e("Really delete all hidden datasets?"),href:"history/delete_hidden_datasets",anon:true},{html:e("Purge Deleted Datasets"),confirm:e("Really delete all deleted datasets permanently? This cannot be undone."),href:"history/purge_deleted_datasets",purge:true,anon:true},{html:e("Show Structure"),href:"history/display_structured",anon:true},{html:e("Export Citations"),href:"history/citations",anon:true},{html:e("Export to File"),href:"history/export_archive?preview=True",anon:true},{html:e("Delete"),confirm:e("Really delete the current history?"),href:"history/delete_current"},{html:e("Delete Permanently"),confirm:e("Really delete the current history permanently? This cannot be undone."),href:"history/delete_current?purge=True",purge:true,anon:true},{html:e("Other Actions"),header:true},{html:e("Import from File"),href:"history/import_archive"}];function d(i,h,g){return _.clone(f).filter(function(j){if(i&&!j.anon){return false}if(!h&&j.purge){return false}if(j.href){j.href=g+j.href;j.target="galaxy_main"}if(j.confirm){j.func=function(){if(confirm(j.confirm)){galaxy_main.location=j.href}}}return true})}var c=function(j,h){h=h||{};var k=h.anonymous===undefined?true:h.anonymous,i=h.purgeAllowed||false,g=h.root||((Galaxy&&Galaxy.options)?Galaxy.options.root:"/"),l=d(k,i,g);return new b(j,l)};return c});
\ No newline at end of file
diff --git a/static/scripts/packed/mvc/ui.js b/static/scripts/packed/mvc/ui.js
index 7dfdb7fd02f..c68c1d61fab 100644
--- a/static/scripts/packed/mvc/ui.js
+++ b/static/scripts/packed/mvc/ui.js
@@ -1 +1 @@
-var IconButton=Backbone.Model.extend({defaults:{title:"",icon_class:"",on_click:null,menu_options:null,is_menu_button:true,id:null,href:null,target:null,enabled:true,visible:true,tooltip_config:{}}});var IconButtonView=Backbone.View.extend({initialize:function(){this.model.attributes.tooltip_config={placement:"bottom"};this.model.bind("change",this.render,this)},render:function(){this.$el.tooltip("hide");var a=this.template(this.model.toJSON());a.tooltip(this.model.get("tooltip_config"));this.$el.replaceWith(a);this.setElement(a);return this},events:{click:"click"},click:function(a){if(_.isFunction(this.model.get("on_click"))){this.model.get("on_click")(a);return false}return true},template:function(b){var a='title="'+b.title+'" class="icon-button';if(b.is_menu_button){a+=" menu-button"}a+=" "+b.icon_class;if(!b.enabled){a+="_disabled"}a+='"';if(b.id){a+=' id="'+b.id+'"'}a+=' href="'+b.href+'"';if(b.target){a+=' target="'+b.target+'"'}if(!b.visible){a+=' style="display: none;"'}if(b.enabled){a=""}else{a=""}return $(a)}});var IconButtonCollection=Backbone.Collection.extend({model:IconButton});var IconButtonMenuView=Backbone.View.extend({tagName:"div",initialize:function(){this.render()},render:function(){var a=this;this.collection.each(function(d){var b=$("").attr("href","javascript:void(0)").attr("title",d.attributes.title).addClass("icon-button menu-button").addClass(d.attributes.icon_class).appendTo(a.$el).click(d.attributes.on_click);if(d.attributes.tooltip_config){b.tooltip(d.attributes.tooltip_config)}var c=d.get("options");if(c){make_popupmenu(b,c)}});return this}});var create_icon_buttons_menu=function(b,a){if(!a){a={}}var c=new IconButtonCollection(_.map(b,function(d){return new IconButton(_.extend(d,a))}));return new IconButtonMenuView({collection:c})};var Grid=Backbone.Collection.extend({});var GridView=Backbone.View.extend({});var PopupMenu=Backbone.View.extend({initialize:function(b,a){this.$button=b;if(!this.$button.size()){this.$button=$("")}this.options=a||[];var c=this;this.$button.click(function(d){$(".popmenu-wrapper").remove();c._renderAndShow(d);return false})},_renderAndShow:function(a){this.render();this.$el.appendTo("body").css(this._getShownPosition(a)).show();this._setUpCloseBehavior()},render:function(){this.$el.addClass("popmenu-wrapper").hide().css({position:"absolute"}).html(this.template(this.$button.attr("id"),this.options));if(this.options.length){var a=this;this.$el.find("li").each(function(c,b){var d=a.options[c];if(d.func){$(this).children("a.popupmenu-option").click(function(e){d.func.call(a,e,d)})}})}return this},template:function(b,a){return['"].join("")},_templateOptions:function(a){if(!a.length){return"(no options)"}return _.map(a,function(d){if(d.divider){return''}else{if(d.header){return['',d.html,""].join("")}}var c=d.href||"javascript:void(0);",e=(d.target)?(' target="'+d.target+'"'):(""),b=(d.checked)?(''):("");return['"].join("")}).join("")},_getShownPosition:function(b){var c=this.$el.width();var a=b.pageX-c/2;a=Math.min(a,$(document).scrollLeft()+$(window).width()-c-5);a=Math.max(a,$(document).scrollLeft()+5);return{top:b.pageY,left:a}},_setUpCloseBehavior:function(){var c=this;function a(e){$(document).off("click.close_popup");if(window.parent!==window){try{$(window.parent.document).off("click.close_popup")}catch(d){}}else{try{$("iframe#galaxy_main").contents().off("click.close_popup")}catch(d){}}c.remove()}$("html").one("click.close_popup",a);if(window.parent!==window){try{$(window.parent.document).find("html").one("click.close_popup",a)}catch(b){}}else{try{$("iframe#galaxy_main").contents().one("click.close_popup",a)}catch(b){}}},addItem:function(b,a){a=(a>=0)?a:this.options.length;this.options.splice(a,0,b);return this},removeItem:function(a){if(a>=0){this.options.splice(a,1)}return this},findIndexByHtml:function(b){for(var a=0;a"}else{a=""}return $(a)}});var IconButtonCollection=Backbone.Collection.extend({model:IconButton});var IconButtonMenuView=Backbone.View.extend({tagName:"div",initialize:function(){this.render()},render:function(){var a=this;this.collection.each(function(d){var b=$("").attr("href","javascript:void(0)").attr("title",d.attributes.title).addClass("icon-button menu-button").addClass(d.attributes.icon_class).appendTo(a.$el).click(d.attributes.on_click);if(d.attributes.tooltip_config){b.tooltip(d.attributes.tooltip_config)}var c=d.get("options");if(c){make_popupmenu(b,c)}});return this}});var create_icon_buttons_menu=function(b,a){if(!a){a={}}var c=new IconButtonCollection(_.map(b,function(d){return new IconButton(_.extend(d,a))}));return new IconButtonMenuView({collection:c})};
\ No newline at end of file
diff --git a/static/scripts/packed/mvc/ui/popup-menu.js b/static/scripts/packed/mvc/ui/popup-menu.js
new file mode 100644
index 00000000000..2cfdabcd8da
--- /dev/null
+++ b/static/scripts/packed/mvc/ui/popup-menu.js
@@ -0,0 +1 @@
+define([],function(){var a=Backbone.View.extend({initialize:function(d,c){this.$button=d;if(!this.$button.size()){this.$button=$("")}this.options=c||[];var e=this;this.$button.click(function(f){$(".popmenu-wrapper").remove();e._renderAndShow(f);return false})},_renderAndShow:function(c){this.render();this.$el.appendTo("body").css(this._getShownPosition(c)).show();this._setUpCloseBehavior()},render:function(){this.$el.addClass("popmenu-wrapper").hide().css({position:"absolute"}).html(this.template(this.$button.attr("id"),this.options));if(this.options.length){var c=this;this.$el.find("li").each(function(e,d){var f=c.options[e];if(f.func){$(this).children("a.popupmenu-option").click(function(g){f.func.call(c,g,f)})}})}return this},template:function(d,c){return['"].join("")},_templateOptions:function(c){if(!c.length){return"(no options)"}return _.map(c,function(f){if(f.divider){return''}else{if(f.header){return['',f.html,""].join("")}}var e=f.href||"javascript:void(0);",g=(f.target)?(' target="'+f.target+'"'):(""),d=(f.checked)?(''):("");return['"].join("")}).join("")},_getShownPosition:function(d){var e=this.$el.width();var c=d.pageX-e/2;c=Math.min(c,$(document).scrollLeft()+$(window).width()-e-5);c=Math.max(c,$(document).scrollLeft()+5);return{top:d.pageY,left:c}},_setUpCloseBehavior:function(){var e=this;function c(g){$(document).off("click.close_popup");if(window.parent!==window){try{$(window.parent.document).off("click.close_popup")}catch(f){}}else{try{$("iframe#galaxy_main").contents().off("click.close_popup")}catch(f){}}e.remove()}$("html").one("click.close_popup",c);if(window.parent!==window){try{$(window.parent.document).find("html").one("click.close_popup",c)}catch(d){}}else{try{$("iframe#galaxy_main").contents().one("click.close_popup",c)}catch(d){}}},addItem:function(d,c){c=(c>=0)?c:this.options.length;this.options.splice(c,0,d);return this},removeItem:function(c){if(c>=0){this.options.splice(c,1)}return this},findIndexByHtml:function(d){for(var c=0;c