[dw-free] screenreader improvements for create.bml
[commit: http://hg.dwscoalition.org/dw-free/rev/f7a7aa9b6525]
http://bugs.dwscoalition.org/show_bug.cgi?id=1016
Accessibility: Add screen reader and keyboard functionality for account
creation flow.
Patch by
jadelennox.
Files modified:
http://bugs.dwscoalition.org/show_bug.cgi?id=1016
Accessibility: Add screen reader and keyboard functionality for account
creation flow.
Patch by
Files modified:
- bin/upgrading/en.dat
- cgi-bin/LJ/Widget/CreateAccount.pm
- cgi-bin/htmlcontrols.pl
- htdocs/js/widgets/createaccount.js
- htdocs/stc/widgets/createaccount.css
--------------------------------------------------------------------------------
diff -r 759db912e36f -r f7a7aa9b6525 bin/upgrading/en.dat
--- a/bin/upgrading/en.dat Wed Jan 06 02:14:11 2010 +0000
+++ b/bin/upgrading/en.dat Thu Jan 07 05:27:39 2010 +0000
@@ -3879,12 +3879,6 @@ widget.communitymanagement.pending.membe
widget.communitymanagement.title=Community Management
-widget.createaccount.alt_layout.error.tos=You must agree to the Terms of Service.
-
-widget.createaccount.alt_layout.field.captcha=Enter the text below to help verify the authenticity of this account:
-
-widget.createaccount.alt_layout.field.tos=Check here to agree.
-
widget.createaccount.btn=Create Account
widget.createaccount.error.birthdate.invalid=You must enter a valid birthdate.
@@ -3897,6 +3891,8 @@ widget.createaccount.error.captcha.inval
widget.createaccount.error.email.lj_domain=You cannot use a [[domain]] alias when creating an account. Please enter a different email address.
+widget.createaccount.error.list=Errors in form:
+
widget.createaccount.error.password.asciionly=You can only use ASCII symbols in the password.
widget.createaccount.error.password.bad=Bad password:
@@ -3920,6 +3916,8 @@ widget.createaccount.error.password.tool
widget.createaccount.error.password.toolong=Password must not be longer than 30 characters.
widget.createaccount.error.password.tooshort=Password must be at least 6 characters.
+
+widget.createaccount.error.tos=You must agree to the Terms of Service.
widget.createaccount.error.username.inuse=Sorry, this username is already in use.
diff -r 759db912e36f -r f7a7aa9b6525 cgi-bin/LJ/Widget/CreateAccount.pm
--- a/cgi-bin/LJ/Widget/CreateAccount.pm Wed Jan 06 02:14:11 2010 +0000
+++ b/cgi-bin/LJ/Widget/CreateAccount.pm Thu Jan 07 05:27:39 2010 +0000
@@ -40,12 +40,7 @@ sub render_body {
return "$pre $msg $post";
};
- my $alt_layout = $opts{alt_layout} ? 1 : 0;
my $ret;
-
- if ($alt_layout) {
- $ret .= "<div class='signup-container'>";
- }
$ret .= $class->start_form(%{$opts{form_attr}});
@@ -54,159 +49,172 @@ sub render_body {
my $tip_password = LJ::ejs($class->ml('widget.createaccount.tip.password'));
my $tip_username = LJ::ejs($class->ml('widget.createaccount.tip.username'));
- # tip module
- if ($alt_layout) {
- $ret .= "<script type='text/javascript'>\n";
- $ret .= "CreateAccount.alt_layout = true;\n";
- $ret .= "</script>\n";
- } else {
- $ret .= "<script type='text/javascript'>\n";
- $ret .= "CreateAccount.birthdate = \"$tip_birthdate\"\n";
- $ret .= "CreateAccount.email = \"$tip_email\"\n";
- $ret .= "CreateAccount.password = \"$tip_password\"\n";
- $ret .= "CreateAccount.username = \"$tip_username\"\n";
- $ret .= "</script>\n";
- $ret .= "<div id='tips_box_arrow'></div>";
- $ret .= "<div id='tips_box'></div>";
+ $ret .= "<script type='text/javascript'>\n";
+ $ret .= "CreateAccount.birthdate = \"$tip_birthdate\"\n";
+ $ret .= "CreateAccount.email = \"$tip_email\"\n";
+ $ret .= "CreateAccount.password = \"$tip_password\"\n";
+ $ret .= "CreateAccount.username = \"$tip_username\"\n";
+ $ret .= "</script>\n";
+
+ # Errors container, listed in a TOC for screen-reader convenience
+ # Don't even build if there are no errors in the page
+ # IMPORTANT: The placement of this list in the HTML is necessary for
+ # screen readers to announce it correctly after form submission. If you want to
+ # move it, use CSS.
+ if ( keys %$errors ) {
+ $ret .= "<div tabindex=1 id='error-list' class='error-list' role='alert'>";
+ $ret .= "<h2 class='nav' id='errorlist_label'>"
+ . LJ::ejs($class->ml('widget.createaccount.error.list'))
+ . "</h2>";
+ $ret .= "<ol role='alert' labelledby='errorlist_label'>";
+
+ # Print out all of the error messages that exist.
+ # Do this manually as opposed to in a for loop in order to guarantee the order
+ # matches the layout of the page
+ $ret .= $error_msg->('username', '<li class="formitemFlag" role="alert">', '</li>');
+ $ret .= $error_msg->('email', '<li class="formitemFlag" role="alert">', '</li>');
+ $ret .= $error_msg->('password', '<li class="formitemFlag" role="alert">', '</li>');
+ $ret .= $error_msg->('confirmpass', '<li class="formitemFlag" role="alert">', '</li>');
+ $ret .= $error_msg->('bday', '<li class="formitemFlag" role="alert">', '</li>');
+ $ret .= $error_msg->('captcha', '<li class="formitemFlag" role="alert">', '</li>');
+ $ret .= $error_msg->('tos', '<li class="formitemFlag" role="alert">', '</li>');
+
+ $ret .= "</ol>";
+ $ret .= "</div> <!-- error-list -->\n";
}
- $ret .= "<table class='create-form' cellspacing='0' cellpadding='3'>\n" unless $alt_layout;
+ # FIXME: this table should be converted to fieldsets and css layout
+ # instead of tables for maximum accessibility. Eventually.
+
+ $ret .= "<div class='relative-container'>\n";
+ $ret .= "<div id='tips_box_arrow'></div>";
+ $ret .= "<div id='tips_box'></div>";
+ $ret .= "<table class='create-form' cellspacing='0' cellpadding='3'>\n";
### username
- if ($alt_layout) {
- $ret .= "<label for='create_user' class='label_create'>" . $class->ml('widget.createaccount.field.username') . "</label>";
- $ret .= "<div class='bubble' id='bubble_user'>";
- $ret .= "<div class='bubble-arrow'></div>";
- $ret .= "<div class='bubble-text'>$tip_username</div>";
- $ret .= "</div>";
- } else {
- $ret .= "<tr><td class='field-name'>" . $class->ml('widget.createaccount.field.username') . "</td>\n<td>";
- }
+
+ # Highlight the field if the user needs to fix errors
+ my $label_username = $errors->{'username'} ? "errors-present" : "errors-absent";
+
+ $ret .= "<tr><td class='$label_username'>"
+ . $class->ml('widget.createaccount.field.username')
+ . "</td>\n<td>";
+
# maxlength 26, so if people don't notice that they hit the limit,
# we give them a warning. (some people don't notice/proofread)
$ret .= $class->html_text(
name => 'user',
id => 'create_user',
- size => $alt_layout ? undef : 20,
+ size => 20,
maxlength => 26,
- raw => 'style="<?loginboxstyle?>"',
+ raw => 'tabindex=1 style="<?loginboxstyle?>" aria-required="true"',
value => $post->{user} || $get->{user},
);
- $ret .= " <img id='username_check' src='$LJ::IMGPREFIX/create/check.png' alt='" . $class->ml('widget.createaccount.field.username.available') . "' title='" . $class->ml('widget.createaccount.field.username.available') . "' />";
- $ret .= $error_msg->('username', '<span id="username_error_main"><br /><span class="formitemFlag">', '</span></span>');
- $ret .= "<span id='username_error'><br /><span id='username_error_inner' class='formitemFlag'></span></span>";
- $ret .= "</td></tr>\n" unless $alt_layout;
+
+ # If JavaScript is available, check to see if the username is available
+ # before submitting the form. Make sure that responses are returned as
+ # ARIA live region for screen reader compatibility.
+ $ret .= " <img id='username_check' src='$LJ::IMGPREFIX/create/check.png' alt='"
+ . $class->ml('widget.createaccount.field.username.available')
+ . "' title='"
+ . $class->ml('widget.createaccount.field.username.available')
+ . "' aria-live='polite' />";
+ $ret .= "<span id='username_error'><br /><span id='username_error_inner' class='formitemFlag' role='alert'></span></span>";
+
+ $ret .= "</td></tr>\n";
### email
- if ($alt_layout) {
- $ret .= "<label for='create_email' class='label_create'>" . $class->ml('widget.createaccount.field.email') . "</label>";
- $ret .= "<div class='bubble' id='bubble_email'>";
- $ret .= "<div class='bubble-arrow'></div>";
- $ret .= "<div class='bubble-text'>$tip_email</div>";
- $ret .= "</div>";
- } else {
- $ret .= "<tr><td class='field-name'>" . $class->ml('widget.createaccount.field.email') . "</td>\n<td>";
- }
+
+ # Highlight the field if the user needs to fix errors
+ my $label_email = $errors->{'email'} ? "errors-present" : "errors-absent";
+
+ $ret .= "<tr><td class='$label_email'>"
+ . $class->ml('widget.createaccount.field.email')
+ . "</td>\n<td>";
$ret .= $class->html_text(
name => 'email',
id => 'create_email',
size => 28,
maxlength => 50,
+ raw => 'tabindex=1 aria-required="true"',
value => $post->{email},
);
- $ret .= $error_msg->('email', '<br /><span class="formitemFlag">', '</span>');
- $ret .= "</td></tr>\n" unless $alt_layout;
+ $ret .= "</td></tr>\n";
### password
+
+ # Highlight the field if the user needs to fix errors
+ my $label_password = $errors->{'password'} ? "errors-present" : "errors-absent";
+
my $pass_value = $errors->{password} ? "" : $post->{password1};
- if ($alt_layout) {
- $ret .= "<label for='create_password1' class='label_create'>" . $class->ml('widget.createaccount.field.password') . "</label>";
- $ret .= "<div class='bubble' id='bubble_password1'>";
- $ret .= "<div class='bubble-arrow'></div>";
- $ret .= "<div class='bubble-text'>$tip_password</div>";
- $ret .= "</div>";
- } else {
- $ret .= "<tr><td class='field-name'>" . $class->ml('widget.createaccount.field.password') . "</td>\n<td>";
- }
+
+ $ret .= "<tr><td class='$label_password'>"
+ . $class->ml('widget.createaccount.field.password')
+ . "</td>\n<td>";
$ret .= $class->html_text(
name => 'password1',
id => 'create_password1',
size => 28,
maxlength => 31,
type => "password",
+ raw => 'tabindex=1 aria-required="true"',
value => $pass_value,
);
- $ret .= $error_msg->('password', '<br /><span class="formitemFlag">', '</span>');
- $ret .= "</td></tr>\n" unless $alt_layout;
+ $ret .= "</td></tr>\n";
### confirm password
- if ($alt_layout) {
- $ret .= "<label for='create_password2' class='label_create'>" . $class->ml('widget.createaccount.field.confirmpassword') . "</label>";
- $ret .= "<div class='bubble' id='bubble_password1'>";
- $ret .= "<div class='bubble-arrow'></div>";
- $ret .= "<div class='bubble-text'>$tip_password</div>";
- $ret .= "</div>";
- } else {
- $ret .= "<tr><td class='field-name'>" . $class->ml('widget.createaccount.field.confirmpassword') . "</td>\n<td>";
- }
+
+ # Highlight the field if the user needs to fix errors
+ my $label_confirmpass = $errors->{'confirmpass'} ? "errors-present" : "errors-absent";
+
+ $ret .= "<tr><td class='$label_confirmpass'>"
+ . $class->ml('widget.createaccount.field.confirmpassword')
+ . "</td>\n<td>";
$ret .= $class->html_text(
name => 'password2',
id => 'create_password2',
size => 28,
maxlength => 31,
type => "password",
+ raw => 'tabindex=1 aria-required="true"',
value => $pass_value,
);
- $ret .= $error_msg->('confirmpass', '<br /><span class="formitemFlag">', '</span>');
- $ret .= "</td></tr>\n" unless $alt_layout;
+ $ret .= "</td></tr>\n";
### birthdate
- if ($alt_layout) {
- $ret .= "<label for='create_bday_mm' class='label_create'>" . $class->ml('widget.createaccount.field.birthdate') . "</label>";
- $ret .= "<div class='bubble' id='bubble_bday_mm'>";
- $ret .= "<div class='bubble-arrow'></div>";
- $ret .= "<div class='bubble-text'>$tip_birthdate</div>";
- $ret .= "</div>";
- $ret .= $class->html_select(
- name => "bday_mm",
- id => "create_bday_mm",
- selected => $post->{bday_mm} || 1,
- list => [ map { $_, LJ::Lang::month_long_ml( $_ ) } (1..12) ],
- ) . " ";
- $ret .= $class->html_text(
- name => "bday_dd",
- id => "create_bday_dd",
- class => 'date',
- maxlength => '2',
- value => $post->{bday_dd} || "",
- );
- $ret .= $class->html_text(
- name => "bday_yyyy",
- id => "create_bday_yyyy",
- class => 'year',
- maxlength => '4',
- value => $post->{bday_yyyy} || "",
- );
- } else {
- $ret .= "<tr><td class='field-name'>" . $class->ml('widget.createaccount.field.birthdate') . "</td>\n<td>";
- $ret .= $class->html_datetime(
- name => 'bday',
- id => 'create_bday',
- notime => 1,
- default => sprintf("%04d-%02d-%02d", $post->{bday_yyyy}, $post->{bday_mm}, $post->{bday_dd}),
- );
- }
- $ret .= $error_msg->('bday', '<br /><span class="formitemFlag">', '</span>');
- $ret .= "</td></tr>\n" unless $alt_layout;
+
+ # Highlight the field if the user needs to fix errors
+ my $label_bday = $errors->{'bday'} ? "errors-present" : "errors-absent";
+
+
+ $ret .= "<tr>"
+ . "<td class='$label_bday'><label for='create_bday_mm'>"
+ . $class->ml('widget.createaccount.field.birthdate')
+ . "</label></td>\n<td>";
+ $ret .= $class->html_datetime(
+ name => 'bday',
+ id => 'create_bday',
+ raw => 'aria-required="true" tabindex=1',
+ notime => 1,
+ default => sprintf("%04d-%02d-%02d", $post->{bday_yyyy}, $post->{bday_mm}, $post->{bday_dd}),
+ );
+
+ $ret .= "</td></tr>\n";
### captcha
+
+ # Highlight the field if the user needs to fix errors
+ # NOTE: Because captcha is not currently in use on
+ # dreamwidth.org, and because its accessibility is negligible
+ # at best, WAI-ARIA code is not wrapped around the
+ # captcha functionality.
+ my $label_captcha = $errors->{'captcha'} ? "errors-present" : "errors-absent";
+
if ($LJ::HUMAN_CHECK{create}) {
if (LJ::is_enabled("recaptcha")) {
- if ($alt_layout) {
- $ret .= "<label class='text'>" . $class->ml('widget.createaccount.alt_layout.field.captcha') . "</label>";
- } else {
- $ret .= "<tr valign='top'><td class='field-name'>" . $class->ml('widget.createaccount.field.captcha') . "</td>\n<td>";
- }
+ $ret .= "<tr valign='top'><td class='$label_captcha'>"
+ . $class->ml('widget.createaccount.field.captcha')
+ . "</td>\n<td>";
my $c = Captcha::reCAPTCHA->new;
$ret .= $c->get_options_setter({ theme => 'white' });
@@ -230,7 +238,9 @@ sub render_body {
$captcha_chal = $captcha_chal || LJ::challenge_generate(900);
$captcha_sess = LJ::get_challenge_attributes($captcha_chal);
- $ret .= "<tr valign='top'><td class='field-name'>" . $class->ml('widget.createaccount.field.captcha') . "</td>\n<td>";
+ $ret .= "<tr valign='top'><td class='$label_captcha'>"
+ . $class->ml('widget.createaccount.field.captcha')
+ . "</td>\n<td>";
if ($wants_audio || $post->{audio_chal}) { # audio
my $url = $capid && $anum ? # previously entered correctly
@@ -258,69 +268,43 @@ sub render_body {
$ret .= $class->html_hidden( captcha_chal => $captcha_chal );
}
- $ret .= $error_msg->('captcha', '<span class="formitemFlag">', '</span><br />');
$ret .= "</td></tr>\n";
}
- if ($alt_layout) {
- $ret .= "<p class='terms'>";
+ ### TOS
- ### TOS
- my $tos_string = $class->ml( 'widget.createaccount.alt_layout.tos', { sitename => $LJ::SITENAMESHORT } );
- if ( $tos_string ) {
- $ret .= "$tos_string<br />";
- $ret .= $class->html_check(
- name => 'tos',
- id => 'create_tos',
- value => '1',
- selected => LJ::did_post() ? $post->{tos} : 0,
- );
- $ret .= " <label for='create_tos' class='text'>" . $class->ml( 'widget.createaccount.alt_layout.field.tos' ) . "</label><br /><br />";
- } else {
- $ret .= LJ::html_hidden( tos => 1 );
- }
+ # Highlight the field if the user needs to fix errors
+ my $label_tos = $errors->{'tos'} ? "errors-present" : "errors-absent";
+
+ # site news
+ $ret .= "<tr valign='top'><td class='field-name'> </td>\n<td>";
+ $ret .= $class->html_check(
+ name => 'news',
+ id => 'create_news',
+ value => '1',
+ raw => 'tabindex=1',
+ selected => LJ::did_post() ? $post->{news} : 1,
+ label => $class->ml('widget.createaccount.field.news', { sitename => $LJ::SITENAMESHORT }),
+ );
+ $ret .= "</td></tr>\n";
- ### site news
- $ret .= $class->html_check(
- name => 'news',
- id => 'create_news',
- value => '1',
- selected => LJ::did_post() ? $post->{news} : 0,
- );
- $ret .= " <label for='create_news' class='text'>" . $class->ml('widget.createaccount.field.news', { sitename => $LJ::SITENAMESHORT }) . "</label>";
-
- $ret .= "</p>";
- $ret .= $error_msg->('tos', '<span class="formitemFlag">', '</span><br />');
- } else {
- ### site news
- $ret .= "<tr valign='top'><td class='field-name'> </td>\n<td>";
- $ret .= $class->html_check(
- name => 'news',
- id => 'create_news',
- value => '1',
- selected => LJ::did_post() ? $post->{news} : 1,
- label => $class->ml('widget.createaccount.field.news', { sitename => $LJ::SITENAMESHORT }),
- );
- $ret .= "</td></tr>\n";
-
- ### TOS
- $ret .= "<tr valign='top'><td class='field-name'> </td>\n<td>";
- $ret .= $class->html_check(
- name => 'tos',
- id => 'create_tos',
- value => '1',
- selected => LJ::did_post() ? $post->{tos} : 0,
- );
- $ret .= " <label for='create_tos' class='text'>";
- $ret .= $class->ml( 'widget.createaccount.field.tos', {
- sitename => $LJ::SITENAMESHORT,
- aopts1 => "href='$LJ::SITEROOT/legal/tos' target='_new'",
- aopts2 => "href='$LJ::SITEROOT/legal/privacy' target='_new'",
- } );
- $ret .= "</label>";
- $ret .= $error_msg->( 'tos', '<span class="formitemFlag">', '</span><br />' );
- $ret .= "</td></tr>\n";
- }
+ # TOS
+ $ret .= "<tr valign='top'><td class='$label_tos'> </td>\n<td>";
+ $ret .= $class->html_check(
+ name => 'tos',
+ id => 'create_tos',
+ value => '1',
+ raw => 'tabindex=1',
+ selected => LJ::did_post() ? $post->{tos} : 0,
+ );
+ $ret .= " <label for='create_tos' class='text'>";
+ $ret .= $class->ml( 'widget.createaccount.field.tos', {
+ sitename => $LJ::SITENAMESHORT,
+ aopts1 => "href='$LJ::SITEROOT/legal/tos' target='_new'",
+ aopts2 => "href='$LJ::SITEROOT/legal/privacy' target='_new'",
+ } );
+ $ret .= "</label>";
+ $ret .= "</td></tr>\n";
if ( $LJ::USE_ACCT_CODES && !DW::InviteCodes->is_promo_code( code => $code ) ) {
my $item = DW::InviteCodes->paid_status( code => $code );
@@ -336,24 +320,21 @@ sub render_body {
}
### submit button
- if ($alt_layout) {
- $ret .= $class->html_submit( submit => $class->ml('widget.createaccount.btn'), { class => "login-button" }) . "\n";
- } else {
- $ret .= "<tr valign='top'><td class='field-name'> </td>\n<td>";
- $ret .= $class->html_submit( submit => $class->ml('widget.createaccount.btn'), { class => "create-button" }) . "\n";
- $ret .= "</td></tr>\n";
- }
-
- $ret .= "</table>\n" unless $alt_layout;
+ $ret .= "<tr valign='top'><td class='field-name'> </td>\n<td>";
+ $ret .= $class->html_submit(
+ submit => $class->ml('widget.createaccount.btn'),
+ { class => "create-button",
+ raw => 'tabindex=1',
+ },
+ ) . "\n";
+ $ret .= "</td></tr>\n";
+ $ret .= "</table>\n";
+ $ret .= "</div> <!-- relative-container -->\n";
$ret .= $class->html_hidden( from => $from ) if $from;
$ret .= $class->html_hidden( code => $code ) if $LJ::USE_ACCT_CODES;
$ret .= $class->end_form;
-
- if ($alt_layout) {
- $ret .= "</div>";
- }
return $ret;
}
@@ -365,7 +346,6 @@ sub handle_post {
my %from_post;
my $remote = LJ::get_remote();
- my $alt_layout = $opts{alt_layout} ? 1 : 0;
# flag to indicate they've submitted with 'audio' as the answer to the captcha
my $wants_audio = $from_post{wants_audio} = 0;
@@ -504,7 +484,7 @@ sub handle_post {
}
# check TOS agreement
- $from_post{errors}->{tos} = $class->ml( 'widget.createaccount.alt_layout.error.tos' ) unless $post->{tos};
+ $from_post{errors}->{tos} = $class->ml( 'widget.createaccount.error.tos' ) unless $post->{tos};
# create user and send email as long as the user didn't double-click submit
# (or they tried to re-create a purged account)
diff -r 759db912e36f -r f7a7aa9b6525 cgi-bin/htmlcontrols.pl
--- a/cgi-bin/htmlcontrols.pl Wed Jan 06 02:14:11 2010 +0000
+++ b/cgi-bin/htmlcontrols.pl Thu Jan 07 05:27:39 2010 +0000
@@ -49,28 +49,60 @@ sub html_datetime
$5 > 0 ? $5 : "",
$6 > 0 ? $6 : "");
}
- $ret .= html_select({ 'name' => "${name}_mm", 'id' => "${id}_mm", 'selected' => $mm, 'class' => 'select',
- 'disabled' => $disabled, %extra_opts },
- map { $_, LJ::Lang::month_long_ml($_) } (1..12));
- $ret .= html_text({ 'name' => "${name}_dd", 'id' => "${id}_dd", 'size' => '2', 'class' => 'text',
- 'maxlength' => '2', 'value' => $dd,
- 'disabled' => $disabled, %extra_opts });
- $ret .= html_text({ 'name' => "${name}_yyyy", 'id' => "${id}_yyyy", 'size' => '4', 'class' => 'text',
- 'maxlength' => '4', 'value' => $yyyy,
- 'disabled' => $disabled, %extra_opts });
+ $ret .= html_select({ 'name' => "${name}_mm",
+ 'id' => "${id}_mm",
+ 'selected' => $mm,
+ 'class' => 'select',
+ 'title' => 'month',
+ 'disabled' => $disabled, %extra_opts,
+ },
+ map { $_, LJ::Lang::month_long_ml($_) } (1..12));
+ $ret .= html_text({ 'name' => "${name}_dd",
+ 'id' => "${id}_dd",
+ 'size' => '2',
+ 'class' => 'text',
+ 'maxlength' => '2',
+ 'value' => $dd,
+ 'title' => 'day',
+ 'disabled' => $disabled, %extra_opts,
+ });
+ $ret .= html_text({ 'name' => "${name}_yyyy",
+ 'id' => "${id}_yyyy",
+ 'size' => '4',
+ 'class' => 'text',
+ 'maxlength' => '4',
+ 'value' => $yyyy,
+ 'title' => 'year',
+ 'disabled' => $disabled, %extra_opts,
+ });
unless ($opts->{'notime'}) {
$ret .= ' ';
- $ret .= html_text({ 'name' => "${name}_hh", 'id' => "${id}_hh", 'size' => '2',
- 'maxlength' => '2', 'value' => $hh,
- 'disabled' => $disabled }) . ':';
- $ret .= html_text({ 'name' => "${name}_nn", 'id' => "${id}_nn", 'size' => '2',
- 'maxlength' => '2', 'value' => $nn,
- 'disabled' => $disabled });
+ $ret .= html_text({ 'name' => "${name}_hh",
+ 'id' => "${id}_hh",
+ 'size' => '2',
+ 'maxlength' => '2',
+ 'value' => $hh,
+ 'title' => 'hour',
+ 'disabled' => $disabled,
+ }) . ':';
+ $ret .= html_text({ 'name' => "${name}_nn",
+ 'id' => "${id}_nn",
+ 'size' => '2',
+ 'maxlength' => '2',
+ 'value' => $nn,
+ 'title' => 'minutes',
+ 'disabled' => $disabled,
+ });
if ($opts->{'seconds'}) {
$ret .= ':';
- $ret .= html_text({ 'name' => "${name}_ss", 'id' => "${id}_ss", 'size' => '2',
- 'maxlength' => '2', 'value' => $ss,
- 'disabled' => $disabled });
+ $ret .= html_text({ 'name' => "${name}_ss",
+ 'id' => "${id}_ss",
+ 'size' => '2',
+ 'maxlength' => '2',
+ 'value' => $ss,
+ 'title' => 'seconds',
+ 'disabled' => $disabled,
+ });
}
}
diff -r 759db912e36f -r f7a7aa9b6525 htdocs/js/widgets/createaccount.js
--- a/htdocs/js/widgets/createaccount.js Wed Jan 06 02:14:11 2010 +0000
+++ b/htdocs/js/widgets/createaccount.js Thu Jan 07 05:27:39 2010 +0000
@@ -18,15 +18,13 @@ CreateAccount.init = function () {
DOM.addEventListener($('create_bday_dd'), "focus", CreateAccount.eventShowTip.bindEventListener("create_bday_mm"));
DOM.addEventListener($('create_bday_yyyy'), "focus", CreateAccount.eventShowTip.bindEventListener("create_bday_mm"));
- if (CreateAccount.alt_layout) {
- DOM.addEventListener($('create_user'), "blur", CreateAccount.eventHideTip.bindEventListener("create_user"));
- DOM.addEventListener($('create_email'), "blur", CreateAccount.eventHideTip.bindEventListener("create_email"));
- DOM.addEventListener($('create_password1'), "blur", CreateAccount.eventHideTip.bindEventListener("create_password1"));
- DOM.addEventListener($('create_password2'), "blur", CreateAccount.eventHideTip.bindEventListener("create_password1"));
- DOM.addEventListener($('create_bday_mm'), "blur", CreateAccount.eventHideTip.bindEventListener("create_bday_mm"));
- DOM.addEventListener($('create_bday_dd'), "blur", CreateAccount.eventHideTip.bindEventListener("create_bday_mm"));
- DOM.addEventListener($('create_bday_yyyy'), "blur", CreateAccount.eventHideTip.bindEventListener("create_bday_mm"));
- }
+ DOM.addEventListener($('create_user'), "blur", CreateAccount.eventHideTip.bindEventListener("create_user"));
+ DOM.addEventListener($('create_email'), "blur", CreateAccount.eventHideTip.bindEventListener("create_email"));
+ DOM.addEventListener($('create_password1'), "blur", CreateAccount.eventHideTip.bindEventListener("create_password1"));
+ DOM.addEventListener($('create_password2'), "blur", CreateAccount.eventHideTip.bindEventListener("create_password1"));
+ DOM.addEventListener($('create_bday_mm'), "blur", CreateAccount.eventHideTip.bindEventListener("create_bday_mm"));
+ DOM.addEventListener($('create_bday_dd'), "blur", CreateAccount.eventHideTip.bindEventListener("create_bday_mm"));
+ DOM.addEventListener($('create_bday_yyyy'), "blur", CreateAccount.eventHideTip.bindEventListener("create_bday_mm"));
if (!$('username_check')) return;
if (!$('username_error')) return;
@@ -50,51 +48,46 @@ CreateAccount.showTip = function (id) {
if (!id) return;
var drop, arrowdrop, text;
- if (CreateAccount.alt_layout) {
- CreateAccount.bubbleid = id.replace(/create/, 'bubble');
- if ($(CreateAccount.bubbleid)) {
- $(CreateAccount.bubbleid).style.visibility = "visible";
- }
- } else {
- if (id == "create_bday_mm") {
- text = CreateAccount.birthdate;
- drop = 40;
- arrowdrop = 53;
- } else if (id == "create_email") {
- text = CreateAccount.email;
- drop = 10;
- arrowdrop = 13;
- } else if (id == "create_password1") {
- text = CreateAccount.password;
- drop = 20;
- arrowdrop = 24;
- } else if (id == "create_user") {
- text = CreateAccount.username;
- drop = 0;
- arrowdrop = 3;
- }
- var box = $('tips_box'), box_arr = $('tips_box_arrow');
- if (box && box_arr) {
- box.innerHTML = text;
+ // Create the location for the tooltip
+ if (id == "create_bday_mm") {
+ text = CreateAccount.birthdate;
+ drop = 40;
+ arrowdrop = 53;
+ } else if (id == "create_email") {
+ text = CreateAccount.email;
+ drop = 10;
+ arrowdrop = 13;
+ } else if (id == "create_password1") {
+ text = CreateAccount.password;
+ drop = 20;
+ arrowdrop = 24;
+ } else if (id == "create_user") {
+ text = CreateAccount.username;
+ drop = 0;
+ arrowdrop = 3;
+ }
- box.style.top = drop + "%";
- box.style.display = "block";
+ var box = $('tips_box'), box_arr = $('tips_box_arrow');
+ if (box && box_arr) {
+ box.innerHTML = text;
- box_arr.style.top = arrowdrop + "%";
- box_arr.style.display = "block";
- }
+ box.style.top = drop + "%";
+ box.style.display = "block";
+ box.style.visibility = "visible";
+
+ box_arr.style.top = arrowdrop + "%";
+ box_arr.style.display = "block";
}
}
CreateAccount.hideTip = function (id) {
if (!id) return;
- if (CreateAccount.alt_layout) {
- if ($(CreateAccount.bubbleid)) {
- $(CreateAccount.bubbleid).style.visibility = "hidden";
- }
- }
+ // Set the tip to the empty string instead of just relying
+ // on CSS to maximize accessibility
+ $('tips_box').style.visibility = "hidden";
+ $('tips_box').innerHTML = "";
}
CreateAccount.checkUsername = function () {
@@ -110,11 +103,13 @@ CreateAccount.checkUsername = function (
$('username_error_inner').innerHTML = data.error;
$('username_check').style.display = "none";
$('username_error').style.display = "inline";
+ $('create_user').setAttribute("aria-invalid", "true");
} else {
if ($('username_error_main')) $('username_error_main').style.display = "none";
$('username_error').style.display = "none";
$('username_check').style.display = "inline";
+ $('create_user').setAttribute("aria-invalid", "false");
}
CreateAccount.showTip(CreateAccount.id); // recalc
},
diff -r 759db912e36f -r f7a7aa9b6525 htdocs/stc/widgets/createaccount.css
--- a/htdocs/stc/widgets/createaccount.css Wed Jan 06 02:14:11 2010 +0000
+++ b/htdocs/stc/widgets/createaccount.css Thu Jan 07 05:27:39 2010 +0000
@@ -1,3 +1,26 @@
+/* Relative container allows a parent container to something which
+ needs absolute positioning around it */
+.relative-container {
+ position: relative;
+}
+
+.error-list {
+ background-color: #fcf6db;
+ border: 1px solid #ffdfc0;
+ padding: 1em;
+ padding-left: 3em;
+ margin-top: 3em;
+}
+
+div.error-list ol, div.error-list li {
+ list-style-type: decimal;
+}
+
+.errors-present {
+ font-weight: bold;
+ color: #f00;
+}
+
.appwidget-createaccount .field-name {
text-align: right;
width: 125px;
@@ -54,9 +77,7 @@ span.appwidget-createaccount #username_e
font-weight: bold;
}
-.create-form span.formitemFlag {
- display: block;
- width: 330px;
+li.formitemFlag {
font-weight: bold;
color: #f00;
}
--------------------------------------------------------------------------------
