summaryrefslogtreecommitdiff
path: root/www/wiki/resources/src/mediawiki/htmlform/htmlform.Checker.js
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/resources/src/mediawiki/htmlform/htmlform.Checker.js')
-rw-r--r--www/wiki/resources/src/mediawiki/htmlform/htmlform.Checker.js180
1 files changed, 180 insertions, 0 deletions
diff --git a/www/wiki/resources/src/mediawiki/htmlform/htmlform.Checker.js b/www/wiki/resources/src/mediawiki/htmlform/htmlform.Checker.js
new file mode 100644
index 00000000..3f53b636
--- /dev/null
+++ b/www/wiki/resources/src/mediawiki/htmlform/htmlform.Checker.js
@@ -0,0 +1,180 @@
+( function ( mw, $ ) {
+
+ 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( '.error' );
+ if ( !this.$errorBox.length ) {
+ this.$errorBox = $( '<span>' );
+ 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, $.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( [] );
+ 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;
+
+ if ( info.valid ) {
+ that.setErrors( [], forceReplacement );
+ } else {
+ that.setErrors( info.messages, forceReplacement );
+ }
+ } ).fail( function () {
+ that.currentValue = null;
+ that.setErrors( [] );
+ } );
+
+ return currentRequestInternal;
+ };
+
+ /**
+ * Display errors associated with the form element
+ * @param {Array} errors Error messages. Each error message will be appended to a
+ * `<span>` 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 ( errors, forceReplacement ) {
+ var $oldErrorBox, tagName, showFunc, text, replace,
+ $errorBox = this.$errorBox;
+
+ if ( errors.length === 0 ) {
+ $errorBox.slideUp( function () {
+ $errorBox
+ .removeAttr( 'class' )
+ .empty();
+ } );
+ } else {
+ // Match behavior of HTMLFormField::formatErrors(), <span> or <ul>
+ // depending on the count.
+ tagName = errors.length === 1 ? 'span' : 'ul';
+
+ // We have to animate the replacement if we're changing the tag. We
+ // also want to if told to by the caller (i.e. to make it visually
+ // obvious that the changed field value gives the same error) or if
+ // the error text changes (because it makes more sense than
+ // changing the text with no animation).
+ replace = (
+ forceReplacement || $errorBox.length > 1 ||
+ $errorBox[ 0 ].tagName.toLowerCase() !== tagName
+ );
+ if ( !replace ) {
+ text = $( '<' + tagName + '>' )
+ .append( errors.map( function ( e ) {
+ return errors.length === 1 ? e : $( '<li>' ).append( e );
+ } ) );
+ if ( text.text() !== $errorBox.text() ) {
+ replace = true;
+ }
+ }
+
+ $oldErrorBox = $errorBox;
+ if ( replace ) {
+ this.$errorBox = $errorBox = $( '<' + tagName + '>' );
+ $errorBox.hide();
+ $oldErrorBox.after( this.$errorBox );
+ }
+
+ showFunc = function () {
+ if ( $oldErrorBox !== $errorBox ) {
+ $oldErrorBox
+ .removeAttr( 'class' )
+ .detach();
+ }
+ $errorBox
+ .attr( 'class', 'error' )
+ .empty()
+ .append( errors.map( function ( e ) {
+ return errors.length === 1 ? e : $( '<li>' ).append( e );
+ } ) )
+ .slideDown();
+ };
+ if ( $oldErrorBox !== $errorBox && $oldErrorBox.hasClass( 'error' ) ) {
+ $oldErrorBox.slideUp( showFunc );
+ } else {
+ showFunc();
+ }
+ }
+
+ return this;
+ };
+
+}( mediaWiki, jQuery ) );