Current File : /home/jvzmxxx/wiki1/extensions/MobileFrontend/resources/mobile.editor.common/EditorOverlayBase.js
( function ( M, $ ) {
	var Overlay = M.require( 'mobile.startup/Overlay' ),
		PageGateway = M.require( 'mobile.startup/PageGateway' ),
		browser = M.require( 'mobile.startup/Browser' ).getSingleton(),
		Icon = M.require( 'mobile.startup/Icon' ),
		toast = M.require( 'mobile.startup/toast' ),
		user = M.require( 'mobile.startup/user' );

	/**
	 * 'Edit' button
	 * @param {OO.ui.ToolGroup} toolGroup
	 * @param {Object} config
	 * @ignore
	 */
	function EditVeTool( toolGroup, config ) {
		config = config || {};
		config.classes = [ 'visual-editor' ];
		EditVeTool.super.call( this, toolGroup, config );
	}
	OO.inheritClass( EditVeTool, OO.ui.Tool );

	EditVeTool.static.name = 'editVe';
	EditVeTool.static.icon = 'edit';
	EditVeTool.static.group = 'editorSwitcher';
	EditVeTool.static.title = mw.msg( 'mobile-frontend-editor-switch-visual-editor' );
	/**
	 * click handler
	 */
	EditVeTool.prototype.onSelect = function () {
		// will be overridden later
	};
	/**
	 * Toolbar update state handler.
	 */
	EditVeTool.prototype.onUpdateState = function () {
		// do nothing
	};

	/**
	 * Base class for EditorOverlay
	 * @class EditorOverlayBase
	 * @extends Overlay
	 * @uses Icon
	 * @uses user
	 * @param {Object} options Configuration options
	 */
	function EditorOverlayBase( options ) {
		var self = this;

		if ( options.isNewPage ) {
			options.placeholder = mw.msg( 'mobile-frontend-editor-placeholder-new-page', mw.user );
		}
		// change the message to request a summary when not in article namespace
		if ( mw.config.get( 'wgNamespaceNumber' ) !== 0 ) {
			options.summaryRequestMsg = mw.msg( 'mobile-frontend-editor-summary' );
		}
		this.pageGateway = new PageGateway( options.api );
		this.editCount = user.getEditCount();
		this.isNewPage = options.isNewPage;
		this.isNewEditor = options.isNewEditor;
		this.sectionId = options.sectionId;
		this.config = mw.config.get( 'wgMFEditorOptions' );
		this.sessionId = options.sessionId;
		this.overlayManager = options.overlayManager;
		this.allowCloseWindow = mw.confirmCloseWindow( {
			// Returns true if content has changed
			test: function () {
				// Check if content has changed
				return self.hasChanged();
			},

			// Message to show the user, if content has changed
			message: mw.msg( 'mobile-frontend-editor-cancel-confirm' ),
			// Event namespace
			namespace: 'editwarning'
		} );

		Overlay.apply( this, arguments );
	}

	OO.mfExtend( EditorOverlayBase, Overlay, {
		/**
		 * @inheritdoc
		 * @cfg {Object} defaults Default options hash.
		 * @cfg {OverlayManager} defaults.overlayManager instance
		 * @cfg {mw.Api} defaults.api to interact with
		 * @cfg {boolean} defaults.hasToolbar Whether the editor has a toolbar or not. When
		 *  disabled a header will be show instead.
		 * @cfg {string} defaults.continueMsg Caption for the next button on edit form which takes
		 * you to the screen that shows a preview and license information.
		 * @cfg {string} defaults.cancelMsg Caption for cancel button on edit form.
		 * @cfg {string} defaults.closeMsg Caption for a button that takes you back to editing
		 * from edit preview screen.
		 * @cfg {string} defaults.summaryRequestMsg Header above edit summary input field asking
		 * the user to summarize the changes they made to the page.
		 * @cfg {string} defaults.summaryMsg A placeholder with examples for the summary input
		 * field asking user what they changed.
		 * @cfg {string} defaults.placeholder Placeholder text for empty sections.
		 * @cfg {string} defaults.waitMsg Text that displays while a page edit is being saved.
		 * @cfg {string} defaults.waitIcon HTML of the icon that displays while a page edit
		 * is being saved.
		 * @cfg {string} defaults.captchaMsg Placeholder for captcha input field.
		 * @cfg {string} defaults.captchaTryAgainMsg A message shown when user enters wrong CAPTCHA
		 * and a new one is displayed.
		 * @cfg {string} defaults.switchMsg Label for button that allows the user to switch between
		 * two different editing interfaces.
		 * @cfg {string} defaults.licenseMsg Text and link of the license, under which this contribution will be
		 * released to inform the user.
		 */
		defaults: $.extend( {}, Overlay.prototype.defaults, {
			hasToolbar: false,
			continueMsg: mw.msg( 'mobile-frontend-editor-continue' ),
			cancelMsg: mw.msg( 'mobile-frontend-editor-cancel' ),
			closeMsg: mw.msg( 'mobile-frontend-editor-keep-editing' ),
			summaryRequestMsg: mw.msg( 'mobile-frontend-editor-summary-request' ),
			summaryMsg: mw.msg( 'mobile-frontend-editor-summary-placeholder' ),
			placeholder: mw.msg( 'mobile-frontend-editor-placeholder' ),
			waitMsg: mw.msg( 'mobile-frontend-editor-wait' ),
			// icons.spinner can't be used, the spinner class changes to display:none in onStageChanges
			waitIcon: new Icon( {
				name: 'spinner',
				additionalClassNames: 'savespinner loading'
			} ).toHtmlString(),
			captchaMsg: mw.msg( 'mobile-frontend-account-create-captcha-placeholder' ),
			captchaTryAgainMsg: mw.msg( 'mobile-frontend-editor-captcha-try-again' ),
			switchMsg: mw.msg( 'mobile-frontend-editor-switch-editor' ),
			confirmMsg: mw.msg( 'mobile-frontend-editor-cancel-confirm' ),
			licenseMsg: undefined
		} ),
		/** @inheritdoc **/
		templatePartials: $.extend( {}, Overlay.prototype.templatePartials, {
			editHeader: mw.template.get( 'mobile.editor.common', 'editHeader.hogan' ),
			previewHeader: mw.template.get( 'mobile.editor.common', 'previewHeader.hogan' ),
			saveHeader: mw.template.get( 'mobile.editor.common', 'saveHeader.hogan' )
		} ),
		/** @inheritdoc **/
		template: mw.template.get( 'mobile.editor.common', 'EditorOverlayBase.hogan' ),
		/** @inheritdoc **/
		className: 'overlay editor-overlay',
		events: $.extend( {}, Overlay.prototype.events, {
			// FIXME: This should be .close (see bug 71203)
			'click .back': 'onClickBack',
			'click .continue': 'onClickContinue',
			'click .submit': 'onClickSubmit'
		} ),
		/**
		 * Logs an event to  http://meta.wikimedia.org/wiki/Schema:Edit
		 * @param {Object} data
		 */
		log: function ( data ) {
			mw.track( 'mf.schemaEdit', $.extend( data, {
				editor: this.editor,
				editingSessionId: this.sessionId
			} ) );
		},

		/**
		 * If this is a new article, require confirmation before saving.
		 * @method
		 * @return {boolean} The user confirmed saving
		 */
		confirmSave: function () {
			if ( this.isNewPage &&
				!window.confirm( mw.msg( 'mobile-frontend-editor-new-page-confirm', mw.user ) )
			) {
				return false;
			} else {
				return true;
			}
		},
		/**
		 * Executed when page save is complete. Handles reloading the page, showing toast
		 * messages, and setting mobile edit cookie.
		 * @method
		 */
		onSaveComplete: function () {
			var msg,
				title = this.options.title,
				self = this;

			// FIXME: use generic method for following 3 lines
			this.pageGateway.invalidatePage( title );

			if ( this.isNewPage ) {
				msg = mw.msg( 'mobile-frontend-editor-success-new-page' );
			} else if ( this.isNewEditor ) {
				msg = mw.msg( 'mobile-frontend-editor-success-landmark-1' );
			} else {
				msg = mw.msg( 'mobile-frontend-editor-success' );
			}
			toast.showOnPageReload( msg, 'success' );

			// Ensure we don't lose this event when logging
			this.log( {
				action: 'saveSuccess'
			} );
			if ( self.sectionLine ) {
				title = title + '#' + self.sectionLine;
			}

			$( window ).off( 'beforeunload.mfeditorwarning' );

			// Set a cookie for 30 days indicating that this user has edited from
			// the mobile interface.
			$.cookie( 'mobileEditor', 'true', {
				expires: 30
			} );

			window.location = mw.util.getUrl( title );
			if ( self.sectionLine ) {
				// since the path and only the hash has changed it has not triggered a refresh so forcefully refresh
				window.location.reload();
			}
		},
		/**
		 * Report load errors back to the user. Silently record the error using EventLogging.
		 * @method
		 * @param {string} text Text of message to display to user
		 */
		reportError: function ( text ) {
			toast.show( text, 'error' );
		},
		/**
		 * Prepares the penultimate screen before saving.
		 * Expects to be overridden by child class.
		 * @method
		 */
		onStageChanges: function () {
			this.showHidden( '.save-header, .save-panel' );
			this.log( {
				action: 'saveIntent'
			} );
			// Scroll to the top of the page, so that the summary input is visible
			// (even if overlay was scrolled down when editing) and weird iOS header
			// problems are avoided (header position not updating to the top of the
			// screen, instead staying lower until a subsequent scroll event).
			window.scrollTo( 0, 1 );
		},
		/**
		 * Executed when the editor clicks the save button. Expects to be overridden by child
		 * class. Checks if the save needs to be confirmed.
		 * @method
		 */
		onSaveBegin: function () {
			this.confirmAborted = false;
			// Ask for confirmation in some cases
			if ( !this.confirmSave() ) {
				this.confirmAborted = true;
				return;
			}
			this.log( {
				action: 'saveAttempt'
			} );
		},
		/** @inheritdoc **/
		postRender: function () {
			// Add a class so editor can make some Android 2 specific customisations.
			if ( browser.isAndroid2() ) {
				this.$el.addClass( 'android-2' );
			}
			// log edit attempt
			this.log( {
				action: 'ready'
			} );

			// decide what happens, when the user clicks the continue button
			if ( this.config.skipPreview ) {
				// skip the preview and save the changes
				this.nextStep = 'onSaveBegin';
				this.$( '.continue' ).text( this.defaults.saveMsg );
			} else {
				// default: show the preview step
				this.nextStep = 'onStageChanges';
			}
			Overlay.prototype.postRender.apply( this );
			this.showHidden( '.initial-header' );
		},
		/**
		 * Back button click handler
		 * @method
		 */
		onClickBack: $.noop,
		/**
		 * Exit handler
		 */
		onExit: function () {
			Overlay.prototype.onExit.apply( this, arguments );
			// log cancel attempt
			this.log( {
				action: 'abort',
				mechanism: 'cancel',
				type: this.hasChanged() ? 'abandon' : 'nochange'
			} );
		},
		/**
		 * Submit button click handler
		 */
		onClickSubmit: function () {
			this.onSaveBegin();
		},
		/**
		 * Continue button click handler
		 */
		onClickContinue: function () {
			this[this.nextStep]();
		},
		/**
		 * @inheritdoc
		 */
		hide: function () {
			var self = this;
			if ( this.hasChanged() ) {
				OO.ui.confirm( mw.msg( 'mobile-frontend-editor-cancel-confirm' ) ).done( function ( confirmed ) {
					if ( confirmed ) {
						self.allowCloseWindow.release();
						Overlay.prototype.hide.call( self );
					}
				} );
			} else {
				this.allowCloseWindow.release();
				Overlay.prototype.hide.call( this );
			}
		},
		/**
		 * Check, if the user should be asked if they really want to leave the page.
		 * Returns false, if he hasn't made changes, otherwise true.
		 * @param {boolean} [force] Whether this function should always return false
		 * @return {boolean}
		 */
		shouldConfirmLeave: function ( force ) {
			if ( force || !this.hasChanged() ) {
				return false;
			}
			return true;

		},
		/**
		 * Checks whether the state of the thing being edited as changed. Expects to be
		 * implemented by child class.
		 * @method
		 */
		hasChanged: $.noop,
		/**
		 * Handles a failed save due to a CAPTCHA provided by ConfirmEdit extension.
		 * @method
		 * @param {Object} details Details returned from the api.
		 */
		handleCaptcha: function ( details ) {
			var self = this,
				$input = this.$( '.captcha-word' );

			if ( this.captchaShown ) {
				$input.val( '' );
				$input.attr( 'placeholder', this.options.captchaTryAgainMsg );
				setTimeout( function () {
					$input.attr( 'placeholder', self.options.captchaMsg );
				}, 2000 );
			}

			// handle different mime types different
			if ( details.mime.indexOf( 'image/' ) === 0 ) {
				// image based CAPTCHA's like provided by FancyCaptcha, ReCaptcha or similar
				this.$( '.captcha-panel#question' ).detach();
				this.$( '.captcha-panel img' ).attr( 'src', details.url );
			} else {
				// not image based CAPTCHA.
				this.$( '.captcha-panel #image' ).detach();
				if ( details.mime.indexOf( 'text/html' ) === 0 ) {
					// handle mime type of HTML as HTML content (display as-is).
					// QuestyCaptcha now have default MIME type "text/html": see T147606
					this.$( '.captcha-panel #question' ).html( details.question );
				} else {
					// handle mime types (other than image based ones and HTML based ones) as plain text by default.
					// e.g. MathCaptcha (solve a math formula) or
					// SimpleCaptcha (simple math formula)
					this.$( '.captcha-panel #question' ).text( details.question );
				}
			}

			this.showHidden( '.save-header, .captcha-panel' );
			this.captchaShown = true;
		}
	} );

	M.define( 'mobile.editor.common/EditorOverlayBase', EditorOverlayBase )
		.deprecate( 'modules/editor/EditorOverlayBase' );
}( mw.mobileFrontend, jQuery ) );