Merge htmlform.checker.js into special.userlogin.signup.js module

Bug: T253362
Change-Id: I749c1955b765ce4ca1b9a191d1a051933a21f16a
This commit is contained in:
DannyS712 2020-05-22 00:20:10 +00:00
parent 2ee29c7e37
commit 743edb1be0
5 changed files with 201 additions and 204 deletions

View file

@ -32,7 +32,6 @@
"mw.cookie",
"mw.experiments",
"mw.viewport",
"mw.htmlform.*",
"mw.visibleTimeout"
]
},

View file

@ -759,15 +759,6 @@ return [
],
'targets' => [ 'desktop', 'mobile' ],
],
'mediawiki.htmlform.checker' => [
'scripts' => [
'resources/src/mediawiki.htmlform.checker.js',
],
'dependencies' => [
'mediawiki.util',
],
'targets' => [ 'desktop', 'mobile' ],
],
'mediawiki.htmlform.ooui' => [
'scripts' => [
'resources/src/mediawiki.htmlform.ooui/Element.js',
@ -2144,6 +2135,7 @@ return [
'remoteBasePath' => "$wgResourceBasePath/resources/src/mediawiki.special.createaccount",
'packageFiles' => [
'signup.js',
'HtmlformChecker.js'
],
'messages' => [
'createacct-emailrequired',
@ -2154,7 +2146,7 @@ return [
'dependencies' => [
'mediawiki.api',
'mediawiki.jqueryMsg',
'mediawiki.htmlform.checker',
'mediawiki.util',
],
],
'mediawiki.special.userlogin.signup.styles' => [

View file

@ -1,191 +0,0 @@
( function () {
// FIXME: mw.htmlform.Element also sets this to empty object
mw.htmlform = {};
/**
* @class mw.htmlform.Checker
*/
/**
* A helper class to add validation to non-OOUI HtmlForm fields.
*
* @constructor
* @param {jQuery} $element Form field generated by HTMLForm
* @param {Function} validator Validation callback
* @param {string} validator.value Value of the form field to be validated
* @param {jQuery.Promise} validator.return The promise should be resolved
* with an object with two properties: Boolean 'valid' to indicate success
* or failure of validation, and an array 'messages' to be passed to
* setErrors() on failure.
*/
mw.htmlform.Checker = function ( $element, validator ) {
this.validator = validator;
this.$element = $element;
this.$errorBox = $element.next( '.errorbox' );
if ( !this.$errorBox.length ) {
this.$errorBox = $( '<div>' );
this.$errorBox.hide();
$element.after( this.$errorBox );
}
this.currentValue = this.$element.val();
};
/**
* Attach validation events to the form element
*
* @param {jQuery} [$extraElements] Additional elements to listen for change
* events on.
* @return {mw.htmlform.Checker}
* @chainable
*/
mw.htmlform.Checker.prototype.attach = function ( $extraElements ) {
var $e,
// We need to hook to all of these events to be sure we are
// notified of all changes to the value of an <input type=text>
// field.
events = 'keyup keydown change mouseup cut paste focus blur';
$e = this.$element;
if ( $extraElements ) {
$e = $e.add( $extraElements );
}
$e.on( events, mw.util.debounce( 1000, this.validate.bind( this ) ) );
return this;
};
/**
* Validate the form element
*
* @return {jQuery.Promise}
*/
mw.htmlform.Checker.prototype.validate = function () {
var currentRequestInternal,
that = this,
value = this.$element.val();
// Abort any pending requests.
if ( this.currentRequest && this.currentRequest.abort ) {
this.currentRequest.abort();
}
if ( value === '' ) {
this.currentValue = value;
this.setErrors( true, [] );
return;
}
this.currentRequest = currentRequestInternal = this.validator( value )
.done( function ( info ) {
var forceReplacement = value !== that.currentValue;
// Another request was fired in the meantime, the result we got here is no longer current.
// This shouldn't happen as we abort pending requests, but you never know.
if ( that.currentRequest !== currentRequestInternal ) {
return;
}
// If we're here, then the current request has finished, avoid calling .abort() needlessly.
that.currentRequest = undefined;
that.currentValue = value;
that.setErrors( info.valid, info.messages, forceReplacement );
} ).fail( function () {
that.currentValue = null;
that.setErrors( true, [] );
} );
return currentRequestInternal;
};
/**
* Display errors associated with the form element
*
* @param {boolean} valid Whether the input is still valid regardless of the messages
* @param {Array} errors Errorbox messages. Each errorbox message will be appended to a
* `<div>` or `<li>`, as with jQuery.append().
* @param {boolean} [forceReplacement] Set true to force a visual replacement even
* if the errors are the same. Ignored if errors are empty.
* @return {mw.htmlform.Checker}
* @chainable
*/
mw.htmlform.Checker.prototype.setErrors = function ( valid, errors, forceReplacement ) {
var $oldErrorBox, showFunc, $text, replace,
$errorBox = this.$errorBox;
if ( errors.length === 0 ) {
// FIXME: Use CSS transition
// eslint-disable-next-line no-jquery/no-slide
$errorBox.slideUp( function () {
$errorBox
.removeAttr( 'class' )
.empty();
} );
} else {
// Animate the replacement if told to by the caller (i.e. to make it visually
// obvious that the changed field value gives the same errorbox) or if
// the errorbox text changes (because it makes more sense than
// changing the text with no animation).
replace = forceReplacement;
if ( !replace ) {
$text = $( '<div>' );
// Match behavior of HTMLFormField::formatErrors()
if ( errors.length === 1 ) {
$text.append( errors[ 0 ] );
} else {
$text.append( $( '<ul>' ).append( errors.map( function ( e ) {
return $( '<li>' ).append( e );
} ) ) );
}
if ( $text.text() !== $errorBox.text() ) {
replace = true;
}
}
$oldErrorBox = $errorBox;
if ( replace ) {
this.$errorBox = $errorBox = $( '<div>' );
$errorBox.hide();
$oldErrorBox.after( this.$errorBox );
}
showFunc = function () {
if ( $oldErrorBox !== $errorBox ) {
$oldErrorBox
.removeAttr( 'class' )
.detach();
}
$errorBox
.attr( 'class', valid ? 'warningbox' : 'errorbox' )
.empty();
// Match behavior of HTMLFormField::formatErrors()
if ( errors.length === 1 ) {
$errorBox.append( errors[ 0 ] );
} else {
$errorBox.append( $( '<ul>' ).append( errors.map( function ( e ) {
return $( '<li>' ).append( e );
} ) ) );
}
// FIXME: Use CSS transition
// eslint-disable-next-line no-jquery/no-slide
$errorBox.slideDown();
};
if (
$oldErrorBox !== $errorBox &&
// eslint-disable-next-line no-jquery/no-class-state
( $oldErrorBox.hasClass( 'errorbox' ) || $oldErrorBox.hasClass( 'warningbox' ) )
) {
// eslint-disable-next-line no-jquery/no-slide
$oldErrorBox.slideUp( showFunc );
} else {
showFunc();
}
}
return this;
};
}() );

View file

@ -0,0 +1,195 @@
/**
* A helper class to add validation to non-OOUI HtmlForm fields.
*
* @private
* @class mw.htmlform.Checker
*
* @constructor
* @param {jQuery} $element Form field generated by HTMLForm
* @param {Function} validator Validation callback
* @param {string} validator.value Value of the form field to be validated
* @param {jQuery.Promise} validator.return The promise should be resolved
* with an object with two properties: Boolean 'valid' to indicate success
* or failure of validation, and an array 'messages' to be passed to
* setErrors() on failure.
*/
function HtmlformChecker( $element, validator ) {
this.validator = validator;
this.$element = $element;
this.$errorBox = $element.next( '.errorbox' );
if ( !this.$errorBox.length ) {
this.$errorBox = $( '<div>' );
this.$errorBox.hide();
$element.after( this.$errorBox );
}
this.currentValue = this.$element.val();
}
/**
* Attach validation events to the form element
*
* @param {jQuery} [$extraElements] Additional elements to listen for change
* events on.
* @return {mw.htmlform.Checker}
* @chainable
*/
HtmlformChecker.prototype.attach = function ( $extraElements ) {
var $e = this.$element,
// We need to hook to all of these events to be sure we are
// notified of all changes to the value of an <input type=text>
// field.
events = 'keyup keydown change mouseup cut paste focus blur';
if ( $extraElements ) {
$e = $e.add( $extraElements );
}
$e.on( events, mw.util.debounce( 1000, this.validate.bind( this ) ) );
return this;
};
/**
* Validate the form element
*
* @return {jQuery.Promise}
*/
HtmlformChecker.prototype.validate = function () {
var currentRequestInternal,
that = this,
value = this.$element.val();
// Abort any pending requests.
if ( this.currentRequest && this.currentRequest.abort ) {
this.currentRequest.abort();
}
if ( value === '' ) {
this.currentValue = value;
this.setErrors( true, [] );
return;
}
this.currentRequest = currentRequestInternal = this.validator( value )
.done( function ( info ) {
var forceReplacement = value !== that.currentValue;
// Another request was fired in the meantime, the result we got here is no longer current.
// This shouldn't happen as we abort pending requests, but you never know.
if ( that.currentRequest !== currentRequestInternal ) {
return;
}
// If we're here, then the current request has finished, avoid calling .abort() needlessly.
that.currentRequest = undefined;
that.currentValue = value;
that.setErrors( info.valid, info.messages, forceReplacement );
} ).fail( function () {
that.currentValue = null;
that.setErrors( true, [] );
} );
return currentRequestInternal;
};
/**
* Display errors associated with the form element
*
* @param {boolean} valid Whether the input is still valid regardless of the messages
* @param {Array} errors Errorbox messages. Each errorbox message will be appended to a
* `<div>` or `<li>`, as with jQuery.append().
* @param {boolean} [forceReplacement] Set true to force a visual replacement even
* if the errors are the same. Ignored if errors are empty.
* @return {mw.htmlform.Checker}
* @chainable
*/
HtmlformChecker.prototype.setErrors = function ( valid, errors, forceReplacement ) {
var $oldErrorBox,
showFunc,
$text,
replace,
$errorBox = this.$errorBox;
if ( errors.length === 0 ) {
// FIXME: Use CSS transition
// eslint-disable-next-line no-jquery/no-slide
$errorBox.slideUp( function () {
$errorBox
.removeAttr( 'class' )
.empty();
} );
} else {
// Animate the replacement if told to by the caller (i.e. to make it visually
// obvious that the changed field value gives the same errorbox) or if
// the errorbox text changes (because it makes more sense than
// changing the text with no animation).
replace = forceReplacement;
if ( !replace ) {
$text = $( '<div>' );
// Match behavior of HTMLFormField::formatErrors()
if ( errors.length === 1 ) {
$text.append( errors[ 0 ] );
} else {
$text.append(
$( '<ul>' ).append(
errors.map( function ( e ) {
return $( '<li>' ).append( e );
} )
)
);
}
if ( $text.text() !== $errorBox.text() ) {
replace = true;
}
}
$oldErrorBox = $errorBox;
if ( replace ) {
this.$errorBox = $errorBox = $( '<div>' );
$errorBox.hide();
$oldErrorBox.after( this.$errorBox );
}
showFunc = function () {
if ( $oldErrorBox !== $errorBox ) {
$oldErrorBox
.removeAttr( 'class' )
.detach();
}
$errorBox
.attr( 'class', valid ? 'warningbox' : 'errorbox' )
.empty();
// Match behavior of HTMLFormField::formatErrors()
if ( errors.length === 1 ) {
$errorBox.append( errors[ 0 ] );
} else {
$errorBox.append(
$( '<ul>' ).append(
errors.map( function ( e ) {
return $( '<li>' ).append( e );
} )
)
);
}
// FIXME: Use CSS transition
// eslint-disable-next-line no-jquery/no-slide
$errorBox.slideDown();
};
if (
$oldErrorBox !== $errorBox &&
// eslint-disable-next-line no-jquery/no-class-state
( $oldErrorBox.hasClass( 'errorbox' ) || $oldErrorBox.hasClass( 'warningbox' ) )
) {
// eslint-disable-next-line no-jquery/no-slide
$oldErrorBox.slideUp( showFunc );
} else {
showFunc();
}
}
return this;
};
module.exports = HtmlformChecker;

View file

@ -1,6 +1,8 @@
/*!
* JavaScript for signup form.
*/
var HtmlformChecker = require( './HtmlformChecker.js' );
// When sending password by email, hide the password input fields.
$( function () {
// Always required if checked, otherwise it depends, so we use the original
@ -120,9 +122,9 @@ mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
return d.promise( { abort: apiPromise.abort } );
}
usernameChecker = new mw.htmlform.Checker( $usernameInput, checkUsername );
usernameChecker = new HtmlformChecker( $usernameInput, checkUsername );
usernameChecker.attach();
passwordChecker = new mw.htmlform.Checker( $passwordInput, checkPassword );
passwordChecker = new HtmlformChecker( $passwordInput, checkPassword );
passwordChecker.attach( $usernameInput.add( $emailInput ).add( $realNameInput ) );
} );