Current File : /home/jvzmxxx/wiki1/extensions/Flow/modules/flow/ui/widgets/mw.flow.ui.TopicMenuSelectWidget.js
( function ( $ ) {
	/**
	 * Flow topic list widget
	 *
	 * @class
	 * @extends OO.ui.MenuSelectWidget
	 *
	 * @constructor
	 * @param {mw.flow.dm.System} system System model
	 * @param {Object} [config]
	 * @cfg {number} [tocPostLimit=50] The number of topics in the ToC per API request
	 */
	mw.flow.ui.TopicMenuSelectWidget = function mwFlowUiTopicMenuSelectWidget( system, config ) {
		config = config || {};

		// Parent constructor
		mw.flow.ui.TopicMenuSelectWidget.parent.call( this, config );

		// Properties
		this.system = system;
		this.board = this.system.getBoard();
		this.tocPostLimit = config.tocPostLimit || 50;
		// Keep a reference to the topic option widgets by the topic Id
		// so we can call them directly
		this.topics = {};

		// Flags for infinite scroll
		// Mark whether the process of loading is undergoing so we won't trigger it multiple times at once
		this.loadingMoreTopics = false;
		// Mark whether there are no more topics available so we can stop triggering infinite scroll
		this.noMoreTopics = false;

		// Load more option
		this.loadingMoreOptionWidget = new OO.ui.MenuOptionWidget( {
			data: null,
			classes: [ 'flow-ui-topicMenuSelectWidget-loadmore', 'flow-loading' ]
		} );

		// Events
		this.connect( this, { choose: 'onTopicChoose' } );
		this.$element.on( 'scroll', this.onMenuScroll.bind( this ) );
		this.board.connect( this, {
			add: 'addTopics',
			remove: 'removeTopics',
			clear: 'clearTopics',
			topicContentChange: 'onTopicContentChange'
		} );

		// Initialize
		this.$element.addClass( 'flow-ui-topicMenuSelectWidget' );
	};

	/* Initialization */

	OO.inheritClass( mw.flow.ui.TopicMenuSelectWidget, OO.ui.MenuSelectWidget );

	/* Methods */

	mw.flow.ui.TopicMenuSelectWidget.prototype.destroy = function () {
		this.board.disconnect( this );
	};

	/**
	 * Respond to model topic content change and update the ToC content
	 *
	 * @param {mw.flow.dm.Topic} topic Topic
	 */
	mw.flow.ui.TopicMenuSelectWidget.prototype.onTopicContentChange = function ( topic ) {
		var topicWidget = this.topics[ topic.getId() ];

		if ( topicWidget ) {
			topicWidget.setLabel( topic.getContent( 'plaintext' ) );
		}
	};

	/**
	 * Respond to scrolling of the menu. If we are close to the
	 * bottom, call for more topics.
	 */
	mw.flow.ui.TopicMenuSelectWidget.prototype.onMenuScroll = function () {
		var actualHeight, naturalHeight, scrollTop, isNearBottom;

		// Do nothing if we're already fetching topics
		// or if there are no more topics to fetch
		if ( this.loadingMoreTopics || this.noMoreTopics ) {
			return true;
		}

		actualHeight = this.$element.height();
		naturalHeight = this.$element.prop( 'scrollHeight' );
		scrollTop = this.$element.scrollTop();
		isNearBottom = scrollTop + actualHeight > naturalHeight - 100;

		if ( isNearBottom ) {
			this.getMoreTopics();
		}
	};

	/**
	 * Respond to topic choose
	 *
	 * @param {OO.ui.MenuOptionWidget} item Chosen menu item
	 * @fires topic
	 */
	mw.flow.ui.TopicMenuSelectWidget.prototype.onTopicChoose = function ( item ) {
		var topic = item.getData(),
			topicId = topic && topic.getId();

		if ( topicId ) {
			this.emit( 'topic', topicId );
		}
	};

	/**
	 * Get more topics from the queue
	 *
	 * @return {jQuery.Promise} Promise that is resolved when all
	 *  available topics in the response have been added to the
	 *  flow.dm.Board
	 */
	mw.flow.ui.TopicMenuSelectWidget.prototype.getMoreTopics = function () {
		var widget = this;

		this.loadingMoreTopics = true;
		this.system.fetchMoreTopics()
			.then( function ( hasMoreTopicsInApi ) {
				widget.noMoreTopics = !hasMoreTopicsInApi;
				if ( widget.noMoreTopics ) {
					// Remove the load more widget
					widget.removeItems( [ widget.loadingMoreOptionWidget ] );
				}
			} )
			.always( function () {
				widget.loadingMoreTopics = false;
			} );
	};

	/**
	 * Add topics to the ToC list
	 *
	 * @param {mw.flow.dm.Topic[]} items Topic data items
	 * @param {number} index Location to add
	 */
	mw.flow.ui.TopicMenuSelectWidget.prototype.addTopics = function ( items, index ) {
		var i, len, optionWidget,
			widgets = [];

		for ( i = 0, len = items.length; i < len; i++ ) {
			optionWidget = this.topics[ items[ i ].getId() ];
			if ( !optionWidget ) {
				optionWidget = new OO.ui.MenuOptionWidget( {
					data: items[ i ],
					label: items[ i ].getContent( 'plaintext' ),
					classes: items[ i ].getModerationState() === 'lock' ?
						[ 'flow-ui-topicMenuSelectWidget-locked' ] :
						[]
				} );
			}
			widgets.push( optionWidget );
		}

		this.addItems( widgets, index );

		// Move the 'load more' to the end
		if ( !this.noMoreTopics ) {
			this.addItems( [ this.loadingMoreOptionWidget ] );
		}
	};

	/**
	 * Clear all topics from the ToC list
	 */
	mw.flow.ui.TopicMenuSelectWidget.prototype.clearTopics = function () {
		this.clearItems();
		this.topics = {};
	};

	/**
	 * Remove topics from the ToC list
	 *
	 * @param {mw.flow.dm.Topic[]} items Topic data items to remove
	 */
	mw.flow.ui.TopicMenuSelectWidget.prototype.removeTopics = function ( items ) {
		var i, len, itemId, optionWidget,
			widgets = [];

		for ( i = 0, len = items.length; i < len; i++ ) {
			itemId = items[ i ].getId();
			optionWidget = this.topics[ itemId ];
			widgets.push( optionWidget );
		}

		this.removeItems( widgets );
	};

	/**
	 * Extend addItems to also add to the topic reference item
	 *
	 * @param {OO.ui.OptionWidget[]} items Items to add
	 * @param {number} [index] Index to insert items after
	 */
	mw.flow.ui.TopicMenuSelectWidget.prototype.addItems = function ( items, index ) {
		var i, len;

		for ( i = 0, len = items.length; i < len; i++ ) {
			if ( items[ i ].getData() ) {
				this.topics[ items[ i ].getData().getId() ] = items[ i ];
			}
		}

		// Parent call
		mw.flow.ui.TopicMenuSelectWidget.parent.prototype.addItems.call( this, items, index );
	};

	/**
	 * Extend removeItems to also remove to the topic reference item
	 *
	 * @param {OO.ui.OptionWidget[]} items Items to remove
	 */
	mw.flow.ui.TopicMenuSelectWidget.prototype.removeItems = function ( items ) {
		var i, len;

		for ( i = 0, len = items.length; i < len; i++ ) {
			if ( items[ i ].getData() ) {
				delete this.topics[ items[ i ].getData().getId() ];
			}
		}

		// Parent call
		mw.flow.ui.TopicMenuSelectWidget.parent.prototype.removeItems.call( this, items );
	};

}( jQuery ) );