/*!
 * jQuery UI Menubar @VERSION
 * http://jqueryui.com
 *
 * Copyright 2013 jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 *
 * http://api.jqueryui.com/menubar/
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 *	jquery.ui.position.js
 *	jquery.ui.menu.js
 */
(function( $ ) {

$.widget( "ui.menubar", {
	version: "@VERSION",
	options: {
		items: "li",
		menus: "ul",
		icons: {
			dropdown: "ui-icon-triangle-1-s"
		},
		position: {
			my: "left top",
			at: "left bottom"
		},

		// callbacks
		select: null
	},

	_create: function() {
		// Top-level elements containing the submenu-triggering elem
		this.menuItems = this.element.children( this.options.items );

		// Links or buttons in menuItems, triggers of the submenus
		this.items = this.menuItems.children( "button, a" );

		// Keep track of open submenus
		this.openSubmenus = 0;

		this._initializeWidget();
		this._initializeMenuItems();
		this._initializeItems();
	},

	_initializeWidget: function() {
		this.element
			.addClass( "ui-menubar ui-widget-header ui-helper-clearfix" )
			.attr( "role", "menubar" );
		this._on({
			keydown: function( event ) {
				var active;

				// If we are in a nested sub-sub-menu and we see an ESCAPE
				// we must close recursively.
				if ( event.keyCode === $.ui.keyCode.ESCAPE &&
						this.active &&
						this.active.menu( "collapse", event ) !== true ) {
					active = this.active;
					this.active.blur();
					this._close( event );
					$( event.target ).blur().mouseleave();
					active.prev().focus();
				}
			},
			focusin: function() {
				clearTimeout( this.closeTimer );
			},
			focusout: function( event ) {
				this.closeTimer = this._delay( function() {
					this._close( event );
					this.items.attr( "tabIndex", -1 );
					this.lastFocused.attr( "tabIndex", 0 );
				}, 150 );
			},
			"mouseenter .ui-menubar-item": function() {
				clearTimeout( this.closeTimer );
			}
		} );
	},

	_initializeMenuItems: function() {
		var subMenus,
			menubar = this;

		this.menuItems
			.addClass( "ui-menubar-item" )
			.attr( "role", "presentation" )
			// TODO why do these not work when moved to CSS?
			.css({
				"border-width": "1px",
				"border-style": "hidden"
			});

		subMenus = this.menuItems.children( menubar.options.menus ).menu({
			position: {
				within: this.options.position.within
			},
			select: function( event, ui ) {
				// TODO don't hardcode markup selectors
				ui.item.parents( "ul.ui-menu:last" ).hide();
				menubar._close();
				ui.item.parents( ".ui-menubar-item" ).children().first().focus();
				menubar._trigger( "select", event, ui );
			},
			menus: this.options.menus
		})
			.hide()
			.attr({
				"aria-hidden": "true",
				"aria-expanded": "false"
			});

		this._on( subMenus, {
			keydown: function( event ) {
				// TODO why is this needed?
				$( event.target ).attr( "tabIndex", 0 );
				var parentButton,
					menu = $( this );
				// TODO why are there keydown events on a hidden menu?
				if ( menu.is( ":hidden" ) ) {
					return;
				}
				switch ( event.keyCode ) {
				case $.ui.keyCode.LEFT:
					// TODO why can't this call menubar.previous()?
					parentButton = menubar.active.prev( ".ui-button" );

					if ( this.openSubmenus ) {
						this.openSubmenus--;
					} else if ( this._hasSubMenu( parentButton.parent().prev() ) ) {
						menubar.active.blur();
						menubar._open( event, parentButton.parent().prev().find( ".ui-menu" ) );
					} else {
						parentButton.parent().prev().find( ".ui-button" ).focus();
						menubar._close( event );
						this.open = true;
					}

					event.preventDefault();
					// TODO same as above where it's set to 0
					$( event.target ).attr( "tabIndex", -1 );
					break;
				case $.ui.keyCode.RIGHT:
					this.next( event );
					event.preventDefault();
					break;
				}
			},
			focusout: function( event ) {
				// TODO why does this have to use event.target? Is that different from currentTarget?
				$( event.target ).removeClass( "ui-state-focus" );
			}
		});

		this.menuItems.each(function( index, menuItem ) {
			menubar._identifyMenuItemsNeighbors( $( menuItem ), menubar, index );
		});

	},

	_hasSubMenu: function( menuItem ) {
		return $( menuItem ).children( this.options.menus ).length > 0;
	},

	// TODO get rid of these - currently still in use in _move
	_identifyMenuItemsNeighbors: function( menuItem, menubar, index ) {
		var collectionLength = this.menuItems.length,
			isFirstElement = ( index === 0 ),
			isLastElement = ( index === ( collectionLength - 1 ) );

		if ( isFirstElement ) {
			menuItem.data( "prevMenuItem", $( this.menuItems[collectionLength - 1]) );
			menuItem.data( "nextMenuItem", $( this.menuItems[index+1]) );
		} else if ( isLastElement ) {
			menuItem.data( "nextMenuItem", $( this.menuItems[0]) );
			menuItem.data( "prevMenuItem", $( this.menuItems[index-1]) );
		} else {
			menuItem.data( "nextMenuItem", $( this.menuItems[index+1]) );
			menuItem.data( "prevMenuItem", $( this.menuItems[index-1]) );
		}
	},

	_initializeItems: function() {
		var menubar = this;

		this._focusable( this.items );
		this._hoverable( this.items );

		// let only the first item receive focus
		this.items.slice(1).attr( "tabIndex", -1 );

		this.items.each(function( index, item ) {
			menubar._initializeItem( $( item ), menubar );
		});
	},

	_initializeItem: function( anItem ) {
		var menuItemHasSubMenu = this._hasSubMenu( anItem.parent() );

		anItem
			.addClass( "ui-button ui-widget ui-button-text-only ui-menubar-link" )
			.attr( "role", "menuitem" )
			.wrapInner( "<span class='ui-button-text'></span>" );

		this._on( anItem, {
			focus:	function(){
				anItem.attr( "tabIndex", 0 );
				anItem.addClass( "ui-state-focus" );
				event.preventDefault();
			},
			focusout:  function(){
				anItem.attr( "tabIndex", -1 );
				this.lastFocused = anItem;
				anItem.removeClass( "ui-state-focus" );
				event.preventDefault();
			}
		} );

		if ( menuItemHasSubMenu ) {
			this._on( anItem, {
				click: this._mouseBehaviorForMenuItemWithSubmenu,
				focus: this._mouseBehaviorForMenuItemWithSubmenu,
				mouseenter: this._mouseBehaviorForMenuItemWithSubmenu
			});

			this._on( anItem, {
				keydown: function( event ) {
					switch ( event.keyCode ) {
					case $.ui.keyCode.SPACE:
					case $.ui.keyCode.UP:
					case $.ui.keyCode.DOWN:
						this._open( event, $( event.target ).next() );
						event.preventDefault();
						break;
					case $.ui.keyCode.LEFT:
						this.previous( event );
						event.preventDefault();
						break;
					case $.ui.keyCode.RIGHT:
						this.next( event );
						event.preventDefault();
						break;
					case $.ui.keyCode.TAB:
						break;
					}
				}
			});

			anItem.attr( "aria-haspopup", "true" );
			if ( this.options.icons ) {
				anItem.append( "<span class='ui-button-icon-secondary ui-icon " + this.options.icons.dropdown + "'></span>" );
				anItem.removeClass( "ui-button-text-only" ).addClass( "ui-button-text-icon-secondary" );
			}
		} else {
			this._on( anItem, {
				click: function() {
					if ( this.active ) {
						this._close();
					} else {
						this.open = true;
						this.active = $( anItem ).parent();
					}
				},
				mouseenter: function() {
					if ( this.open ) {
						this.stashedOpenMenu = this.active;
						this._close();
					}
				},
				keydown: function( event ) {
					if ( event.keyCode === $.ui.keyCode.LEFT ) {
						this.previous( event );
						event.preventDefault();
					} else if ( event.keyCode === $.ui.keyCode.RIGHT ) {
						this.next( event );
						event.preventDefault();
					}
				}
			});
		}
	},

	// TODO silly name, too much complexity
	// TODO why is this used for three types of events?
	_mouseBehaviorForMenuItemWithSubmenu: function( event ) {
		var isClickingToCloseOpenMenu, menu;

		// ignore triggered focus event
		if ( event.type === "focus" && !event.originalEvent ) {
			return;
		}
		event.preventDefault();

		menu = $(event.target).parents( ".ui-menubar-item" ).children( this.options.menus );

		// If we have an open menu and we see a click on the menuItem
		// and the menu thereunder is the same as the active menu, close it.
		// Succinctly: toggle menu open / closed  on the menuItem
		isClickingToCloseOpenMenu = event.type === "click" &&
			menu.is( ":visible" ) &&
			this.active &&
			this.active[0] === menu[0];

		if ( isClickingToCloseOpenMenu ) {
			this._close();
			return;
		}
		if ( event.type === "mouseenter" ) {
			this.element.find( ":focus" ).focusout();
			if ( this.stashedOpenMenu ) {
				this._open( event, menu);
			}
			this.stashedOpenMenu = undefined;
		}
		// If we already opened a menu and then changed to be "over" another MenuItem ||
		// we clicked on a new menuItem (whether open or not) or if we auto expand (i.e.
		// we expand regardless of click if there is a submenu
		if ( ( this.open && event.type === "mouseenter" ) || event.type === "click" ) {
			clearTimeout( this.closeTimer );
			this._open( event, menu );
			// Stop propagation so that menuItem mouseenter doesn't fire.  If it does it
			// takes the "selected" status off off of the first element of the submenu.
			event.stopPropagation();
		}
	},

	_destroy : function() {
		this.menuItems
			.removeClass( "ui-menubar-item" )
			.removeAttr( "role" )
			.css({
				"border-width": "",
				"border-style": ""
			});

		this.element
			.removeClass( "ui-menubar ui-widget-header ui-helper-clearfix" )
			.removeAttr( "role" )
			.unbind( ".menubar" );

		this.items
			.unbind( ".menubar" )
			.removeClass( "ui-button ui-widget ui-button-text-only ui-menubar-link ui-state-default" )
			.removeAttr( "role" )
			.removeAttr( "aria-haspopup" )
			.children( ".ui-icon" ).remove();

		// TODO fix this
		if ( false ) {
			// Does not unwrap
			this.items.children( "span.ui-button-text" ).unwrap();
		} else {
			// Does "unwrap"
			this.items.children( "span.ui-button-text" ).each( function(){
				var item = $( this );
				item.parent().html( item.html() );
			});
		}

		this.element.find( ":ui-menu" )
			.menu( "destroy" )
			.show()
			.removeAttr( "aria-hidden" )
			.removeAttr( "aria-expanded" )
			.removeAttr( "tabindex" )
			.unbind( ".menubar" );
	},

	_collapseActiveMenu: function() {
		if ( !this.active.is( ":ui-menu" ) ) {
			return;
		}
		this.active
			.menu( "collapseAll" )
			.hide()
			.attr({
				"aria-hidden": "true",
				"aria-expanded": "false"
			})
			.closest( this.options.items ).removeClass( "ui-state-active" );
	},

	_close: function() {
		if ( !this.active ) {
			return;
		}

		this._collapseActiveMenu();

		this.active = null;
		this.open = false;
		this.openSubmenus = 0;
	},

	_open: function( event, menu ) {
		var menuItem = menu.closest( ".ui-menubar-item" );

		if ( this.active && this.active.length &&
				this._hasSubMenu( this.active.closest( this.options.items ) ) ) {
					this._collapseActiveMenu();
		}

		menuItem.addClass( "ui-state-active" );
		// workaround when clicking a non-menu item, then hovering a menu, then going back
		// this way afterwards its still possible to tab back to a menubar, even if its
		// the wrong item
		// see also "click menu-less item, hover in and out of item with menu" test in menubar_core
		if ( !this.lastFocused ) {
			this.lastFocused = menu.prev();
		}

		this.active = menu
			.show()
			.position( $.extend({
				of: menuItem
			}, this.options.position ) )
			.removeAttr( "aria-hidden" )
			.attr( "aria-expanded", "true" )
			.menu( "focus", event, menu.children( ".ui-menu-item" ).first()  )
			.focus();

		this.open = true;
	},

	next: function( event ) {
		function shouldOpenNestedSubMenu() {
			return this.active &&
				this._hasSubMenu( this.active.closest( this.options.items ) ) &&
				this.active.data( "uiMenu" ) &&
				this.active.data( "uiMenu" ).active &&
				this.active.data( "uiMenu" ).active.has( ".ui-menu" ).length;
		}

		if ( this.open ) {
			if ( shouldOpenNestedSubMenu.call( this ) ) {
				// Track number of open submenus and prevent moving to next menubar item
				this.openSubmenus++;
				return;
			}
		}
		this.openSubmenus = 0;
		this._move( "next", event );
	},

	previous: function( event ) {
		if ( this.open && this.openSubmenus ) {
			// Track number of open submenus and prevent moving to previous menubar item
			this.openSubmenus--;
			return;
		}
		this.openSubmenus = 0;
		this._move( "prev", event );
	},

	_move: function( direction, event ) {
		var closestMenuItem = $( event.target ).closest( ".ui-menubar-item" ),
			nextMenuItem = closestMenuItem.data( direction + "MenuItem" ),
			focusableTarget = nextMenuItem.find( ".ui-button" );

		if ( this.open ) {
			if ( this._hasSubMenu( nextMenuItem ) ) {
				this._open( event, nextMenuItem.children( ".ui-menu" ) );
			} else {
				this._collapseActiveMenu();
				nextMenuItem.find( ".ui-button" ).focus();
				this.open = true;
			}
		} else {
			closestMenuItem.find( ".ui-button" );
			focusableTarget.focus();
		}
	}

});

}( jQuery ));
