Current File : /home/jvzmxxx/wiki/extensions/Flow/modules/engine/components/board/flow-board.js
/*!
 * Contains the base constructor for FlowBoardComponent.
 * @todo Clean up the remaining code that may not need to be here.
 */

( function ( $, mw ) {
	/**
	 * Constructor class for instantiating a new Flow board.
	 *
	 *    <div class="flow-component" data-flow-component="board" data-flow-id="rqx495tvz888x5ur">...</div>
	 *
	 * @param {jQuery} $container
	 * @extends FlowBoardAndHistoryComponentBase
	 * @mixins FlowComponentEventsMixin
	 * @mixins FlowComponentEnginesMixin
	 * @mixins FlowBoardComponentApiEventsMixin
	 * @mixins FlowBoardComponentInteractiveEventsMixin
	 * @mixins FlowBoardComponentLoadEventsMixin
	 * @mixins FlowBoardComponentLoadMoreFeatureMixin
	 * @mixins FlowBoardComponentVisualEditorFeatureMixin
	 *
	 * @constructor
	 */
	function FlowBoardComponent( $container ) {
		var uri = new mw.Uri( location.href ),
			anchorUid = String( location.hash.match( /[0-9a-z]{16,19}$/i ) || '' ),
			highlightUid;

		// Default API submodule for FlowBoard URLs is to fetch a topiclist
		this.Api.setDefaultSubmodule( 'view-topiclist' );

		// Set up the board
		if ( this.reinitializeContainer( $container ) === false ) {
			// Failed to init for some reason
			return false;
		}

		// Handle URL parameters.  If topic_showPostId is used, there should also be an
		// anchor.
		if ( anchorUid ) {
			if ( uri.query.fromnotif ) {
				highlightUid = uri.query.topic_showPostId;
				_flowHighlightPost( $container, highlightUid, 'newer' );
			} else {
				highlightUid = anchorUid;
				_flowHighlightPost( $container, highlightUid );
			}
		} else {
			// There is a weird bug with url ending with #flow-post-xxxx
			// and full height side rail.
			// We only enable the full height when we don't have such url.
			$container.addClass( 'flow-full-height-side-rail' );
		}

		_overrideWatchlistNotification();
	}
	OO.initClass( FlowBoardComponent );

	// Register
	mw.flow.registerComponent( 'board', FlowBoardComponent, 'boardAndHistoryBase' );

	//
	// Methods
	//

	/**
	 * Sets up the board and base properties on this class.
	 * Returns either FALSE for failure, or jQuery object of old nodes that were replaced.
	 * @param {jQuery|boolean} $container
	 * @return {boolean|jQuery}
	 */
	function flowBoardComponentReinitializeContainer( $container ) {
		if ( $container === false ) {
			return false;
		}

		// Trigger this on FlowBoardAndHistoryComponentBase
		// @todo use EventEmitter to do this?
		var $retObj = FlowBoardComponent.parent.prototype.reinitializeContainer.call( this, $container ),
		// Find any new (or previous) elements
			$header = $container.find( '.flow-board-header' ).addBack().filter( '.flow-board-header:first' ),
			$boardNavigation = $container.find( '.flow-board-navigation' ).addBack().filter( '.flow-board-navigation:first' ),
			$board = $container.find( '.flow-board' ).addBack().filter( '.flow-board:first' );

		if ( $retObj === false ) {
			return false;
		}

		// Remove any of the old elements that are still in use
		if ( $header.length ) {
			if ( this.$header ) {
				$retObj = $retObj.add( this.$header.replaceWith( $header ) );
				this.$header.remove();
			}

			this.$header = $header;
		}
		if ( $boardNavigation.length ) {
			if ( this.$boardNavigation ) {
				$retObj = $retObj.add( this.$boardNavigation.replaceWith( $boardNavigation ) );
				this.$boardNavigation.remove();
			}

			this.$boardNavigation = $boardNavigation;
		}
		if ( $board.length ) {
			if ( this.$board ) {
				$retObj = $retObj.add( this.$board.replaceWith( $board ) );
				this.$board.remove();
			}

			this.$board = $board;
		}

		// Second, verify that this board in fact exists
		if ( !this.$board || !this.$board.length ) {
			// You need a board, dammit!
			this.debug( 'Could not find .flow-board', arguments );
			return false;
		}

		this.emitWithReturn( 'makeContentInteractive', this );

		// Initialize editors, turning them from textareas into editor objects
		if ( typeof this.editorTimer === 'undefined' ) {
			/*
			 * When this method is first run, all page elements are initialized.
			 * We probably don't need editor immediately, so defer loading it
			 * to speed up the rest of the work that needs to be done.
			 */
			this.editorTimer = setTimeout( $.proxy( function ( $container ) { this.emitWithReturn( 'initializeEditors', $container ); }, this, $container ), 20000 );
		} else {
			/*
			 * Subsequent calls here (e.g. when rendering the edit header form)
			 * should immediately initialize the editors!
			 */
			clearTimeout( this.editorTimer );
			this.emitWithReturn( 'initializeEditors', $container );
		}

		return $retObj;
	}
	FlowBoardComponent.prototype.reinitializeContainer = flowBoardComponentReinitializeContainer;

	//
	// Private functions
	//

	/**
	 * Helper receives
	 * @param {jQuery} $container
	 * @param {string} uid Anchor to scroll to
	 * @param {string} [option] 'newer' if all posts equal to or newer than uid should be
	 *  highlighted.  Otherwise, it will only highlight that post itself.
	 * @return {jQuery}
	 */
	function _flowHighlightPost( $container, uid, option ) {
		var $target = $container.find( '#flow-post-' + uid );

		// reset existing highlights
		$container.find( '.flow-post-highlighted' ).removeClass( 'flow-post-highlighted' );

		if ( option === 'newer' ) {
			$target.addClass( 'flow-post-highlight-newer' );
			if ( uid ) {
				$container.find( '.flow-post' ).each( function ( idx, el ) {
					var $el = $( el ),
						id = $el.data( 'flow-id' );
					if ( id && id > uid ) {
						$el.addClass( 'flow-post-highlight-newer' );
					}
				} );
			}
		} else {
			$target.addClass( 'flow-post-highlighted' );
		}

		return $target;
	}

	/**
	 * We want the default behavior of watch/unwatch for page. However, we
	 * do want to show our own tooltip after this has happened.
	 * We'll override mw.notify, which is fired after successfully
	 * (un)watchlisting, to stop the notification from being displayed.
	 * If the action we just intercepted was after succesful watching, we'll
	 * want to show our own tooltip instead.
	 */
	function _overrideWatchlistNotification() {
		var _notify = mw.notify;
		mw.notify = function ( message, options ) {
			// override message when we've just watched the board
			if ( options && options.tag === 'watch-self' && $( '#ca-watch' ).length ) {
				// Render a div telling the user that they have subscribed
				message = $( mw.flow.TemplateEngine.processTemplateGetFragment(
					'flow_subscribed.partial',
					{
						type: 'board',
						user: mw.user
					}
				) ).children();
			}

			return _notify.apply( this, arguments );
		};
	}
}( jQuery, mediaWiki ) );