r79924 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r79923‎ | r79924 | r79925 >
Date:04:40, 10 January 2011
Author:krinkle
Status:ok (Comments)
Tags:
Comment:
* Consistent single quotes
* The problem with onKeyUp was that is shows errors even when initially typing a (possibly valid) address. onBlur has the downside of not giving feedback while correcting the mistake + since e-mail is the last input element at the moment, it doesn't blur on submit and required a seperate event handler.
** Solution: Bind onBlur once, and then listen for onKeyUp.
* Adding missing semicolon(s)
* Compare with strict (===) to empty string / null
* Moved non-global wfFunction to mw.util
** The "FIXME:" note was kept
* Comment "apostrophe" fixed to "at". Comments don't run :)
* Caching selector in wfUpdateMailValidityLabel
* Renaming "wfUpdateMailValidityLabel" to "updateMailValidityLabel"
( Ping r79910, r75670, r75627 )
Modified paths:
  • /trunk/phase3/resources/Resources.php (modified) (history)
  • /trunk/phase3/resources/mediawiki.special/mediawiki.special.preferences.css (modified) (history)
  • /trunk/phase3/resources/mediawiki.special/mediawiki.special.preferences.js (modified) (history)
  • /trunk/phase3/resources/mediawiki.util/mediawiki.util.js (modified) (history)

Diff [purge]

Index: trunk/phase3/resources/mediawiki.util/mediawiki.util.js
@@ -40,8 +40,8 @@
4141 // (but not Safari on Windows)
4242 } else if ( !( profile.platform == 'win' && profile.name == 'safari' )
4343 && ( profile.name == 'safari'
44 - || profile.platform == 'mac'
45 - || profile.name == 'konqueror' ) ) {
 44+ || profile.platform == 'mac'
 45+ || profile.name == 'konqueror' ) ) {
4646 mw.util.tooltipAccessKeyPrefix = 'ctrl-';
4747
4848 // Firefox 2.x
@@ -263,26 +263,26 @@
264264 * ( '#foobar' ) of that item.
265265 *
266266 * @example mw.util.addPortletLink(
267 - * 'p-tb', 'http://mediawiki.org/',
268 - * 'MediaWiki.org', 't-mworg', 'Go to MediaWiki.org ', 'm', '#t-print'
269 - * )
 267+ * 'p-tb', 'http://mediawiki.org/',
 268+ * 'MediaWiki.org', 't-mworg', 'Go to MediaWiki.org ', 'm', '#t-print'
 269+ * )
270270 *
271271 * @param portlet ID of the target portlet ( 'p-cactions' or 'p-personal' etc.)
272272 * @param href Link URL
273273 * @param text Link text (will be automatically converted to lower
274 - * case by CSS for p-cactions in Monobook)
 274+ * case by CSS for p-cactions in Monobook)
275275 * @param id ID of the new item, should be unique and preferably have
276 - * the appropriate prefix ( 'ca-', 'pt-', 'n-' or 't-' )
 276+ * the appropriate prefix ( 'ca-', 'pt-', 'n-' or 't-' )
277277 * @param tooltip Text to show when hovering over the link, without accesskey suffix
278278 * @param accesskey Access key to activate this link (one character, try
279 - * to avoid conflicts. Use $( '[accesskey=x' ).get() in the console to
280 - * see if 'x' is already used.
 279+ * to avoid conflicts. Use $( '[accesskey=x' ).get() in the console to
 280+ * see if 'x' is already used.
281281 * @param nextnode DOM node or jQuery-selector of the item that the new
282 - * item should be added before, should be another item in the same
283 - * list will be ignored if not the so
 282+ * item should be added before, should be another item in the same
 283+ * list will be ignored if not the so
284284 *
285285 * @return The DOM node of the new item (a LI element, or A element for
286 - * older skins) or null.
 286+ * older skins) or null.
287287 */
288288 'addPortletLink' : function( portlet, href, text, id, tooltip, accesskey, nextnode ) {
289289
@@ -376,10 +376,10 @@
377377 * something, replacing any previous message.
378378 *
379379 * @param message mixed The DOM-element or HTML-string to be put inside the message box]
380 - * Calling with no arguments, with an empty string or null will hide the message
 380+ * Calling with no arguments, with an empty string or null will hide the message
381381 * @param className string Used in adding a class; should be different for each
382 - * call to allow CSS/JS to hide different boxes. null = no class used.
383 - * @return Boolean True on success, false on failure
 382+ * call to allow CSS/JS to hide different boxes. null = no class used.
 383+ * @return Boolean True on success, false on failure
384384 */
385385 'jsMessage' : function( message, className ) {
386386
@@ -389,13 +389,13 @@
390390 return true; // Emptying and hiding message is intended behaviour, return true
391391
392392 } else {
393 - // We special-case skin structures provided by the software. Skins that
 393+ // We special-case skin structures provided by the software. Skins that
394394 // choose to abandon or significantly modify our formatting can just define
395395 // an mw-js-message div to start with.
396396 var $messageDiv = $( '#mw-js-message' );
397397 if ( !$messageDiv.length ) {
398398 $messageDiv = $( '<div id="mw-js-message">' );
399 - if ( mw.util.$content.parent().length ) {
 399+ if ( mw.util.$content.parent().length ) {
400400 mw.util.$content.parent().prepend( $messageDiv );
401401 } else {
402402 return false;
@@ -416,6 +416,80 @@
417417 $messageDiv.slideDown();
418418 return true;
419419 }
 420+ },
 421+
 422+ /**
 423+ * Validate a string as representing a valid e-mail address
 424+ * according to HTML5 specification. Please note the specification
 425+ * does not validate a domain with one character.
 426+ *
 427+ * FIXME: should be moved to a JavaScript validation module.
 428+ */
 429+ 'validateEmail' : function( mailtxt ) {
 430+ if( mailtxt === '' ) {
 431+ return null;
 432+ }
 433+
 434+ /**
 435+ * HTML5 defines a string as valid e-mail address if it matches
 436+ * the ABNF:
 437+ * 1 * ( atext / "." ) "@" ldh-str 1*( "." ldh-str )
 438+ * With:
 439+ * - atext : defined in RFC 5322 section 3.2.3
 440+ * - ldh-str : defined in RFC 1034 section 3.5
 441+ *
 442+ * (see STD 68 / RFC 5234 http://tools.ietf.org/html/std68):
 443+ */
 444+
 445+ /**
 446+ * First, define the RFC 5322 'atext' which is pretty easy :
 447+ * atext = ALPHA / DIGIT / ; Printable US-ASCII
 448+ "!" / "#" / ; characters not including
 449+ "$" / "%" / ; specials. Used for atoms.
 450+ "&" / "'" /
 451+ "*" / "+" /
 452+ "-" / "/" /
 453+ "=" / "?" /
 454+ "^" / "_" /
 455+ "`" / "{" /
 456+ "|" / "}" /
 457+ "~"
 458+ */
 459+ var rfc5322_atext = "a-z0-9!#$%&'*+-/=?^_`{|}�~",
 460+
 461+ /**
 462+ * Next define the RFC 1034 'ldh-str'
 463+ * <domain> ::= <subdomain> | " "
 464+ * <subdomain> ::= <label> | <subdomain> "." <label>
 465+ * <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
 466+ * <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
 467+ * <let-dig-hyp> ::= <let-dig> | "-"
 468+ * <let-dig> ::= <letter> | <digit>
 469+ */
 470+ rfc1034_ldh_str = "a-z0-9-",
 471+
 472+ HTML5_email_regexp = new RegExp(
 473+ // start of string
 474+ '^'
 475+ +
 476+ // User part which is liberal :p
 477+ '[' + rfc5322_atext + '\\.' + ']' + '+'
 478+ +
 479+ // "at"
 480+ '@'
 481+ +
 482+ // Domain first part
 483+ '[' + rfc1034_ldh_str + ']+'
 484+ +
 485+ // Second part and following are separated by a dot
 486+ '(?:\\.[' + rfc1034_ldh_str + ']+)+'
 487+ +
 488+ // End of string
 489+ '$',
 490+ // RegExp is case insensitive
 491+ 'i'
 492+ );
 493+ return (null !== mailtxt.match( HTML5_email_regexp ) );
420494 }
421495
422496 };
Index: trunk/phase3/resources/Resources.php
@@ -358,9 +358,9 @@
359359 'dependencies' => 'mediawiki.util',
360360 ),
361361 'mediawiki.special.preferences' => array(
362 - 'messages' => array( 'email-address-validity-valid', 'email-address-validity-invalid' ),
363362 'scripts' => 'resources/mediawiki.special/mediawiki.special.preferences.js',
364363 'styles' => 'resources/mediawiki.special/mediawiki.special.preferences.css',
 364+ 'messages' => array( 'email-address-validity-valid', 'email-address-validity-invalid' ),
365365 ),
366366 'mediawiki.special.changeslist' => array(
367367 'styles' => 'resources/mediawiki.special/mediawiki.special.changeslist.css',
Index: trunk/phase3/resources/mediawiki.special/mediawiki.special.preferences.css
@@ -2,12 +2,12 @@
33 padding: 2px 1em;
44 }
55 body.ltr #mw-emailaddress-validity {
6 - border-bottom-right-radius:0.8em;
7 - border-top-right-radius:0.8em;
 6+ border-bottom-right-radius: 0.8em;
 7+ border-top-right-radius: 0.8em;
88 }
99 body.rtl #mw-emailaddress-validity {
10 - border-bottom-left-radius:0.8em;
11 - border-top-left-radius:0.8em;
 10+ border-bottom-left-radius: 0.8em;
 11+ border-top-left-radius: 0.8em;
1212 }
1313 #mw-emailaddress-validity.valid {
1414 border: 1px solid #80FF80;
Index: trunk/phase3/resources/mediawiki.special/mediawiki.special.preferences.js
@@ -39,119 +39,36 @@
4040 );
4141 } );
4242
43 -// User preference form validation
44 -$( '#mw-prefs-form' )
45 - .submit( function () {
46 - var isValid = wfValidateEmail( $('#mw-input-wpemailaddress').val() );
47 - wfUpdateMailValidityLabel( isValid );
48 - if(isValid == false ) {
49 - $('#mw-input-wpemailaddress').focus();
50 - return false;
51 - }
52 - }
53 -);
54 -
55 -// Lame tip to let user know if its email is valid. See bug 22449
56 -$( '#mw-input-wpemailaddress' )
57 - .blur( function () {
58 - if( $( "#mw-emailaddress-validity" ).length == 0 ) {
59 - $(this).after( '<label for="mw-input-wpemailaddress" id="mw-emailaddress-validity"></label>' );
60 - }
61 - var isValid = wfValidateEmail( $(this).val() );
62 - wfUpdateMailValidityLabel( isValid );
63 - } );
64 -
6543 /**
6644 * Given an email validity status (true, false, null) update the label CSS class
6745 */
68 -wfUpdateMailValidityLabel = function( isValid ) {
69 - var class_to_add = isValid ? 'valid' : 'invalid';
70 - var class_to_remove = isValid ? 'invalid' : 'valid';
 46+var updateMailValidityLabel = function( mail ) {
 47+ var isValid = mw.util.validateEmail( mail ),
 48+ $label = $( '#mw-emailaddress-validity' );
7149
72 - // We allow null address
73 - if( isValid == null ) {
74 - $( '#mw-emailaddress-validity' ).text( '' )
75 - .removeClass( 'valid invalid');
76 - } else {
77 - $( '#mw-emailaddress-validity' )
78 - .text(
79 - isValid ?
80 - mediaWiki.msg( 'email-address-validity-valid' )
81 - : mediaWiki.msg( 'email-address-validity-invalid' )
82 - )
83 - .addClass( class_to_add )
84 - .removeClass( class_to_remove );
85 - }
86 -}
 50+ // We allow empty address
 51+ if( isValid === null ) {
 52+ $label.text( '' ).removeClass( 'valid invalid' );
8753
88 -/**
89 - * Validate a string as representing a valid e-mail address
90 - * according to HTML5 specification. Please note the specification
91 - * does not validate a domain with one character.
92 - *
93 - * FIXME: should be moved to a JavaScript validation module.
94 - */
95 -wfValidateEmail = function( mailtxt ) {
96 - if( mailtxt == '' ) { return null; }
 54+ // Valid
 55+ } else if ( isValid ) {
 56+ $label.text( mw.msg( 'email-address-validity-valid' ) ).addClass( 'valid' ).removeClass( 'invalid' );
9757
98 - /**
99 - * HTML 5 define a string as valid e-mail address if it matches
100 - * the ABNF :
101 - * 1 * ( atext / "." ) "@" ldh-str 1*( "." ldh-str )
102 - * With:
103 - * - atext : defined in RFC 5322 section 3.2.3
104 - * - ldh-str : defined in RFC 1034 section 3.5
105 - *
106 - * (see STD 68 / RFC 5234 http://tools.ietf.org/html/std68):
107 - */
108 -
109 - /**
110 - * First, define the RFC 5322 'atext' which is pretty easy :
111 - * atext = ALPHA / DIGIT / ; Printable US-ASCII
112 - "!" / "#" / ; characters not including
113 - "$" / "%" / ; specials. Used for atoms.
114 - "&" / "'" /
115 - "*" / "+" /
116 - "-" / "/" /
117 - "=" / "?" /
118 - "^" / "_" /
119 - "`" / "{" /
120 - "|" / "}" /
121 - "~"
122 - */
123 - var rfc5322_atext = "a-z0-9!#$%&'*+-/=?^_`{|}—~" ;
124 -
125 - /**
126 - * Next define the RFC 1034 'ldh-str'
127 - * <domain> ::= <subdomain> | " "
128 - * <subdomain> ::= <label> | <subdomain> "." <label>
129 - * <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
130 - * <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
131 - * <let-dig-hyp> ::= <let-dig> | "-"
132 - * <let-dig> ::= <letter> | <digit>
133 - */
134 - var rfc1034_ldh_str = "a-z0-9-" ;
135 -
136 - var HTML5_email_regexp = new RegExp(
137 - // start of string
138 - '^'
139 - +
140 - // User part which is liberal :p
141 - '[' + rfc5322_atext + '\\.' + ']' + '+'
142 - +
143 - // "apostrophe"
144 - '@'
145 - +
146 - // Domain first part
147 - '[' + rfc1034_ldh_str + ']+'
148 - +
149 - // Second part and following are separated by a dot
150 - '(?:\\.[' + rfc1034_ldh_str + ']+)+'
151 - +
152 - // End of string
153 - '$',
154 - // RegExp is case insensitive
155 - 'i'
156 - );
157 - return (null != mailtxt.match( HTML5_email_regexp ) );
 58+ // Not valid
 59+ } else {
 60+ $label.text( mw.msg( 'email-address-validity-invalid' ) ).addClass( 'invalid' ).removeClass( 'valid' );
 61+ }
15862 };
 63+
 64+// Lame tip to let user know if its email is valid. See bug 22449
 65+// Only bind once for 'blur' so that the user can fill it in without errors
 66+// After that look at every keypress for direct feedback if it was invalid onblur
 67+$( '#mw-input-wpemailaddress' ).one( 'blur', function() {
 68+ if ( $( '#mw-emailaddress-validity' ).length === 0 ) {
 69+ $(this).after( '<label for="mw-input-wpemailaddress" id="mw-emailaddress-validity"></label>' );
 70+ }
 71+ updateMailValidityLabel( $(this).val() );
 72+ $(this).keyup( function() {
 73+ updateMailValidityLabel( $(this).val() );
 74+ } );
 75+} );

Follow-up revisions

RevisionCommit summaryAuthorDate
r79926traversal indention. Follow-up r79924krinkle04:47, 10 January 2011
r79952Fix RFC 5322 'atext'...hashar19:55, 10 January 2011
r809751.17: MFT r79856, r79877, r79878, r79885, r79924, r79926, r79952catrope18:07, 25 January 2011

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r75627Lame user side email validator using JQuery....hashar21:01, 28 October 2010
r75670Follow up r75627 (code review comments):...hashar20:44, 29 October 2010
r79910js email validation improvment (onBlur)...hashar18:36, 9 January 2011

Comments

#Comment by Hashar (talk | contribs)   20:02, 10 January 2011

Encoding was set to latin1 (fixed with r79952).

Looks like you implemented your comment about r79910. Looks much better :b

You have removed the part that prevented a user to submit the form. Is there any reason or is that a bad cut/paste?

#Comment by Krinkle (talk | contribs)   20:43, 24 January 2011

IIRC the reason was

A) There's no longer a checker needed on submit since it now does it live onkeyup
B) There are still cases where the regex is imperfect so blocking submission didn't seem a good idea, yet. (I think there's an open bug/discussion about foo@localhost being considered invalid)

It would depend on what the server side (non-javascript fallback) does when an 'invalid' e-mailaddress is submitted. If the server side blocks saving the new preferences as well, then I think it's OK to re-attach a submit event handler and block submission and show a similar error as the server side (in this case the error could be emphasized, but a click on "Submit/Save" should not do "nothing").

If the serverside saves the rest but not the address, then we can simply let the server handle it (it would save the rest and show an error for the e-mailaddress)

#Comment by Hashar (talk | contribs)   20:46, 24 January 2011

Make senses. The form will validate server side anyway so we can let the user submit about anything without blocking the submit button.

Thanks for the answer, and thanks for the code! Marking 'ok'

Status & tagging log