Current File : /home/jvzmxxx/wiki1/extensions/Flow/modules/engine/components/common/flow-component-menus.js
/*!
 * Contains flow-menu functionality.
 */

( function ( $, mw ) {
	/**
	 * Binds handlers for flow-menu.
	 * @param {jQuery} $container
	 * @this FlowComponent
	 * @constructor
	 */
	function FlowComponentMenusFeatureMixin( $container ) {
		// Bind events to this instance
		this.bindComponentHandlers( FlowComponentMenusFeatureMixin.eventHandlers );

		// Bind element handlers
		this.bindNodeHandlers( FlowComponentMenusFeatureMixin.UI.events );

		// Bind special toggle menu handler
		$container
			.on(
				'click.FlowBoardComponent mousedown.FlowBoardComponent mouseup.FlowBoardComponent focusin.FlowBoardComponent focusout.FlowBoardComponent',
				'.flow-menu',
				this.getDispatchCallback( 'toggleHoverMenu' )
			);
	}
	OO.initClass( FlowComponentMenusFeatureMixin );

	FlowComponentMenusFeatureMixin.eventHandlers = {};
	FlowComponentMenusFeatureMixin.UI = {
		events: {
			loadHandlers: {},
			interactiveHandlers: {}
		}
	};

	//
	// Event handler methods
	//

	/**
	 * On click, focus, and blur of hover menu events, decides whether or not to hide or show the expanded menu
	 * @param {Event} event
	 */
	function flowComponentMenusFeatureMixinToggleHoverMenuCallback( event ) {
		var $this = $( event.target ),
			$menu = $this.closest( '.flow-menu' );

		if ( event.type === 'click' ) {
			// If the caret was clicked, toggle focus
			if ( $this.closest( '.flow-menu-js-drop' ).length ) {
				$menu.toggleClass( 'focus' );

				// This trick lets us wait for a blur event from A instead on body, to later hide the menu on outside click
				if ( $menu.hasClass( 'focus' ) ) {
					$menu.find( '.flow-menu-js-drop' ).find( 'a' ).focus();
				}
			} else if ( $this.is( 'a, button' ) ) {
				// Remove the focus from the menu so it can hide after clicking on a link or button
				setTimeout( function () {
					if ( $this.is( ':focus' ) ) {
						$this.blur();
					}
				}, 50 );
			}

			$menu.removeData( 'mousedown' );
		} else if ( event.type === 'mousedown' ) {
			// Fix for Chrome: it triggers blur when you click on the scrollbar! Let's prevent that.
			$menu.data( 'mousedown', true );
		} else if ( event.type === 'mouseup' ) {
			// Chrome fix ^
			$menu.removeData( 'mousedown' );
		} else if ( event.type === 'focusin' ) {
			// If we are focused on a menu item (eg. tabbed in), open the whole menu
			$menu.addClass( 'focus' );
		} else if ( event.type === 'focusout' && !$menu.find( 'a' ).filter( ':focus' ).length ) {
			// If we lost focus, make sure no other element in this menu has focus, and then hide the menu
			setTimeout( function () {
				if ( !$menu.data( 'mousedown' ) && !$menu.find( 'a' ).filter( ':focus' ).length ) {
					$menu.removeClass( 'focus' );
				}
			}, 250 );
		}
	}
	FlowComponentMenusFeatureMixin.eventHandlers.toggleHoverMenu = flowComponentMenusFeatureMixinToggleHoverMenuCallback;

	//
	// On element-click handlers
	//

	/**
	 * Allows you to open a flow-menu from a secondary click handler elsewhere.
	 * Uses data-flow-menu-target="< foo .flow-menu"
	 * @param {Event} event
	 */
	function flowComponentMenusFeatureElementMenuToggleCallback( event ) {
		var $this = $( this ),
			flowComponent = mw.flow.getPrototypeMethod( 'component', 'getInstanceByElement' )( $this ),
			target = $this.data( 'flowMenuTarget' ),
			$target = $.findWithParent( $this, target ),
			$deferred = $.Deferred();

		event.preventDefault();

		if ( !$target || !$target.length ) {
			flowComponent.debug( 'Could not find openFlowMenu target', arguments );
			return $deferred.reject().promise();
		}

		$target.find( '.flow-menu-js-drop' ).trigger( 'click' );

		return $deferred.resolve().promise();
	}
	FlowComponentMenusFeatureMixin.UI.events.interactiveHandlers.menuToggle = flowComponentMenusFeatureElementMenuToggleCallback;

	//
	// On element-load handlers
	//

	/**
	 * When a menu appears, check if it's already got the focus class. If so, re-focus it.
	 * @param {jQuery} $menu
	 */
	function flowComponentMenusFeatureElementLoadCallback( $menu ) {
		// For some reason, this menu is visible, but lacks physical focus
		// This happens when you clone an activated flow-menu
		if ( $menu.hasClass( 'focus' ) && !$menu.find( 'a' ).filter( ':focus' ).length ) {
			// Give it focus again
			$menu.find( '.flow-menu-js-drop' ).find( 'a' ).focus();
		}
	}
	FlowComponentMenusFeatureMixin.UI.events.loadHandlers.menu = flowComponentMenusFeatureElementLoadCallback;

	//
	// Private functions
	//

	// Mixin to FlowComponent
	mw.flow.mixinComponent( 'component', FlowComponentMenusFeatureMixin );
}( jQuery, mediaWiki ) );