From 740cea9767dc3e140ec32a5a286b6c65b8b7b62e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?"J.=20Rene=CC=81e=20Beach"?= <splendidnoise@gmail.com>
Date: Wed, 29 Aug 2012 10:57:29 -0400
Subject: [PATCH] Issue #1276908 by jessebeach, moshe weitzman: Update table
 theming to create responsive table behavior at small screen
 sizes.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

commit ac7d91f50e85c5b24ec1ee9f636c086e23b4dcbf
Author: J. Renée Beach <splendidnoise@gmail.com>
Date:   Wed Aug 29 10:35:54 2012 -0400

    Issue #1276908 by jessebeach: Updated the comments in table.js.

    Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>

commit 5e00b9ecde0d42baabf8e25288649a6ab3a40cfe
Author: J. Renée Beach <splendidnoise@gmail.com>
Date:   Wed Aug 29 10:31:01 2012 -0400

    Issue #1276908 be jessebeach: wrapped the debounce function in an IIFE and added "use strict".

    Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>

commit 6f5447e2aa3093148c5c59e7ccbf7d9a1599e50e
Author: J. Renée Beach <splendidnoise@gmail.com>
Date:   Tue Aug 28 16:34:26 2012 -0400

    Issue #1276908 by jessebeach: Refactored the tableDrag class to use the action registration of the table class, rather than simply appending its actions to the DOM.

    Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>

commit f3a8b4e0ec2b9069f08893864d52c5ad90640704
Author: J. Renée Beach <splendidnoise@gmail.com>
Date:   Tue Aug 28 15:45:21 2012 -0400

    Issue #1276908 by jessebeach: Refactored the namespace to tableResponsive.

    Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>

commit 518f60e45bb0bdf338a37c1c39e789d66520878c
Author: J. Renée Beach <splendidnoise@gmail.com>
Date:   Tue Aug 28 15:34:29 2012 -0400

    Issue #1276908 by jessebeach: Added a control bar to tables within the Drupal.Table object. Plumbed the responsive table actions through this new Table API for added actions.

    Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>

commit ddc14059c299bb6e13408b755710f030ddae5185
Author: Moshe Weitzman <weitzman@tejasa.com>
Date:   Fri Aug 24 09:21:53 2012 -0400

    tabledrag fixes with jesse

commit 31f153a431014cfcf6fdc7366a4e61e2fee28193
Author: Moshe Weitzman <weitzman@tejasa.com>
Date:   Fri Aug 24 08:37:46 2012 -0400

    typo fix for 404

commit 9fd6465acddff5565cd558bd4a728bbd0643d829
Author: Moshe Weitzman <weitzman@tejasa.com>
Date:   Fri Aug 24 08:22:39 2012 -0400

    Incorporate some recent feedback. debounce and tableresponsive are now libraries, and not just files.

commit c0ae57ae90880b09890c22a33769cf4b452ae2af
Author: Moshe Weitzman <weitzman@tejasa.com>
Date:   Tue Aug 21 05:02:16 2012 -0400

    minor whitespace and misc.

commit 6e2163457f4b2b03c3e785c9b9d7792fcd4eda14
Author: J. Renée Beach <splendidnoise@gmail.com>
Date:   Mon Aug 20 02:41:14 2012 +0200

    Added a few more comments to tableresponsive.js

    Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>

commit 6d1c9b17a923fe5c7e8fb690e8c778230892d78f
Author: J. Renée Beach <splendidnoise@gmail.com>
Date:   Mon Aug 20 02:38:47 2012 +0200

    Issue #1276908: Commented up the tableresponsive.js file.

    Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>

commit ba91c77c088ba0d6e796d30f6a1777ee40c7673e
Author: J. Renée Beach <splendidnoise@gmail.com>
Date:   Mon Aug 20 02:38:19 2012 +0200

    Added a responsive flag to theme_table.

    Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>

commit d38c4042f7704947cfa86764b81a5dc31317892d
Author: J. Renée Beach <splendidnoise@gmail.com>
Date:   Mon Aug 20 02:37:54 2012 +0200

    Added a denounce function to drupal.js

    Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>

commit 2dc6033fc2b7c7515f3684c7f021859a2ebc7c45
Author: J. Renée Beach <splendidnoise@gmail.com>
Date:   Mon Aug 20 01:44:16 2012 +0200

    Moved the responsive table javascript to misc/ and removed it from Seven. It's generic enough that it doesn't need to be in the theme and can be in core.

    Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>

commit 73df827c8ff93df75199a35021c5dd82e9691072
Author: J. Renée Beach <splendidnoise@gmail.com>
Date:   Sun Aug 19 19:46:39 2012 +0200

    Issue #1276908: Added seven.js to the Seven theme and implemented a class that adds a link to show hidden columns on a table.

    Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>

commit f6fb5aca7fcafc75d0f92d97c52fe80b9ea2303e
Author: Moshe Weitzman <weitzman@tejasa.com>
Date:   Sun Aug 19 13:47:24 2012 -0400

    RWD for field list table.

commit 82871074f3d1caeea1c360713b0cedc1496f2dd6
Author: Moshe Weitzman <weitzman@tejasa.com>
Date:   Sun Aug 19 13:43:34 2012 -0400

    Revert node admin changes that are not needed.

commit 7dbfc8d2447f6052dfdbaf456b74f574a1f833ff
Author: Moshe Weitzman <weitzman@tejasa.com>
Date:   Sun Aug 19 13:35:25 2012 -0400

    RWD for dblog table.

commit 0cb53f88d7cc16ee2bedd38afd5bb92b8738ed05
Author: Moshe Weitzman <weitzman@tejasa.com>
Date:   Sun Aug 19 13:29:06 2012 -0400

    People table.

commit 701b908c9029d6f07ddebb490336d0e51affe44a
Author: Moshe Weitzman <weitzman@tejasa.com>
Date:   Sun Aug 19 13:19:03 2012 -0400

    Use constants for helpful/advisable.

commit 6aa47ea37182dbb958ca9a1e6cf0c38a8c66c3ba
Author: Moshe Weitzman <weitzman@tejasa.com>
Date:   Sun Aug 19 12:25:00 2012 -0400

    Document classes that responsive understands.

commit e1d47549afffdbff3ff4fcc1801f4a049a415397
Author: Moshe Weitzman <weitzman@tejasa.com>
Date:   Sun Aug 19 11:28:11 2012 -0400

    Add CSS to bartik and stark.

commit 4504c0b53fbe8f292466894cd1c0545778a74cde
Author: Moshe Weitzman <weitzman@tejasa.com>
Date:   Sun Aug 19 11:21:24 2012 -0400

    Helpful.

commit 3d567bbb204aa5b2ed7c934ab406307404783759
Author: Moshe Weitzman <weitzman@tejasa.com>
Date:   Sun Aug 19 10:45:13 2012 -0400

    remove extra $

commit 5cc2acaf0d0508771b69e0551414103fbcf3cf2a
Author: Moshe Weitzman <weitzman@tejasa.com>
Date:   Sun Aug 19 10:42:58 2012 -0400

    Copy helpful/advisable classes from header to cell as needed.

commit cb11c05b1a31b93f6d6a0dab489c07f2a587c8f3
Author: J. Renée Beach <splendidnoise@gmail.com>
Date:   Sun Aug 19 15:36:54 2012 +0200

    Issue #1276908: Changed helpful to advisable.

    Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>

commit 0f6b6678846db42868ec990555e97c83e1adc5fc
Author: J. Renée Beach <splendidnoise@gmail.com>
Date:   Sun Aug 19 15:24:20 2012 +0200

    Issue #1276908: Changed helpful to advisable.

    Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>

commit 6a5409b04d502cfedcb713a55f22e731aaa434ba
Author: J. Renée Beach <splendidnoise@gmail.com>
Date:   Sun Aug 19 14:58:26 2012 +0200

    Issue #1276908: cleaned up some CSS for margins on the side of the #page element. The RTL specifications for the left/right margins were unnecessary because the values are the same for LTR and RTL text directions.

    Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>

commit 74ff8efa775c9cb4c7dedcfcc3acfbc3fc5fa5e4
Author: J. Renée Beach <splendidnoise@gmail.com>
Date:   Sun Aug 19 14:40:18 2012 +0200

    Issue #1276908: Marked up node table cells with optional and helpful classes to facilitate reducing the table size on smaller screens.

    Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>

commit e1c23042887d063568ab50161fc23d6656c1b752
Author: J. Renée Beach <splendidnoise@gmail.com>
Date:   Sun Aug 19 14:13:41 2012 +0200

    Issue #1276908: Added initial attempt to define responsive cell hiding for tables in Seven.

    Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>

commit e6aeb64c4d3e7ba07a9dab442c6fffc4bdf25c45
Author: Moshe Weitzman <weitzman@tejasa.com>
Date:   Sun Aug 19 07:48:44 2012 -0400

    Add classes to node admin table.

Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>
---
 core/includes/common.inc                 |    2 +-
 core/includes/theme.inc                  |   57 ++++++++++-
 core/misc/debounce.js                    |   40 ++++++++
 core/misc/table.js                       |   90 +++++++++++++++++
 core/misc/tabledrag.js                   |   43 +++++----
 core/misc/tableresponsive.js             |  154 ++++++++++++++++++++++++++++++
 core/modules/comment/comment.admin.inc   |    6 +-
 core/modules/dblog/dblog.admin.inc       |    8 +-
 core/modules/field_ui/field_ui.admin.inc |    6 +-
 core/modules/node/node.admin.inc         |   30 ++++--
 core/modules/system/system.admin.inc     |    4 +-
 core/modules/system/system.base-rtl.css  |    7 ++
 core/modules/system/system.base.css      |   12 ++-
 core/modules/system/system.module        |   48 +++++++++-
 core/modules/user/user.admin.inc         |    9 +-
 core/themes/bartik/css/style.css         |   18 ++++
 core/themes/seven/style-rtl.css          |    6 +-
 core/themes/seven/style.css              |   55 ++++++++++-
 core/themes/stark/css/layout.css         |   18 ++++
 19 files changed, 559 insertions(+), 54 deletions(-)
 create mode 100644 core/misc/debounce.js
 create mode 100644 core/misc/table.js
 create mode 100644 core/misc/tableresponsive.js

diff --git a/core/includes/common.inc b/core/includes/common.inc
index 44fa367..cbb0125 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -6596,7 +6596,7 @@ function drupal_common_theme() {
       'variables' => array(),
     ),
     'table' => array(
-      'variables' => array('header' => NULL, 'rows' => NULL, 'attributes' => array(), 'caption' => NULL, 'colgroups' => array(), 'sticky' => TRUE, 'empty' => ''),
+      'variables' => array('header' => NULL, 'rows' => NULL, 'attributes' => array(), 'caption' => NULL, 'colgroups' => array(), 'sticky' => TRUE, 'responsive' => TRUE, 'empty' => ''),
     ),
     'meter' => array(
       'variables' => array('display_value' => NULL, 'form' => NULL, 'high' => NULL, 'low' => NULL, 'max' => NULL, 'min' => NULL, 'optimum' => NULL, 'value' => NULL, 'percentage' => NULL, 'attributes' => array()),
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 504030e..d5a79d9 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -33,6 +33,16 @@ const MARK_NEW = 1;
 const MARK_UPDATED = 2;
 
 /**
+ * A responsive table class.
+ */
+const RWD_ADVISABLE = 'advisable';
+
+/**
+ * A responsive table class.
+ */
+const RWD_HELPFUL = 'helpful';
+
+/**
  * @} End of "defgroup content_flags".
  */
 
@@ -1767,6 +1777,9 @@ function theme_breadcrumb($variables) {
  *     - "field": The database field represented in the table column (required
  *       if user is to be able to sort on this column).
  *     - "sort": A default sort order for this column ("asc" or "desc").
+ *     - "class": An array of values for 'class' attribute. In particular,
+ *        less important columns should have 'adviseable' or 'helpful' values.
+ *        Themes may hide less important columns for narrow viewports.
  *     - Any HTML attributes, such as "colspan", to apply to the column header
  *       cell.
  *   - rows: An array of table rows. Every row is an array of cells, or an
@@ -1837,15 +1850,25 @@ function theme_table($variables) {
   $caption = $variables['caption'];
   $colgroups = $variables['colgroups'];
   $sticky = $variables['sticky'];
+  $responsive = $variables['responsive'];
   $empty = $variables['empty'];
-
+  
+  // Add the standard table behaviors.
+  drupal_add_library('system', 'drupal.table');
   // Add sticky headers, if applicable.
   if (count($header) && $sticky) {
-    drupal_add_js('core/misc/tableheader.js');
+    drupal_add_library('system', 'drupal.tableheader');
     // Add 'sticky-enabled' class to the table to identify it for JS.
     // This is needed to target tables constructed by this function.
     $attributes['class'][] = 'sticky-enabled';
   }
+  // If columns are hidden, add a link to show the columns.
+  if (count($header) && $responsive) {
+    drupal_add_library('system', 'drupal.tableresponsive');
+    // Add 'responsive-enabled' class to the table to identify it for JS.
+    // This is needed to target tables constructed by this function.
+    $attributes['class'][] = 'responsive-enabled';
+  }
 
   $output = '<table' . drupal_attributes($attributes) . ">\n";
 
@@ -1902,16 +1925,31 @@ function theme_table($variables) {
     $rows[] = array(array('data' => $empty, 'colspan' => $header_count, 'class' => array('empty', 'message')));
   }
 
+  $responsive = array();
   // Format the table header:
   if (count($header)) {
     $ts = tablesort_init($header);
     // HTML requires that the thead tag has tr tags in it followed by tbody
     // tags. Using ternary operator to check and see if we have any rows.
     $output .= (count($rows) ? ' <thead><tr>' : ' <tr>');
+    $i=0;
     foreach ($header as $cell) {
+      $i++;
+
+      // Track responsive classes for each column as needed.
+      if (!empty($cell['class']) && is_array($cell['class'])) {
+        if (in_array(RWD_ADVISABLE, $cell['class'])) {
+          $responsive[$i] =  RWD_ADVISABLE;
+        }
+        elseif (in_array(RWD_HELPFUL, $cell['class'])) {
+          $responsive[$i] =  RWD_HELPFUL;
+        }
+      }
+
       $cell = tablesort_header($cell, $header, $ts);
       $output .= _theme_table_cell($cell, TRUE);
     }
+
     // Using ternary operator to close the tags based on whether or not there are rows
     $output .= (count($rows) ? " </tr></thead>\n" : "</tr>\n");
   }
@@ -1952,7 +1990,20 @@ function theme_table($variables) {
         $output .= ' <tr' . drupal_attributes($attributes) . '>';
         $i = 0;
         foreach ($cells as $cell) {
-          $cell = tablesort_cell($cell, $header, $ts, $i++);
+          $i++;
+          // Add active class if needed for sortable tables.
+          $cell = tablesort_cell($cell, $header, $ts, $i);
+
+          // Copy advisable/helpful class from header to cell as needed.
+          if (isset($responsive[$i])) {
+            if (is_array($cell)) {
+              $cell['class'][] = $responsive[$i];
+            }
+            else {
+              $cell = array('data' => $cell, 'class' => $responsive[$i]);
+            }
+          }
+
           $output .= _theme_table_cell($cell);
         }
         $output .= " </tr>\n";
diff --git a/core/misc/debounce.js b/core/misc/debounce.js
new file mode 100644
index 0000000..2a784a0
--- /dev/null
+++ b/core/misc/debounce.js
@@ -0,0 +1,40 @@
+/**
+ * @file debounce.js
+ *
+ * Returns a function that will not invoked while it continues to be called,
+ * until the wait period has experied without a call to the function.
+ *
+ * Use this function to prevent frequent calls to functions, for
+ * example like event handlers attached to the resize event.
+ *
+ * To use debounce, pass your function to debounce as the first argument
+ * and the waiting period as the second.
+ *
+ * If immediate is passed as true, the provided function will be invoked before
+ * the wait time has elapsed instead of after, once the debounce-wrapped function
+ * ceases to be called.
+ */
+(function (Drupal, undefined) {
+
+"use strict";
+
+  Drupal.debounce = function (fn, wait, immediate) {
+    var timeout;
+    return function () {
+      var context = this;
+      var args = arguments;
+      var deferred = function () {
+        timeout = null;
+        if (!immediate) {
+          fn.apply(context, args);
+        }
+      };
+      var invoke = immediate && !timeout;
+      clearTimeout(timeout);
+      timeout = setTimeout(deferred, wait);
+      if (invoke) {
+        fn.apply(context, args);
+      }
+    };
+  };
+})(Drupal);
diff --git a/core/misc/table.js b/core/misc/table.js
new file mode 100644
index 0000000..6b03bfd
--- /dev/null
+++ b/core/misc/table.js
@@ -0,0 +1,90 @@
+/**
+ * table.js
+ *
+ * Behaviors to facilitate interactivity with tables.
+ */
+(function ($) {
+
+"use strict";
+
+  /**
+   * Attach the table function to Drupal.behaviors.
+   */
+  Drupal.behaviors.table = {
+    attach: function (context, settings) {
+      $(context).find('table').once('table', function () {
+        $(this).data("drupal-table", new Drupal.Table(this));
+      });
+    }
+  };
+  /**
+   * The Table manages behaviors that enhance table element interactivity.
+   */
+  Drupal.Table = function (table) {
+    var self = this;
+    this.$table = $(table);
+    this.actions = $();
+    this.hasActions = false;
+    // Build a link that will toggle the column visibility.
+    this.$actionsContainer = $('<div>', {
+      'class': 'table-actions'
+    })
+    .data('drupal-table', {});
+  };
+  /**
+   * Add a link to perform an action on the table to the list of table actions.
+   *
+   * @param actions: actions may be an object or an array of action objects.
+   * An action object has the following structure:
+   *
+   * action = {
+   *   'href': 'url',
+   *   'text': 'string',
+   *   'attributes': {
+   *     'title': 'title attribute description',
+   *     'class': 'string of classes'
+   *   },
+   *   'callback': function () {}
+   * };
+   */
+  Drupal.Table.prototype.addAction = function (actions) {
+    // Keep a list of actions added during this method call.
+    var newActions = $();
+    // Actions could be a single action or an array.
+    if (!$.isArray(actions)) {
+      actions = [actions];
+    }
+    // If the table does not have any actions yet, prepend the container
+    // that will display them.
+    if (!this.hasActions) {
+      this.$actionsContainer.insertBefore(this.$table);
+    }
+    // Append the actions to the list of table actions.
+    for (var i = 0; i < actions.length; i++) {
+      var link;
+      var action = actions[i];
+      // If the action doesn't have a callback, there is no reason to proceed.
+      if ('callback' in action && typeof action.callback === 'function') {
+        var attributes = $.extend({}, ('attributes' in action && typeof action.attributes === 'object') ? action.attributes : {});
+        this.$actionsContainer.append(
+          link = $('<a>', {
+            'href': ('href' in action) ? action.href : '#',
+            'text': ('text' in action) ? Drupal.t(action.text) : Drupal.t('No text provided'),
+            'title': ('title' in attributes) ? Drupal.t(attributes.title) : '',
+            'class': ('class' in attributes) ? attributes['class'] : '',
+            // Aria information.
+            'role': 'button',
+          })
+          .on({
+            'click': action.callback
+          })
+        );
+        newActions = $().add(newActions).add(link);
+      }
+    }
+    // Store the actions added in this object's list of actions.
+    this.actions = $().add(this.actions).add(newActions);
+    // Return just the actions that were added.
+    return newActions;
+  };
+})(jQuery);
diff --git a/core/misc/tabledrag.js b/core/misc/tabledrag.js
index 1e2b6cc..88d4ee5 100644
--- a/core/misc/tabledrag.js
+++ b/core/misc/tabledrag.js
@@ -44,7 +44,8 @@ Drupal.tableDrag = function (table, tableSettings) {
   var $table = $(table);
 
   // Required object variables.
-  this.table = table;
+  this.$table = $(table);
+  this.table = this.$table.data('drupal-table');
   this.tableSettings = tableSettings;
   this.dragObject = null; // Used to hold information about a current drag operation.
   this.rowObject = null; // Provides operations for row manipulation.
@@ -52,7 +53,9 @@ Drupal.tableDrag = function (table, tableSettings) {
   this.oldY = 0; // Used to determine up or down direction from last mouse move.
   this.changed = false; // Whether anything in the entire table has changed.
   this.maxDepth = 0; // Maximum amount of allowed parenting.
-  this.rtl = $(this.table).css('direction') === 'rtl' ? -1 : 1; // Direction of the table.
+  this.rtl = this.$table.css('direction') === 'rtl' ? -1 : 1; // Direction of the table.
+  this.showText = Drupal.t('Show row weights');
+  this.hideText = Drupal.t('Hide row weights');
 
   // Configure the scroll settings.
   this.scrollSettings = { amount: 4, interval: 50, trigger: 70 };
@@ -92,16 +95,22 @@ Drupal.tableDrag = function (table, tableSettings) {
   // Match immediate children of the parent element to allow nesting.
   $table.find('> tr.draggable, > tbody > tr.draggable').each(function () { self.makeDraggable(this); });
 
-  // Add a link before the table for users to show or hide weight columns.
-  $table.before($('<a href="#" class="tabledrag-toggle-weight"></a>')
-    .attr('title', Drupal.t('Re-order rows by numerical weight instead of dragging.'))
-    .click($.proxy(function (e) {
-      e.preventDefault();
-      this.toggleColumns();
-    }, this))
-    .wrap('<div class="tabledrag-toggle-weight-wrapper"></div>')
-    .parent()
-  );
+  // Build a link before the table for users to show or hide weight columns.
+  this.actionLink = {
+    'text': this.showText,
+    'attributes': {
+      'title': Drupal.t('Re-order rows by numerical weight instead of dragging.'),
+      'class': 'tabledrag-toggle-weight',
+    },
+    'namespace': 'drupal-tabledrag',
+    'callback': $.proxy(function (event) {
+        event.preventDefault();
+        this.toggleColumns();
+      }, this)
+  };
+  // Add the toggle to the table's control bar.
+  this.$columnToggle = this.table.addAction([this.actionLink]);
+  this.$columnToggle.data('drupal-tabledrag', {});
 
   // Initialize the specified columns (for example, weight or parent columns)
   // to show or hide according to user preference. This aids accessibility
@@ -133,7 +142,7 @@ Drupal.tableDrag = function (table, tableSettings) {
  * 'Drupal.tableDrag.showWeight' localStorage value.
  */
 Drupal.tableDrag.prototype.initColumns = function () {
-  var $table = $(this.table);
+  var $table = this.$table;
   for (var group in this.tableSettings) {
     // Find the first field in this group.
     for (var d in this.tableSettings[group]) {
@@ -227,7 +236,7 @@ Drupal.tableDrag.prototype.hideColumns = function () {
     this.colSpan = this.colSpan - 1;
   });
   // Change link text.
-  $('.tabledrag-toggle-weight').text(Drupal.t('Show row weights'));
+  $('.tabledrag-toggle-weight').text(this.showText);
 };
 
 /**
@@ -245,7 +254,7 @@ Drupal.tableDrag.prototype.showColumns = function () {
     this.colSpan = this.colSpan + 1;
   });
   // Change link text.
-  $('.tabledrag-toggle-weight').text(Drupal.t('Hide row weights'));
+  $('.tabledrag-toggle-weight').text(this.hideText);
 };
 
 /**
@@ -751,7 +760,7 @@ Drupal.tableDrag.prototype.updateField = function (changedRow, group) {
       // Use the first row in the table as source, because it's guaranteed to
       // be at the root level. Find the first item, then compare this row
       // against it as a sibling.
-      sourceRow = $(this.table).find('tr.draggable:first').get(0);
+      sourceRow = this.$table.find('tr.draggable:first').get(0);
       if (sourceRow === this.rowObject.element) {
         sourceRow = $(this.rowObject.group[this.rowObject.group.length - 1]).next('tr.draggable').get(0);
       }
@@ -873,7 +882,7 @@ Drupal.tableDrag.prototype.restripeTable = function () {
   // :even and :odd are reversed because jQuery counts from 0 and
   // we count from 1, so we're out of sync.
   // Match immediate children of the parent element to allow nesting.
-  $(this.table).find('> tbody > tr.draggable:visible, > tr.draggable:visible')
+  this.$table.find('> tbody > tr.draggable:visible, > tr.draggable:visible')
     .removeClass('odd even')
     .filter(':odd').addClass('even').end()
     .filter(':even').addClass('odd');
diff --git a/core/misc/tableresponsive.js b/core/misc/tableresponsive.js
new file mode 100644
index 0000000..977feeb
--- /dev/null
+++ b/core/misc/tableresponsive.js
@@ -0,0 +1,154 @@
+/**
+ * tableresponsive.js
+ *
+ * Behaviors to facilitate the presentation of tables across screens of any size.
+ */
+(function ($) {
+
+"use strict";
+
+  /**
+   * Attach the tableResponsive function to Drupal.behaviors.
+   */
+  Drupal.behaviors.tableResponsive = {
+    attach: function (context, settings) {
+      $(context).find('table.responsive-enabled').once('tableresponsive', function () {
+        $(this).data("drupal-tableresponsive", new Drupal.tableResponsive(this));
+      });
+    }
+  };
+  /**
+   * A responsive table hides columns at small screen sizes, leaving the most
+   * important columns visible to the end user. Users should not be prevented from
+   * accessing all columns, however. This class adds a toggle to a table with
+   * hidden columns that exposes the columns. Exposing the columns will likely
+   * break layouts, but it provides the user with a means to access data, which
+   * is a guiding principle of responsive design.
+   */
+  Drupal.tableResponsive = function (table) {
+    var self = this;
+    this.$table = $(table);
+    this.table = this.$table.data('drupal-table');
+    this.showText = Drupal.t('Show all columns');
+    this.hideText = Drupal.t('Hide unimportant columns');
+    // Store a reference to the header elements of the table so that the DOM is
+    // traversed only once to find them.
+    this.$headers = this.$table.find('th');
+    // Build a link that will toggle the column visibility.
+    this.actionLink = {
+      'text': this.showText,
+      'attributes': {
+        'title': Drupal.t('Expose table cells that were hidden to make the table fit within a small screen.'),
+        'class': 'responsive-table-toggle',
+        'aria-disabled': 'false'
+      },
+      'namespace': 'drupal-tableresponsive',
+      'callback': $.proxy(this, 'eventhandlerToggleColumns')
+    };
+    // Add the toggle to the table's control bar.
+    this.$columnToggle = this.table.addAction([this.actionLink]);
+    this.$columnToggle.data('drupal-tableresponsive', {});
+    // Attach a resize handler to the window.
+    $(window)
+      .bind('resize.drupal-tableresponsive', Drupal.debounce($.proxy(this, 'eventhandlerEvaluateColumnVisibility'), 250))
+      .triggerHandler('resize.drupal-tableresponsive');
+  };
+  /**
+   * Associates an action link with the table that will show hidden columns.
+   * Columns are assumed to be hidden if their header's display property is none
+   * or if the visibility property is hidden.
+   */
+  Drupal.tableResponsive.prototype.eventhandlerEvaluateColumnVisibility = function (event) {
+    var self = this;
+    var $headers = this.$headers;
+    var $toggle = this.$columnToggle;
+    var $hiddenHeaders = $headers.filter('.advisable:hidden, .helpful:hidden');
+    var hiddenLength = $hiddenHeaders.length;
+    var toggleData = $toggle.data('drupal-tableresponsive');
+    // If the table has hidden columns, associate an action link with the table
+    // to show the columns.
+    if (hiddenLength > 0) {
+      $toggle
+      .show()
+      .attr('aria-disabled', 'false');
+      
+    }
+    // When the toggle is sticky, its presence is maintained because the user has
+    // interacted with it. This is necessary to keep the link visible if the user
+    // adjusts screen size and changes the visibilty of columns.
+    if ((!('sticky' in toggleData) && hiddenLength === 0) || ('sticky' in toggleData && !toggleData.sticky && hiddenLength === 0)) {
+      $toggle
+      .hide()
+      .attr('aria-disabled', 'true');
+      delete this.$columnToggle.data('drupal-tableresponsive').sticky;
+    }
+  };
+  /**
+   * Reveal hidden columns and hide any columns that were revealed because they were
+   * previously hidden.
+   */
+  Drupal.tableResponsive.prototype.eventhandlerToggleColumns = function (event) {
+    event.preventDefault();
+    var self = this;
+    var $headers = this.$headers;
+    var $hiddenHeaders = this.$headers.filter('.advisable:hidden, .helpful:hidden');
+    this.$revealedCells = this.$revealedCells || $();
+    // Reveal hidden columns.
+    if ($hiddenHeaders.length > 0) {
+      $hiddenHeaders.each(function (index, element) {
+        var $header = $(this);
+        var position = Number($header.prevAll('th').length);
+        $('tbody tr', this.$table).each(function (index, element) {
+          var $row = $(this);
+          var $cells = $row.find('td:eq(' + position + ')');
+          $cells.show();
+          // Keep track of the revealed cells, so they can be hidden later.
+          self.$revealedCells = $().add(self.$revealedCells).add($cells);
+        });
+        $header.show();
+        // Keep track of the revealed headers, so they can be hidden later.
+        self.$revealedCells = $().add(self.$revealedCells).add($header);
+
+
+      });
+      this.$columnToggle.text(this.hideText);
+      this.$columnToggle.data('drupal-tableresponsive').sticky = true;
+    }
+    // Hide revealed columns.
+    else {
+      this.$revealedCells.hide();
+      this.$columnToggle.text(this.showText);
+      // Strip out display
+      this.$revealedCells.each(function (index, element) {
+        var $cell = $(this);
+        var style = $cell.attr('style');
+        var properties = style.split(';');
+        var newProps = [];
+        // The columns should simply have the display table-cell property
+        // removed, which the jQuery hide method does. The hide method
+        // also adds display none to the element. The element should be
+        // returned to the same state it was in before the columns were
+        // revealed, so it is necessary to remove the display none
+        // value from the style attribute.
+        var match =  /^display\s*\:\s*none$/;
+        for (var i = 0; i < properties.length; i++) {
+          var prop = properties[i]
+          prop.trim();
+          // Find the display:none property and remove it.
+          var isDisplayNone = match.exec(prop);
+          if (isDisplayNone) {
+            continue;
+          }
+          newProps.push(prop);
+        }
+        // Return the rest of the style attribute values to the element.
+        $cell.attr('style', newProps.join(';'));
+      });
+      delete this.$revealedCells;
+      this.$columnToggle.data('drupal-tableresponsive').sticky = false;
+      // Refresh the toggle link.
+      $(window)
+      .triggerHandler('resize.drupal-tableresponsive');
+    }
+  };
+})(jQuery);
diff --git a/core/modules/comment/comment.admin.inc b/core/modules/comment/comment.admin.inc
index 6210cc5..5737ea4 100644
--- a/core/modules/comment/comment.admin.inc
+++ b/core/modules/comment/comment.admin.inc
@@ -73,9 +73,9 @@ function comment_admin_overview($form, &$form_state, $arg) {
   $status = ($arg == 'approval') ? COMMENT_NOT_PUBLISHED : COMMENT_PUBLISHED;
   $header = array(
     'subject' => array('data' => t('Subject'), 'field' => 'subject'),
-    'author' => array('data' => t('Author'), 'field' => 'name'),
-    'posted_in' => array('data' => t('Posted in'), 'field' => 'node_title'),
-    'changed' => array('data' => t('Updated'), 'field' => 'c.changed', 'sort' => 'desc'),
+    'author' => array('data' => t('Author'), 'field' => 'name', 'class' => array(RWD_ADVISABLE)),
+    'posted_in' => array('data' => t('Posted in'), 'field' => 'node_title', 'class' => array(RWD_HELPFUL)),
+    'changed' => array('data' => t('Updated'), 'field' => 'c.changed', 'sort' => 'desc', 'class' => array(RWD_HELPFUL)),
     'operations' => array('data' => t('Operations')),
   );
 
diff --git a/core/modules/dblog/dblog.admin.inc b/core/modules/dblog/dblog.admin.inc
index 8928f46..b3fe920 100644
--- a/core/modules/dblog/dblog.admin.inc
+++ b/core/modules/dblog/dblog.admin.inc
@@ -30,11 +30,11 @@ function dblog_overview() {
 
   $header = array(
     '', // Icon column.
-    array('data' => t('Type'), 'field' => 'w.type'),
-    array('data' => t('Date'), 'field' => 'w.wid', 'sort' => 'desc'),
+    array('data' => t('Type'), 'field' => 'w.type', 'class' => array(RWD_ADVISABLE)),
+    array('data' => t('Date'), 'field' => 'w.wid', 'sort' => 'desc', 'class' => array(RWD_HELPFUL)),
     t('Message'),
-    array('data' => t('User'), 'field' => 'u.name'),
-    array('data' => t('Operations')),
+    array('data' => t('User'), 'field' => 'u.name', 'class' => array(RWD_ADVISABLE)),
+    array('data' => t('Operations'), 'class' => array(RWD_HELPFUL)),
   );
 
   $query = db_select('watchdog', 'w')
diff --git a/core/modules/field_ui/field_ui.admin.inc b/core/modules/field_ui/field_ui.admin.inc
index 5871be0..cc2bd1c 100644
--- a/core/modules/field_ui/field_ui.admin.inc
+++ b/core/modules/field_ui/field_ui.admin.inc
@@ -17,7 +17,11 @@ function field_ui_fields_list() {
 
   $modules = system_rebuild_module_data();
 
-  $header = array(t('Field name'), t('Field type'), t('Used in'));
+  $header = array(
+    t('Field name'),
+    array('data' => t('Field type'), 'class' => array(RWD_ADVISABLE)),
+    t('Used in'),
+  );
   $rows = array();
   foreach ($instances as $entity_type => $type_bundles) {
     foreach ($type_bundles as $bundle => $bundle_instances) {
diff --git a/core/modules/node/node.admin.inc b/core/modules/node/node.admin.inc
index 0bf8a91..daf59e5 100644
--- a/core/modules/node/node.admin.inc
+++ b/core/modules/node/node.admin.inc
@@ -436,14 +436,32 @@ function node_admin_nodes() {
 
   // Build the sortable table header.
   $header = array(
-    'title' => array('data' => t('Title'), 'field' => 'n.title'),
-    'type' => array('data' => t('Content type'), 'field' => 'n.type'),
-    'author' => t('Author'),
-    'status' => array('data' => t('Status'), 'field' => 'n.status'),
-    'changed' => array('data' => t('Updated'), 'field' => 'n.changed', 'sort' => 'desc')
+    'title' => array(
+      'data' => t('Title'),
+      'field' => 'n.title',
+    ),
+    'type' => array(
+      'data' => t('Content type'),
+      'field' => 'n.type',
+      'class' => array(RWD_ADVISABLE),
+    ),
+    'author' => array(
+      'data' => t('Author'),
+      'class' => array(RWD_HELPFUL),
+    ),
+    'status' => array(
+      'data' => t('Status'),
+      'field' => 'n.status',
+    ),
+    'changed' => array(
+      'data' => t('Updated'),
+      'field' => 'n.changed',
+      'sort' => 'desc',
+      'class' => array(RWD_HELPFUL)
+    ,)
   );
   if ($multilingual) {
-    $header['language_name'] = array('data' => t('Language'), 'field' => 'n.langcode');
+    $header['language_name'] = array('data' => t('Language'), 'field' => 'n.langcode', 'class' => array(RWD_HELPFUL));
   }
   $header['operations'] = array('data' => t('Operations'));
 
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index 72c50d4..d4c0f04 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -917,8 +917,8 @@ function system_modules($form, $form_state = array()) {
       '#header' => array(
         array('data' => t('Enabled'), 'class' => array('checkbox')),
         t('Name'),
-        t('Version'),
-        t('Description'),
+        array('data' => t('Version'), 'class' => array(RWD_ADVISABLE)),
+        array('data' => t('Description'), 'class' => array(RWD_HELPFUL)),
         array('data' => t('Operations'), 'colspan' => 3),
       ),
       // Ensure that the "Core" package fieldset comes first.
diff --git a/core/modules/system/system.base-rtl.css b/core/modules/system/system.base-rtl.css
index d01792e..25a30ec 100644
--- a/core/modules/system/system.base-rtl.css
+++ b/core/modules/system/system.base-rtl.css
@@ -29,6 +29,13 @@
 }
 
 /**
+ * Table behavior.
+ */
+.table-actions > a {
+  margin-left: 1.5em;
+  margin-right: 0;
+}
+/**
  * TableDrag behavior.
  */
 .draggable a.tabledrag-handle {
diff --git a/core/modules/system/system.base.css b/core/modules/system/system.base.css
index f8a7726..ce21006 100644
--- a/core/modules/system/system.base.css
+++ b/core/modules/system/system.base.css
@@ -136,8 +136,18 @@ div.tree-child-horizontal {
   text-align: right; /* LTR */
 }
 
+
+/**
+ * Table behavior.
+ *
+ * @see core/misc/table.js
+ */
+.table-actions > a {
+  margin-right: 1.5em; /* LTR */
+}
+
 /**
- * TableHeader behavior.
+ * Tableheader behavior.
  *
  * @see tableheader.js
  */
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 69a1f62..064e243 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -1241,6 +1241,15 @@ function system_library_info() {
     ),
   );
 
+  // Drupal's debounce API.
+  $libraries['drupal.debounce'] = array(
+    'title' => 'Drupal debounce API',
+    'version' => VERSION,
+    'js' => array(
+      'core/misc/debounce.js' => array('group' => JS_LIBRARY),
+    ),
+  );
+
   // Drupal's progress indicator.
   $libraries['drupal.progress'] = array(
     'title' => 'Drupal progress indicator',
@@ -1268,15 +1277,50 @@ function system_library_info() {
     ),
   );
 
+  // Drupal's table library.
+  $libraries['drupal.table'] = array(
+    'title' => 'Drupal table API',
+    'version' => VERSION,
+    'js' => array(
+      'core/misc/table.js' => array('group' => JS_LIBRARY,),
+    ),
+  );
+
+  // Drupal's tableheader library.
+  $libraries['drupal.tableheader'] = array(
+    'title' => 'Drupal tableheader API',
+    'version' => VERSION,
+    'js' => array(
+      'core/misc/tableheader.js' => array('group' => JS_LIBRARY,),
+    ),
+    'dependencies' => array(
+      array('system', 'drupal.table',),
+    ),
+  );
+
   // Drupal's tabledrag library.
   $libraries['drupal.tabledrag'] = array(
     'title' => 'Drupal tabledrag',
     'version' => VERSION,
     'js' => array(
-      'core/misc/tabledrag.js' => array('group' => JS_LIBRARY, 'weight' => -1),
+      'core/misc/tabledrag.js' => array('group' => JS_LIBRARY, 'cache' => FALSE),
     ),
     'dependencies' => array(
-      array('system', 'jquery.cookie'),
+      array('system', 'jquery.cookie',),
+      array('system', 'drupal.table',),
+    ),
+  );
+
+  // Drupal's responsive table API.
+  $libraries['drupal.tableresponsive'] = array(
+    'title' => 'Drupal responsive table API',
+    'version' => VERSION,
+    'js' => array(
+      'core/misc/tableresponsive.js' => array('group' => JS_LIBRARY,),
+    ),
+    'dependencies' => array(
+      array('system', 'drupal.debounce',),
+      array('system', 'drupal.table',),
     ),
   );
 
diff --git a/core/modules/user/user.admin.inc b/core/modules/user/user.admin.inc
index 2268ae1..4436201 100644
--- a/core/modules/user/user.admin.inc
+++ b/core/modules/user/user.admin.inc
@@ -145,10 +145,10 @@ function user_admin_account() {
 
   $header = array(
     'username' => array('data' => t('Username'), 'field' => 'u.name'),
-    'status' => array('data' => t('Status'), 'field' => 'u.status'),
-    'roles' => array('data' => t('Roles')),
-    'member_for' => array('data' => t('Member for'), 'field' => 'u.created', 'sort' => 'desc'),
-    'access' => array('data' => t('Last access'), 'field' => 'u.access'),
+    'status' => array('data' => t('Status'), 'field' => 'u.status', 'class' => array(RWD_HELPFUL)),
+    'roles' => array('data' => t('Roles'), 'class' => array(RWD_HELPFUL)),
+    'member_for' => array('data' => t('Member for'), 'field' => 'u.created', 'sort' => 'desc', 'class' => array(RWD_HELPFUL)),
+    'access' => array('data' => t('Last access'), 'field' => 'u.access', 'class' => array(RWD_HELPFUL)),
     'operations' => array('data' => t('Operations')),
   );
 
@@ -1051,4 +1051,3 @@ function user_admin_role_delete_confirm_submit($form, &$form_state) {
   drupal_set_message(t('The role has been deleted.'));
   $form_state['redirect'] = 'admin/people/roles';
 }
-
diff --git a/core/themes/bartik/css/style.css b/core/themes/bartik/css/style.css
index 117a2d5..3a11800 100644
--- a/core/themes/bartik/css/style.css
+++ b/core/themes/bartik/css/style.css
@@ -1733,3 +1733,21 @@ div.admin-panel .description {
     padding-bottom: 2em;
   }
 }
+
+/**
+ * Responsive tables
+ */
+@media screen and (max-width:28.125em) { /* 450px */
+  th.helpful,
+  td.helpful,
+  th.advisable,
+  td.advisable {
+    display: none;
+  }
+}
+@media screen and (max-width:45em) { /* 720px */
+  th.helpful,
+  td.helpful {
+    display: none;
+  }
+}
diff --git a/core/themes/seven/style-rtl.css b/core/themes/seven/style-rtl.css
index fd9f2ee..ec3d3f2 100644
--- a/core/themes/seven/style-rtl.css
+++ b/core/themes/seven/style-rtl.css
@@ -71,10 +71,8 @@ ul.primary {
 /**
  * Page layout.
  */
-#page {
-  padding: 20px 0 40px 0;
-  margin-left: 40px;
-  margin-right: 40px;
+#secondary-links ul.links li {
+  padding: 0 0 10px 10px;
 }
 ul.links li,
 ul.inline li {
diff --git a/core/themes/seven/style.css b/core/themes/seven/style.css
index 4e9dadf..9659822 100644
--- a/core/themes/seven/style.css
+++ b/core/themes/seven/style.css
@@ -317,13 +317,43 @@ ul.secondary li.active a.active {
  * Page layout.
  */
 #page {
-  padding: 20px 0 40px 0; /* LTR */
-  margin-right: 40px; /* LTR */
-  margin-left: 40px; /* LTR */
+  padding: 20px 0 40px 0;
+  margin-right: 0.8125em;
+  margin-left: 0.8125em;
   background: #fff;
   position: relative;
   color: #333;
 }
+@media screen and (min-width:28.125em) { /* 450px */
+  #page {
+    margin-left: 1.25em;
+    margin-right: 1.25em;
+  }
+}
+@media screen and (min-width:45em) { /* 720px */
+  #page {
+    margin-left: 2.5em;
+    margin-right: 2.5em;
+  }
+}
+#secondary-links ul.links li {
+  padding: 0 10px 10px 0; /* LTR */
+}
+#secondary-links ul.links li a {
+  font-size: 0.923em;
+  background: #777;
+  color: #fff;
+  text-align: center;
+  padding: 5px;
+  height: 55px;
+  width: 80px;
+  overflow: hidden;
+  -moz-border-radius: 5px;
+  border-radius: 5px;
+}
+#secondary-links ul.links li a:hover {
+  background: #999;
+}
 ul.links li,
 ul.inline li {
   padding-right: 1em; /* LTR */
@@ -477,8 +507,23 @@ table.system-status-report tr.error {
 tr td:last-child {
   border-right: 1px solid #bebfb9; /* LTR */
 }
-
-
+/**
+ * Responsive tables
+ */
+@media screen and (max-width:28.125em) { /* 450px */
+  th.helpful,
+  td.helpful,
+  th.advisable,
+  td.advisable {
+    display: none;
+  }
+}
+@media screen and (max-width:45em) { /* 720px */
+  th.helpful,
+  td.helpful {
+    display: none;
+  }
+}
 /**
  * Fieldsets.
  *
diff --git a/core/themes/stark/css/layout.css b/core/themes/stark/css/layout.css
index a31a774..7055238 100644
--- a/core/themes/stark/css/layout.css
+++ b/core/themes/stark/css/layout.css
@@ -93,3 +93,21 @@ img {
     width: 20%;
   }
 }
+
+/**
+ * Responsive tables
+ */
+@media screen and (max-width:28.125em) { /* 450px */
+  th.helpful,
+  td.helpful,
+  th.advisable,
+  td.advisable {
+    display: none;
+  }
+}
+@media screen and (max-width:45em) { /* 720px */
+  th.helpful,
+  td.helpful {
+    display: none;
+  }
+}
-- 
1.7.10.4

