Current File : /home/jvzmxxx/wiki1/extensions/Flow/modules/flow/ui/widgets/mw.flow.ui.BoardDescriptionWidget.js
( function ( $ ) {
	/**
	 * Flow board description widget
	 *
	 * @class
	 * @extends OO.ui.Widget
	 *
	 * @constructor
	 * @param {mw.flow.dm.Board} boardModel The board model
	 * @param {Object} [config]
	 * @cfg {jQuery} [$existing] A jQuery object of the existing contents of the board description
	 * @cfg {string} [specialPageCategoryLink] Link to the localized Special:Categories page
	 * @cfg {jQuery} [$categories] A jQuery object of the existing board categories
	 * @cfg {Object} [editor] Config options to pass to mw.flow.ui.EditorWidget
	 */
	mw.flow.ui.BoardDescriptionWidget = function mwFlowUiBoardDescriptionWidget( boardModel, config ) {
		var $content = $();

		config = config || {};

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

		this.board = boardModel;
		this.attachModel( this.board.getDescription() );

		// Since the content is already displayed, we will "steal" the already created
		// node to avoid having to render it twice.
		// Upon creation of this widget, this should be the rendering of the data
		// that exists in the model. Take care, however, that if this widget is
		// used elsewhere, the model and rendering must be synchronized.
		if ( config.$existing ) {
			$content = config.$existing;
		}

		this.$content = $( '<div>' )
			.addClass( 'flow-ui-boardDescriptionWidget-content' )
			.append( $content );

		this.api = new mw.flow.dm.APIHandler(
			this.board.getPageTitle().getPrefixedDb(),
			{
				currentRevision: this.model.getRevisionId()
			}
		);

		this.editor = new mw.flow.ui.EditorWidget( $.extend( {
			saveMsgKey: mw.user.isAnon() ? 'flow-edit-header-submit-anonymously' : 'flow-edit-header-submit',
			classes: [ 'flow-ui-boardDescriptionWidget-editor' ]
		}, config.editor ) );
		this.editor.toggle( false );

		this.anonWarning = new mw.flow.ui.AnonWarningWidget();
		this.anonWarning.toggle( false );

		this.error = new OO.ui.LabelWidget( {
			classes: [ 'flow-ui-boardDescriptionWidget-error flow-errors errorbox' ]
		} );
		this.error.toggle( false );

		this.captcha = new mw.flow.dm.Captcha();
		this.captchaWidget = new mw.flow.ui.CaptchaWidget( this.captcha );

		this.button = new OO.ui.ButtonWidget( {
			label: mw.msg( 'flow-edit-header-link' ),
			framed: false,
			icon: 'edit',
			flags: 'progressive',
			classes: [ 'flow-ui-boardDescriptionWidget-editButton' ]
		} );

		if ( !this.model.isEditable() ) {
			this.button.toggle( false );
		}

		this.categoriesWidget = new mw.flow.ui.CategoriesWidget( this.board, {
			specialPageCategoryLink: config.specialPageCategoryLink
		} );
		if ( config.$categories ) {
			this.addCategoriesFromDom( config.$categories );
		}

		// Events
		this.button.connect( this, { click: 'onEditButtonClick' } );
		this.editor.connect( this, {
			saveContent: 'onEditorSaveContent',
			cancel: 'onEditorCancel'
		} );

		// NOTE: Unlike other widgets, in the board description widget there is
		// no use listening to change events in the content, because:
		// 1. Any time the model changes, the widget must re-request the content
		// in fixed-html format.
		// 2. Due to the above, we initialize the widget already with the content
		// from the DOM, and assume that all other changes to the content happen
		// from the widget itself, which would run its own api request for the
		// content in the proper format.
		//
		// The events below are specific listeners for specific behaviors identified
		// as necessary.
		this.model.connect( this, { editableChange: 'onModelEditableChange' } );

		// Initialize
		this.$element
			.append(
				this.error.$element,
				this.captchaWidget.$element,
				this.anonWarning.$element,
				this.button.$element,
				this.$content,
				this.editor.$element,
				this.categoriesWidget.$element
			)
			.addClass( 'flow-ui-boardDescriptionWidget' );
	};

	/* Initialization */

	OO.inheritClass( mw.flow.ui.BoardDescriptionWidget, OO.ui.Widget );

	/* Events */

	/**
	 * @event saveContent
	 * The content of the description was saved
	 */

	/**
	 * @event cancel
	 * The edit operation on the description was canceled
	 */

	/* Methods */

	/**
	 * Respond to changes in the model's editable status
	 *
	 * @param {boolean} editable Description is editable
	 */
	mw.flow.ui.BoardDescriptionWidget.prototype.onModelEditableChange = function ( editable ) {
		this.button.toggle( editable && !this.editor.isVisible() );
	};

	/**
	 * Respond to edit button click. Switch to the editor widget
	 */
	mw.flow.ui.BoardDescriptionWidget.prototype.onEditButtonClick = function () {
		var widget = this,
			contentFormat = this.editor.getInitialFormat() || 'wikitext';

		// Hide the edit button, any errors, and the content
		this.button.toggle( false );
		this.error.toggle( false );
		this.categoriesWidget.toggle( false );
		this.$content.addClass( 'oo-ui-element-hidden' );

		this.editor.toggle( true );

		// Load the editor
		this.editor.pushPending();
		this.anonWarning.toggle( true );
		this.editor.activate();

		// Get the description from the API
		this.api.getDescription( contentFormat )
			.then(
				function ( desc ) {
					var content = OO.getProp( desc, 'content', 'content' ),
						format = OO.getProp( desc, 'content', 'format' );

					if ( content !== undefined && format !== undefined ) {
						// Give it to the editor
						widget.editor.setContent( content, format );

						// Update revisionId in the API
						widget.api.setCurrentRevision( widget.model.getRevisionId() );
					}
				},
				// Error fetching description
				function ( error ) {
					// Display error
					widget.error.setLabel( mw.msg( 'flow-error-external', error ) );
					widget.error.toggle( true );

					// Return to read mode
					widget.showContent( false );
				}
			)
			.always( function () {
				// Unset pending editor
				widget.editor.popPending();
				// Focus again: pending editors are disabled and can't be focused
				widget.editor.focus();
			} );

	};

	/**
	 * Respond to an editor cancel event
	 * @fires cancel
	 */
	mw.flow.ui.BoardDescriptionWidget.prototype.onEditorCancel = function () {
		this.showContent( true );
		this.emit( 'cancel' );
	};

	/**
	 * Respond to editor save event. Save the content and display the new description.
	 *
	 * @param {string} content Content to save
	 * @param {string} contentFormat Format of content
	 * @fires saveContent
	 */
	mw.flow.ui.BoardDescriptionWidget.prototype.onEditorSaveContent = function ( content, format ) {
		var widget = this,
			captchaResponse;

		this.editor.pushPending();

		captchaResponse = this.captchaWidget.getResponse();

		this.error.setLabel( '' );
		this.error.toggle( false );

		this.api.saveDescription( content, format, captchaResponse )
			.then( function ( newRevisionId ) {
				widget.captchaWidget.toggle( false );

				// Update revisionId in the API
				widget.api.setCurrentRevision( newRevisionId );
				// Get the new header to update the dm.BoardDescription
				// The widget should update automatically by its events
				return widget.api.getDescription( 'html' );
			} )
			.then( function ( description ) {
				// Update the model
				widget.model.populate( description );
				return widget.api.getDescription( 'fixed-html' );
			} )
			.then( function ( desc ) {
				// Change the actual content
				widget.$content.empty().append( $.parseHTML( desc.content.content ) );
				widget.emit( 'saveContent' );
			} )
			.then( null, function ( errorCode, errorObj ) {
				widget.captcha.update( errorCode, errorObj );
				if ( !widget.captcha.isRequired() ) {
					widget.error.setLabel( new OO.ui.HtmlSnippet( errorObj.error && errorObj.error.info || errorObj.exception ) );
					widget.error.toggle( true );
				}
			} )
			// Get the new categories
			.then( this.api.getCategories.bind( this.api ) )
			.then( function ( catObject ) {
				var cat, title,
					categories = {};

				for ( cat in catObject ) {
					title = mw.Title.newFromText( catObject[ cat ].title );
					categories[ title.getName() ] = { exists: catObject[ cat ].missing === undefined };
				}
				// Update the board data model
				widget.board.clearCategories();
				widget.board.setCategoriesFromObject( categories );
			} )
			// Remove the editor and show content
			.then( function () {
				widget.showContent( true );
			} )
			// Always pop pending for the editor
			.always( function () {
				widget.editor.popPending();
			} );
	};

	/**
	 * Add categories from a jQuery object. This is so that we can feed categories from the
	 * nojs rendering of the page without having the widget to ask the API for the categories
	 * when it just loads.
	 *
	 * @param {jQuery} [$categoriesWrapper] Categories div wrapper
	 */
	mw.flow.ui.BoardDescriptionWidget.prototype.addCategoriesFromDom = function ( $categoriesWrapper ) {
		var categories = {};

		$categoriesWrapper.find( '.flow-board-header-category-item a' ).each( function () {
			categories[ $( this ).text() ] = {
				exists: !$( this ).hasClass( 'new' )
			};
		} );

		this.board.setCategoriesFromObject( categories );
		this.categoriesWidget.toggle( this.board.hasCategories() );
	};

	/**
	 * Show the content instead of the editor
	 *
	 * @param {boolean} [hideErrors] Hide error bar
	 */
	mw.flow.ui.BoardDescriptionWidget.prototype.showContent = function ( hideErrors ) {
		// Hide the editor
		this.editor.toggle( false );
		this.anonWarning.toggle( false );

		if ( !hideErrors ) {
			// Hide errors
			this.error.toggle( false );
		}

		// Display the edit button and the content
		this.button.toggle( true );
		this.$content.removeClass( 'oo-ui-element-hidden' );
		this.categoriesWidget.toggle( this.board.hasCategories() );
	};

	/**
	 * Attach a model to the widget
	 *
	 * @param {mw.flow.dm.BoardDescription} model Board model
	 */
	mw.flow.ui.BoardDescriptionWidget.prototype.attachModel = function ( model ) {
		if ( this.model ) {
			this.model.disconnect( this );
		}

		this.model = model;
	};

}( jQuery ) );