Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F4190313
HtmlformChecker.js
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
5 KB
Referenced Files
None
Subscribers
None
HtmlformChecker.js
View Options
const
util
=
require
(
'mediawiki.util'
);
/**
* A helper class to add validation to non-OOUI HTMLForm fields.
*
* @private
* @class HtmlformChecker
*
* @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 (containing HTML strings or
* jQuery collections) 'messages' to be passed to setErrors() on failure.
*/
function
HtmlformChecker
(
$element
,
validator
)
{
this
.
validator
=
validator
;
this
.
$element
=
$element
;
this
.
$errorBox
=
$element
.
next
(
'.html-form-error'
);
if
(
!
this
.
$errorBox
.
length
)
{
this
.
$errorBox
=
$
(
'<div>'
).
addClass
(
'html-form-error'
);
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 {HtmlformChecker}
* @chainable
*/
HtmlformChecker
.
prototype
.
attach
=
function
(
$extraElements
)
{
let
$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.
const
events
=
'keyup keydown change mouseup cut paste focus blur'
;
if
(
$extraElements
)
{
$e
=
$e
.
add
(
$extraElements
);
}
$e
.
on
(
events
,
mw
.
util
.
debounce
(
this
.
validate
.
bind
(
this
),
1000
)
);
return
this
;
};
/**
* Validate the form element
*
* @return {jQuery.Promise|undefined}
*/
HtmlformChecker
.
prototype
.
validate
=
function
()
{
let
currentRequestInternal
;
const
that
=
this
;
const
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
(
(
info
)
=>
{
const
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
(
()
=>
{
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 A list of error messages with formatting. Each message may be
* a string (which will be interpreted as HTML) or a jQuery collection. They will
* be appended to `<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 {HtmlformChecker}
* @chainable
*/
HtmlformChecker
.
prototype
.
setErrors
=
function
(
valid
,
errors
,
forceReplacement
)
{
let
replace
;
let
$errorBox
=
this
.
$errorBox
;
if
(
errors
.
length
===
0
)
{
// FIXME: Use CSS transition
// eslint-disable-next-line no-jquery/no-slide
$errorBox
.
slideUp
(
()
=>
{
$errorBox
.
removeAttr
(
'class'
)
.
empty
();
}
);
}
else
{
let
$error
;
// Match behavior of HTMLFormField::formatErrors()
if
(
errors
.
length
===
1
)
{
$error
=
$
(
'<div>'
).
append
(
errors
[
0
]
);
}
else
{
$error
=
$
(
'<ul>'
).
append
(
errors
.
map
(
(
e
)
=>
$
(
'<li>'
).
append
(
e
)
)
);
}
// 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
&&
$error
.
text
()
!==
$errorBox
.
text
()
)
{
replace
=
true
;
}
const
$oldErrorBox
=
$errorBox
;
if
(
replace
)
{
this
.
$errorBox
=
$errorBox
=
$
(
'<div>'
);
$errorBox
.
hide
();
$oldErrorBox
.
after
(
this
.
$errorBox
);
}
const
oldErrorType
=
this
.
oldErrorType
||
'notice'
;
const
errorType
=
valid
?
'warning'
:
'error'
;
this
.
oldErrorType
=
errorType
;
const
showFunc
=
function
()
{
if
(
$oldErrorBox
!==
$errorBox
)
{
$oldErrorBox
.
removeAttr
(
'class'
)
.
detach
();
}
$errorBox
.
empty
();
$errorBox
.
append
(
util
.
messageBox
(
$error
[
0
],
errorType
)
);
// FIXME: Use CSS transition
// eslint-disable-next-line no-jquery/no-slide
$errorBox
.
slideDown
();
};
if
(
$oldErrorBox
!==
$errorBox
&&
(
oldErrorType
===
'error'
||
oldErrorType
===
'warning'
)
)
{
// eslint-disable-next-line no-jquery/no-slide
$oldErrorBox
.
slideUp
(
showFunc
);
}
else
{
showFunc
();
}
}
return
this
;
};
module
.
exports
=
HtmlformChecker
;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Aug 20 2026, 17:25 (11 w, 6 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
5d/fc/dbe1a6514a8429c124e0a58caf80
Default Alt Text
HtmlformChecker.js (5 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment