Current File : /home/jvzmxxx/wiki1/extensions/Flow/modules/engine/components/flow-registry.js
/*!
 * Creates and manages the component registry.
 * We expand upon OOjs in several ways here:
 * 1. Allow mixinClasses to have their constructor functions to be called (at initComponent).
 * 2. Automatically call all parent constructors from inheritClasses (at initComponent).
 * 3. Create a global instance registry of components on a page, and also create a registry for each component type.
 * 4. Have the ability to fetch individual prototype methods from classes in the registry, as they are out of scope.
 */

( function ( $, mw ) {
	/** @class mw.flow */
	mw.flow = mw.flow || {}; // create mw.flow globally

	var _componentRegistry = new OO.Registry();

	/**
	 * Instantiate one or more new FlowComponents.
	 * Uses data-flow-component to find the right class, and returns that new instance.
	 * Accepts one or more container elements in $container. If multiple, returns an array of FlowBoardComponents.
	 * @param {jQuery} $container
	 * @return {FlowComponent|boolean|Array} The created FlowComponent instance, or an
	 *  array of FlowComponent instances, or boolean false in case of an error.
	 */
	function initFlowComponent( $container ) {
		var a, i, componentName, componentBase;

		/**
		 * @private
		 * Deep magic: This crazy little function becomes the "real" top-level constructor
		 * It recursively calls every parent so that we don't have to do it manually in a Component constructor
		 * @return {FlowComponent}
		 */
		function _RecursiveConstructor() {
			var constructors = [],
				parent = this.constructor.parent,
				i, j, parentReturn;

			// Find each parent class
			while ( parent ) {
				constructors.push( parent );
				parent = parent.parent;
			}

			// Call each parent in reverse (starting with the base class and moving up the chain)
			for ( i = constructors.length; i--; ) {
				// Call each mixin constructor
				for ( j = 0; j < constructors[ i ].static.mixinConstructors.length; j++ ) {
					constructors[ i ].static.mixinConstructors[ j ].apply( this, arguments );
				}

				// Call this class constructor
				parentReturn = constructors[ i ].apply( this, arguments );

				if ( parentReturn && parentReturn.constructor ) {
					// If the parent returned an instantiated class (cached), return that instead
					return parentReturn;
				}
			}

			// Run any post-instantiation handlers
			this.emitWithReturn( 'instantiationComplete', this );
		}

		if ( !$container || !$container.length ) {
			// No containers found
			mw.flow.debug( 'Will not instantiate: no $container.length', arguments );
			return false;
		} else if ( $container.length > 1 ) {
			// Too many elements; instantiate them all
			for ( a = [], i = $container.length; i--; ) {
				a.push( initFlowComponent( $( $container[ i ] ) ) );
			}
			return a;
		}

		// Find out which component this is
		componentName = $container.data( 'flow-component' );
		// Get that component
		componentBase = _componentRegistry.lookup( componentName );
		if ( componentBase ) {
			// Return the new instance of that FlowComponent, via our _RecursiveConstructor method
			OO.inheritClass( _RecursiveConstructor, componentBase );
			return new _RecursiveConstructor( $container );
		}

		// Don't know what kind of component this is.
		mw.flow.debug( 'Unknown FlowComponent: ', componentName, arguments );
		return false;
	}
	mw.flow.initComponent = initFlowComponent;

	/**
	 * Registers a given FlowComponent into the component registry, and also has it inherit another class using the
	 * prototypeName argument (defaults to 'component', which returns FlowComponent).
	 * @param {string} name Name of component to register
	 * @param {Function} constructorClass Actual class to link to that name
	 * @param {string} [prototypeName='component'] A base class which this one will inherit
	 */
	function registerFlowComponent( name, constructorClass, prototypeName ) {
		if ( name !== 'component' ) {
			// Inherit a base class; defaults to FlowComponent
			OO.inheritClass( constructorClass, _componentRegistry.lookup( prototypeName || 'component' ) );
		}

		// Create the instance registry for this component
		constructorClass._instanceRegistry = [];
		constructorClass._instanceRegistryById = {};

		// Assign the OOjs static name to this class
		constructorClass.static.name = name;

		// Allow mixins to use their constructor
		constructorClass.static.mixinConstructors = [];

		// Register the component class
		_componentRegistry.register( name, constructorClass );
	}
	mw.flow.registerComponent = registerFlowComponent;

	/**
	 * For when you want to call a specific function from a class's prototype.
	 *
	 *     mw.flow.getPrototypeMethod( 'board', 'getInstanceByElement' )( $el );
	 *
	 * @param {string} className
	 * @param {string} methodName
	 * @param {*} [context]
	 * @return {Function}
	 */
	function getFlowPrototypeMethod( className, methodName, context ) {
		var registeredClass = _componentRegistry.lookup( className ),
			method;

		if ( !registeredClass ) {
			mw.flow.debug( 'Failed to find FlowComponent.', arguments );
			return $.noop;
		}

		method = registeredClass.prototype[ methodName ];
		if ( !method ) {
			mw.flow.debug( 'Failed to find FlowComponent method.', arguments );
			return $.noop;
		}

		return $.proxy( method, context || registeredClass );
	}
	mw.flow.getPrototypeMethod = getFlowPrototypeMethod;

	/**
	 * Mixes in the given mixinClass to be copied to an existing class, by name.
	 * @param {string} targetName Target component
	 * @param {Function} mixinClass Class with extension to add to target
	 */
	function mixinFlowComponent( targetName, mixinClass ) {
		var registeredClass = _componentRegistry.lookup( targetName );

		if ( !registeredClass ) {
			mw.flow.debug( 'Failed to find FlowComponent to extend.', arguments );
			return;
		}

		OO.mixinClass( registeredClass, mixinClass );

		// Allow mixins to use their constructors (in init)
		if ( typeof mixinClass === 'function' ) {
			registeredClass.static.mixinConstructors.push( mixinClass );
		}
	}
	mw.flow.mixinComponent = mixinFlowComponent;
}( jQuery, mediaWiki ) );