/static/scripts/mvc/ui.js
JavaScript | 592 lines | 353 code | 75 blank | 164 comment | 37 complexity | a99f4d5e25ac810a2576151bc7392e8a MD5 | raw file
- /**
- * necessary galaxy paths
- */
- var GalaxyPaths = Backbone.Model.extend(
- {
- defaults:
- {
- root_path: "",
- image_path: ""
- }
- });
- /**
- * functions for creating large ui elements
- */
- /**
- * backbone model for icon buttons
- */
- 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 : {}
- }
- });
- /**
- * backbone view for icon buttons
- */
- var IconButtonView = Backbone.View.extend(
- {
- // initialize
- initialize: function()
- {
- // better rendering this way
- this.model.attributes.tooltip_config = { placement : 'bottom' };
- this.model.bind('change', this.render, this);
- },
-
- // render
- render: function()
- {
- // hide tooltip
- this.$el.tooltip('hide');
-
- // create element
- var new_elem = this.template(this.model.attributes);
-
- // configure tooltip
- new_elem.tooltip(this.model.get('tooltip_config'));
-
- // replace
- this.$el.replaceWith(new_elem);
- this.setElement(new_elem);
- // return
- return this;
- },
-
- // events
- events:
- {
- 'click' : 'click'
- },
-
- // click
- click: function( event )
- {
- // if on_click pass to that function
- if(this.model.attributes.on_click)
- {
- this.model.attributes.on_click(event);
- return false;
- }
-
- // otherwise, bubble up (to href or whatever)
- return true;
- },
-
- // generate html element
- template: function(options)
- {
- // initialize
- var buffer = 'title="' + options.title + '" class="icon-button';
-
- // is menu button
- if(options.is_menu_button)
- buffer += ' menu-button';
-
- // define tooltip
- if(options.title)
- buffer += ' tooltip';
-
- // add icon class
- buffer += ' ' + options.icon_class;
-
- // add enabled/disabled class
- if(!options.enabled)
- buffer += '_disabled';
-
- // close class tag
- buffer += '"';
-
- // add id
- if(options.id)
- buffer += ' id="' + options.id + '"';
-
- // add href
- buffer += ' href="' + options.href + '"';
-
- // add target for href
- if(options.target)
- buffer += ' target="' + options.target + '"';
-
- // set visibility
- if(!options.visible)
- buffer += ' style="display: none;"';
-
- // enabled/disabled
- if (options.enabled)
- buffer = '<a ' + buffer + '/>';
- else
- buffer = '<span ' + buffer + '/>';
-
- // return element
- return $(buffer);
- }
- });
- // define collection
- var IconButtonCollection = Backbone.Collection.extend(
- {
- model: IconButton
- });
- /**
- * menu with multiple icon buttons
- * views are not needed nor used for individual buttons
- */
- var IconButtonMenuView = Backbone.View.extend(
- {
- // tag
- tagName: 'div',
- // initialize
- initialize: function()
- {
- this.render();
- },
-
- // render
- render: function()
- {
- // initialize icon buttons
- var self = this;
- this.collection.each(function(button)
- {
- // create and add icon button to menu
- var elt =
- $('<a/>').attr('href', 'javascript:void(0)')
- .attr('title', button.attributes.title)
- .addClass('icon-button menu-button')
- .addClass(button.attributes.icon_class)
- .appendTo(self.$el)
- .click(button.attributes.on_click);
- // configure tooltip
- if (button.attributes.tooltip_config)
- elt.tooltip(button.attributes.tooltip_config);
- // add popup menu to icon
- var menu_options = button.get('options');
- if (menu_options)
- make_popupmenu(elt, menu_options);
- });
-
- // return
- return this;
- }
- });
- /**
- * Returns an IconButtonMenuView for the provided configuration.
- * Configuration is a list of dictionaries where each dictionary
- * defines an icon button. Each dictionary must have the following
- * elements: icon_class, title, and on_click.
- */
- var create_icon_buttons_menu = function(config, global_config)
- {
- // initialize global configuration
- if (!global_config) global_config = {};
- // create and initialize menu
- var buttons = new IconButtonCollection(
- _.map(config, function(button_config)
- {
- return new IconButton(_.extend(button_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:
- add submenus
- add hrefs
- test various html keys
- add make_popupmenus style
- */
- /** 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.
- */
- //TODO: include docs on special option keys (divider, checked, etc.)
- initialize: function($button, options)
- {
- // default settings
- this.$button = $button || $('<div/>');
- this.options = options || [];
- // set up button click -> open menu behavior
- var menu = this;
- this.$button.click(function(event)
- {
- menu._renderAndShow(event);
- return false;
- });
- // attach this view as a data object on the button - for later access
- //TODO:?? memleak?
- this.$button.data('PopupMenu', this);
- },
- // render the menu
- // this menu doesn't attach itself to the DOM (see _renderAndShow)
- render: function()
- {
- // link this popup
- var menu = this;
- // render the menu body
- this.$el.addClass('popmenu-wrapper')
- .css(
- {
- position : 'absolute',
- display : 'none'
- });
- // use template
- this.$el.html(this.template(this.$button.attr('id'), this.options));
- // set up behavior on each link/anchor elem
- if(this.options.length)
- {
- this.$el.find('li').each(function(i, li)
- {
- var $li = $(li),
- $anchor = $li.children( 'a.popupmenu-option' ),
- menuFunc = menu.options[i].func;
- // click event
- if($anchor.length && menuFunc)
- {
- $anchor.click(function(event)
- {
- menuFunc(event, menu.options[i]);
- });
- }
- // cache the anchor as a jq obj within the options obj
- menu.options[i].$li = $li;
- });
- }
- return this;
- },
- // get the absolute position/offset for the menu
- _getShownPosition : function( clickEvent )
- {
- // get element width
- var menuWidth = this.$el.width();
-
- // display menu horiz. centered on click...
- 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
- return {
- top: clickEvent.pageY,
- left: x
- };
- },
- // 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');
- this.$el.css( this._getShownPosition(clickEvent));
- this._setUpCloseBehavior();
- this.$el.show();
- },
- // 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()
- {
- // function to close popup and unbind itself
- var menu = this;
- var closePopupWhenClicked = function($elClicked)
- {
- $elClicked.bind("click.close_popup", function()
- {
- menu.remove();
- $elClicked.unbind("click.close_popup");
- });
- };
- // bind to current, parent, and sibling frames
- closePopupWhenClicked($(window.document));
- closePopupWhenClicked($(window.top.document));
- _.each(window.top.frames, function(siblingFrame)
- {
- closePopupWhenClicked($(siblingFrame.document));
- });
- },
- // 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 it's 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 it's html
- findItemByHtml: function(html)
- {
- return this.options[(this.findIndexByHtml(html))];
- },
- // string representation
- toString: function()
- {
- return 'PopupMenu';
- },
-
- // template
- template: function(id, options)
- {
- // initialize template
- var tmpl = '<ul id="' + id + '-menu" class="dropdown-menu">';
-
- // check item number
- if (options.length > 0)
- {
- // add option
- for (var i in options)
- {
- // get item
- var item = options[i];
-
- // check for divider
- if (item.divider)
- {
- // add divider
- tmpl += '<li class="divider"></li>';
- } else {
- // identify header
- if(item.header)
- {
- tmpl += '<li class="head"><a href="javascript:void(0);">' + item.html + '</a></li>';
- } else {
- // add href
- if (item.href)
- {
- tmpl += '<li><a href="' + item.href + '"';
- tmpl += 'target="' + item.target + '"';
- } else
- tmpl += '<li><a href="javascript:void(0);"';
-
- // add class
- tmpl += 'class="popupmenu-option">'
-
- // add target
- if (item.checked)
- tmpl += '<span class="fa-icon-ok"></span>';
-
- // add html
- tmpl += item.html;
- }
- }
- }
- } else
- tmpl += '<li>No Options.</li>';
-
- // return
- return tmpl + '</ul>';
- }
- });
- // -----------------------------------------------------------------------------
- // 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( linkHref )
- {
- 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:
- var f;
- switch( linkTarget )
- {
- // relocate the center panel
- case '_parent':
- window.parent.location = linkHref;
- break;
- // relocate the entire window
- case '_top':
- window.top.location = linkHref;
- break;
- // Http request target is a window named demolocal on the local box
- //TODO: I still don't understand this option (where the hell does f get set? confirm?)
- case 'demo':
- if( f === undefined || f.closed ){
- f = window.open( linkHref, linkTarget );
- f.creator = self;
- }
- 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;
- }