Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.1128 diff -u -p -r1.1128 common.inc --- includes/common.inc 12 Mar 2010 14:20:32 -0000 1.1128 +++ includes/common.inc 13 Mar 2010 05:27:14 -0000 @@ -4080,6 +4080,7 @@ function drupal_add_tabledrag($table_id, // Add the table drag JavaScript to the page before the module JavaScript // to ensure that table drag behaviors are registered before any module // uses it. + drupal_add_js('misc/jquery.cookie.js', array('weight' => JS_DEFAULT - 2)); drupal_add_js('misc/tabledrag.js', array('weight' => JS_DEFAULT - 1)); $js_added = TRUE; } Index: includes/form.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/form.inc,v retrieving revision 1.441 diff -u -p -r1.441 form.inc --- includes/form.inc 12 Mar 2010 14:38:37 -0000 1.441 +++ includes/form.inc 13 Mar 2010 05:27:14 -0000 @@ -2905,6 +2905,9 @@ function theme_file($variables) { * for radio and checkbox #type elements as set in system_element_info(). * If the #title is empty but the field is #required, the label will * contain only the required marker. + * - invisible: Labels are critical for screen readers to enable them to + * properly navigate through forms but can be visually distracting. This + * property hides the label for everyone except screen readers. * - attribute: Set the title attribute on the element to create a tooltip * but output no label element. This is supported only for checkboxes * and radios in form_pre_render_conditional_form_element(). It is used @@ -2961,6 +2964,7 @@ function theme_form_element($variables) $output .= ' ' . $element['#children'] . "\n"; break; + case 'invisible': case 'after': $output .= ' ' . $element['#children']; $output .= ' ' . theme('form_element_label', $variables) . "\n"; @@ -3051,6 +3055,11 @@ function theme_form_element_label($varia // Style the label as class option to display inline with the element. $attributes['class'] = 'option'; } + elseif ($element['#title_display'] == 'invisible') { + // Show label only to screen readers to avoid disruption in visual flows. + $attributes['class'] = 'element-invisible'; + } + if (!empty($element['#id'])) { $attributes['for'] = $element['#id']; } Index: modules/menu/menu.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/menu/menu.admin.inc,v retrieving revision 1.76 diff -u -p -r1.76 menu.admin.inc --- modules/menu/menu.admin.inc 23 Feb 2010 10:32:10 -0000 1.76 +++ modules/menu/menu.admin.inc 13 Mar 2010 05:27:14 -0000 @@ -99,13 +99,15 @@ function _menu_overview_tree_form($tree) '#type' => 'weight', '#delta' => 50, '#default_value' => isset($form_state[$mlid]['weight']) ? $form_state[$mlid]['weight'] : $item['weight'], + '#title_display' => 'invisible', + '#title' => t('Weight for') . ' ' . $item['title'], ); $form[$mlid]['mlid'] = array( '#type' => 'hidden', '#value' => $item['mlid'], ); $form[$mlid]['plid'] = array( - '#type' => 'textfield', + '#type' => 'hidden', '#default_value' => isset($form_state[$mlid]['plid']) ? $form_state[$mlid]['plid'] : $item['plid'], '#size' => 6, ); Index: modules/field_ui/field_ui.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/field_ui/field_ui.admin.inc,v retrieving revision 1.45 diff -u -p -r1.45 field_ui.admin.inc --- modules/field_ui/field_ui.admin.inc 3 Mar 2010 08:01:42 -0000 1.45 +++ modules/field_ui/field_ui.admin.inc 13 Mar 2010 05:27:14 -0000 @@ -137,7 +137,9 @@ function field_ui_field_overview_form($f '#type' => 'textfield', '#default_value' => $weight, '#size' => 3, - ), + '#title_display' => 'invisible', + '#title' => t('Weight for') . ' ' . check_plain($instance['label']), + ), 'hidden_name' => array( '#type' => 'hidden', '#default_value' => $instance['field_name'], Index: modules/block/block.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/block/block.admin.inc,v retrieving revision 1.75 diff -u -p -r1.75 block.admin.inc --- modules/block/block.admin.inc 9 Mar 2010 12:09:52 -0000 1.75 +++ modules/block/block.admin.inc 13 Mar 2010 05:27:14 -0000 @@ -80,6 +80,8 @@ function block_admin_display_form($form, '#type' => 'weight', '#default_value' => $block['weight'], '#delta' => $weight_delta, + '#title_display' => 'invisible', + '#title' => t('Weight for') . ' ' . check_plain($block['info']), ); $form[$key]['region'] = array( '#type' => 'select', Index: modules/filter/filter.module =================================================================== RCS file: /cvs/drupal/drupal/modules/filter/filter.module,v retrieving revision 1.322 diff -u -p -r1.322 filter.module --- modules/filter/filter.module 8 Mar 2010 03:59:25 -0000 1.322 +++ modules/filter/filter.module 13 Mar 2010 05:27:14 -0000 @@ -1075,7 +1075,7 @@ function theme_filter_tips_more_info() { function theme_filter_guidelines($variables) { $format = $variables['format']; - $name = isset($format->name) ? '' : ''; + $name = isset($format->name) ? $format->name : ''; return '
' . $name . theme('filter_tips', array('tips' => _filter_tips($format->format, FALSE))) . '
'; } Index: modules/simpletest/tests/form.test =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/form.test,v retrieving revision 1.39 diff -u -p -r1.39 form.test --- modules/simpletest/tests/form.test 12 Mar 2010 14:38:37 -0000 1.39 +++ modules/simpletest/tests/form.test 13 Mar 2010 05:27:14 -0000 @@ -305,6 +305,9 @@ class FormsElementsLabelsTestCase extend $elements = $this->xpath('//input[@id="edit-form-textfield-test-title-after"]/following-sibling::label[@for="edit-form-textfield-test-title-after" and @class="option"]'); $this->assertTrue(isset($elements[0]), t("Label after field and label option class correct for text field.")); + $elements = $this->xpath('//input[@id="edit-form-textfield-test-title-invisible"]/following-sibling::label[@for="edit-form-textfield-test-title-invisible" and @class="element-invisible"]'); + $this->assertTrue(isset($elements[0]), t("Label after field and label class is element-invisible.")); + $elements = $this->xpath('//label[@for="edit-form-textfield-test-title-no-show"]'); $this->assertFalse(isset($elements[0]), t("No label tag when title set not to display.")); } Index: modules/simpletest/tests/form_test.module =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/form_test.module,v retrieving revision 1.30 diff -u -p -r1.30 form_test.module --- modules/simpletest/tests/form_test.module 7 Mar 2010 07:49:26 -0000 1.30 +++ modules/simpletest/tests/form_test.module 13 Mar 2010 05:27:14 -0000 @@ -536,6 +536,11 @@ function form_label_test_form(&$form_sta '#title' => t('Textfield test for title after element'), '#title_display' => 'after', ); + $form['form_textfield_test_title_invisible'] = array( + '#type' => 'textfield', + '#title' => t('Textfield test for invisible title'), + '#title_display' => 'invisible', + ); // Textfield test for title set not to display $form['form_textfield_test_title_no_show'] = array( '#type' => 'textfield', Index: misc/tabledrag.js =================================================================== RCS file: /cvs/drupal/drupal/misc/tabledrag.js,v retrieving revision 1.35 diff -u -p -r1.35 tabledrag.js --- misc/tabledrag.js 10 Mar 2010 20:31:59 -0000 1.35 +++ misc/tabledrag.js 13 Mar 2010 05:27:14 -0000 @@ -83,8 +83,18 @@ Drupal.tableDrag = function (table, tabl // Match immediate children of the parent element to allow nesting. $('> tr.draggable, > tbody > tr.draggable', table).each(function() { self.makeDraggable(this); }); - // Hide columns containing affected form elements. - this.hideColumns(); + // Hide or show weight and parent columns according to user preference. + // This aids screenreader accessibility so users can enter weight values. + // Initialize the weight columns for the show/hide toggle. + self.initColumns(); + // Add a link before the table for users to show or hide weight columns. + $(table).before($('') + .append(Drupal.t('Enable/disable accessible table editing')) + .click(function () { + self.toggleShowWeight(self); + return false; + }) + ); // Add mouse bindings to the document. The self variable is passed along // as event handlers do not have direct access to the tableDrag object. @@ -93,10 +103,12 @@ Drupal.tableDrag = function (table, tabl }; /** - * Hide the columns containing form elements according to the settings for - * this tableDrag instance. + * Initialize weight/parent columns to be hidden by default. + * + * Identify and mark each cell with a CSS class so we can easily toggle show/hide it. + * Finally, hide columns if user does not have a 'showWeight' cookie. */ -Drupal.tableDrag.prototype.hideColumns = function () { +Drupal.tableDrag.prototype.initColumns = function () { for (var group in this.tableSettings) { // Find the first field in this group. for (var d in this.tableSettings[group]) { @@ -108,7 +120,7 @@ Drupal.tableDrag.prototype.hideColumns = } } - // Hide the column containing this field. + // Mark the column containing this field so it can be hidden. if (hidden && cell[0] && cell.css('display') != 'none') { // Add 1 to our indexes. The nth-child selector is 1 based, not 0 based. // Match immediate children of the parent element to allow nesting. @@ -128,21 +140,86 @@ Drupal.tableDrag.prototype.hideColumns = if (index > 0) { cell = row.children(':nth-child(' + index + ')'); if (cell[0].colSpan > 1) { - // If this cell has a colspan, simply reduce it. - cell[0].colSpan = cell[0].colSpan - 1; + // If this cell has a colspan, mark it so we can reduce the colspan. + $(cell[0]).addClass('tabledrag-reduce-colspan'); } else { - // Hide table body cells, but remove table header cells entirely - // (Safari doesn't hide properly). - parentTag == 'thead' ? cell.remove() : cell.css('display', 'none'); + // Mark this cell so we can hide it. + $(cell[0]).addClass('tabledrag-hide'); } } }); } } + + // Now hide cells and reduce colspans unless user cookie is set. + var show = $.cookie('Drupal.tableDrag.showWeight'); + if (show != 1) { + $.cookie('Drupal.tableDrag.showWeight', 0, { + path: Drupal.settings.basePath, + // The cookie should "never" expire. + expires: 36500, + } + ); + Drupal.tableDrag.prototype.hideColumns(); + } }; /** + * Hide the columns containing weight/parent form elements. + * Undo showColumns(). + */ +Drupal.tableDrag.prototype.hideColumns = function () { + // Turn off display of weight/parent cells and headers. + $('.tabledrag-hide').css('display', 'none'); + // Reduce colspan of any effected multi-span columns. + $('.tabledrag-reduce-colspan').each(function() { this.colSpan = this.colSpan - 1; }); + $('.tabledrag-handle').css('display', 'table-cell'); + +} + +/** + * Show the columns containing weight/parent form elements + * Undo hideColumns(). + */ +Drupal.tableDrag.prototype.showColumns = function () { + // Increase colspan back to columns it was reduced. + $('.tabledrag-reduce-colspan').each(function() { this.colSpan = this.colSpan + 1; }); + // Turn on display of weight/parent cells and headers. + $('.tabledrag-hide').css('display', 'table-cell'); + $('.tabledrag-handle').css('display', 'none'); +} + +/** + * Toggle visibility of weight/parent columns. Use a saved cookie to store the + * user preference. + */ +Drupal.tableDrag.prototype.toggleShowWeight = function (self) { + // Retrieve the tableDrag status from a stored cookie. + var show = $.cookie('Drupal.tableDrag.showWeight'); + + // Show or hide columns with weight fields and toggle the cookie value. + if (show == 1) { + $.cookie('Drupal.tableDrag.showWeight', 0, { + path: Drupal.settings.basePath, + // The cookie should "never" expire. + expires: 36500, + } + ); + this.hideColumns(); + } + else { + $.cookie('Drupal.tableDrag.showWeight', 1, { + path: Drupal.settings.basePath, + // The cookie should "never" expire. + expires: 36500, + } + ); + this.showColumns(); + } +} + +/** * Find the target used within a particular row and group. */ Drupal.tableDrag.prototype.rowSettings = function (group, row) { @@ -442,7 +519,7 @@ Drupal.tableDrag.prototype.dropRow = fun // fields in the entire dragged group. for (var group in self.tableSettings) { var rowSettings = self.rowSettings(group, droppedRow); - if (rowSettings.relationship == 'group') { + if (rowSettings !== undefined && rowSettings.relationship == 'group') { for (var n in self.rowObject.children) { self.updateField(self.rowObject.children[n], group); } @@ -594,11 +671,11 @@ Drupal.tableDrag.prototype.updateField = var rowSettings = this.rowSettings(group, changedRow); // Set the row as it's own target. - if (rowSettings.relationship == 'self' || rowSettings.relationship == 'group') { + if (rowSettings !== undefined && (rowSettings.relationship == 'self' || rowSettings.relationship == 'group')) { var sourceRow = changedRow; } // Siblings are easy, check previous and next rows. - else if (rowSettings.relationship == 'sibling') { + else if (rowSettings !== undefined && rowSettings.relationship == 'sibling') { var previousRow = $(changedRow).prev('tr').get(0); var nextRow = $(changedRow).next('tr').get(0); var sourceRow = changedRow; @@ -625,7 +702,7 @@ Drupal.tableDrag.prototype.updateField = } // Parents, look up the tree until we find a field not in this group. // Go up as many parents as indentations in the changed row. - else if (rowSettings.relationship == 'parent') { + else if (rowSettings !== undefined && rowSettings.relationship == 'parent') { var previousRow = $(changedRow).prev('tr'); while (previousRow.length && $('.indentation', previousRow).length >= this.rowObject.indents) { previousRow = previousRow.prev('tr'); @@ -659,10 +736,12 @@ Drupal.tableDrag.prototype.updateField = rowSettings.relationship = 'sibling'; rowSettings.source = rowSettings.target; } - - var targetClass = '.' + rowSettings.target; - var targetElement = $(targetClass, changedRow).get(0); - + + if(rowSettings !== undefined) { + var targetClass = '.' + rowSettings.target; + var targetElement = $(targetClass, changedRow).get(0); + } + // Check if a target element exists in this row. if (targetElement) { var sourceClass = '.' + rowSettings.source;