? drupal.storage-collapse.11.patch
? drupal.storage-collapse.12.patch
? sites/default/files
? sites/default/settings.php
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1276
diff -u -p -r1.1276 common.inc
--- includes/common.inc	15 Dec 2010 23:50:56 -0000	1.1276
+++ includes/common.inc	18 Dec 2010 05:38:16 -0000
@@ -3953,7 +3953,10 @@ function drupal_add_js($data = NULL, $op
       $javascript = array(
         'settings' => array(
           'data' => array(
-            array('basePath' => base_path()),
+            array(
+              'basePath' => base_path(),
+              'q' => $_GET['q'],
+            ),
           ),
           'type' => 'setting',
           'scope' => 'header',
Index: misc/collapse.js
===================================================================
RCS file: /cvs/drupal/drupal/misc/collapse.js,v
retrieving revision 1.32
diff -u -p -r1.32 collapse.js
--- misc/collapse.js	14 May 2010 16:45:56 -0000	1.32
+++ misc/collapse.js	18 Dec 2010 05:38:16 -0000
@@ -1,69 +1,25 @@
 // $Id: collapse.js,v 1.32 2010/05/14 16:45:56 dries Exp $
 (function ($) {
 
-/**
- * Toggle the visibility of a fieldset using smooth animations.
- */
-Drupal.toggleFieldset = function (fieldset) {
-  var $fieldset = $(fieldset);
-  if ($fieldset.is('.collapsed')) {
-    var $content = $('> .fieldset-wrapper', fieldset).hide();
-    $fieldset
-      .removeClass('collapsed')
-      .trigger({ type: 'collapsed', value: false })
-      .find('> legend span.fieldset-legend-prefix').html(Drupal.t('Hide'));
-    $content.slideDown({
-      duration: 'fast',
-      easing: 'linear',
-      complete: function () {
-        Drupal.collapseScrollIntoView(fieldset);
-        fieldset.animating = false;
-      },
-      step: function () {
-        // Scroll the fieldset into view.
-        Drupal.collapseScrollIntoView(fieldset);
-      }
-    });
-  }
-  else {
-    $fieldset.trigger({ type: 'collapsed', value: true });
-    $('> .fieldset-wrapper', fieldset).slideUp('fast', function () {
-      $fieldset
-        .addClass('collapsed')
-        .find('> legend span.fieldset-legend-prefix').html(Drupal.t('Show'));
-      fieldset.animating = false;
-    });
-  }
-};
-
-/**
- * Scroll a given fieldset into view as much as possible.
- */
-Drupal.collapseScrollIntoView = function (node) {
-  var h = document.documentElement.clientHeight || document.body.clientHeight || 0;
-  var offset = document.documentElement.scrollTop || document.body.scrollTop || 0;
-  var posY = $(node).offset().top;
-  var fudge = 55;
-  if (posY + node.offsetHeight + fudge > h + offset) {
-    if (node.offsetHeight > h) {
-      window.scrollTo(0, posY);
-    }
-    else {
-      window.scrollTo(0, posY + node.offsetHeight - h + fudge);
-    }
-  }
-};
-
 Drupal.behaviors.collapse = {
   attach: function (context, settings) {
+    var self = this;
     $('fieldset.collapsible', context).once('collapse', function () {
       var $fieldset = $(this);
       // Expand fieldset if there are errors inside, or if it contains an
-      // element that is targeted by the uri fragment identifier. 
+      // element that is targeted by the uri fragment identifier.
+      // @todo This should be executed once per page only, not each time
+      //   behaviors are attached.
       var anchor = location.hash && location.hash != '#' ? ', ' + location.hash : '';
       if ($('.error' + anchor, $fieldset).length) {
         $fieldset.removeClass('collapsed');
       }
+      if (Drupal.storage.supported) {
+        var uncollapse = Drupal.storage.load('system.collapse.' + Drupal.settings.q);
+        if (uncollapse) {
+          $(uncollapse).removeClass('collapsed');
+        }
+      }
 
       var summary = $('<span class="summary"></span>');
       $fieldset.
@@ -91,14 +47,83 @@ Drupal.behaviors.collapse = {
           // Don't animate multiple times.
           if (!fieldset.animating) {
             fieldset.animating = true;
-            Drupal.toggleFieldset(fieldset);
+            self.toggle(fieldset);
           }
           return false;
         });
 
       $legend.append(summary);
     });
-  }
+  },
+
+  /**
+   * Toggle the visibility of a fieldset using smooth animations.
+   */
+  toggle: function (fieldset) {
+    var self = this;
+    var $fieldset = $(fieldset);
+    if ($fieldset.is('.collapsed')) {
+      var $content = $('> .fieldset-wrapper', fieldset).hide();
+      $fieldset
+        .removeClass('collapsed')
+        .trigger({ type: 'collapsed', value: false })
+        .find('> legend span.fieldset-legend-prefix').html(Drupal.t('Hide'));
+      $content.slideDown({
+        duration: 'fast',
+        easing: 'linear',
+        complete: function () {
+          self.scrollIntoView(fieldset);
+          self.storageUpdate();
+          fieldset.animating = false;
+        },
+        step: function () {
+          // Scroll the fieldset into view.
+          self.scrollIntoView(fieldset);
+        }
+      });
+    }
+    else {
+      $fieldset.trigger({ type: 'collapsed', value: true });
+      $('> .fieldset-wrapper', fieldset).slideUp('fast', function () {
+        $fieldset
+          .addClass('collapsed')
+          .find('> legend span.fieldset-legend-prefix').html(Drupal.t('Show'));
+        self.storageUpdate();
+        fieldset.animating = false;
+      });
+    }
+  },
+
+  /**
+   * Scroll a given fieldset into view as much as possible.
+   */
+  scrollIntoView: function (node) {
+    var h = document.documentElement.clientHeight || document.body.clientHeight || 0;
+    var offset = document.documentElement.scrollTop || document.body.scrollTop || 0;
+    var posY = $(node).offset().top;
+    var fudge = 55;
+    if (posY + node.offsetHeight + fudge > h + offset) {
+      if (node.offsetHeight > h) {
+        window.scrollTo(0, posY);
+      }
+      else {
+        window.scrollTo(0, posY + node.offsetHeight - h + fudge);
+      }
+    }
+  },
+
+  storageUpdate: function () {
+    if (Drupal.storage.supported) {
+      var uncollapse = [];
+      $('fieldset.collapsible:not(.collapsed)').each(function () {
+        if (this.id) {
+          uncollapse.push(this.id);
+        }
+      });
+      Drupal.storage.save('#' + uncollapse.join(',#'), 'system.collapse.' + Drupal.settings.q);
+    }
+  },
+
 };
 
 })(jQuery);
Index: misc/drupal.js
===================================================================
RCS file: /cvs/drupal/drupal/misc/drupal.js,v
retrieving revision 1.71
diff -u -p -r1.71 drupal.js
--- misc/drupal.js	22 Nov 2010 04:33:02 -0000	1.71
+++ misc/drupal.js	18 Dec 2010 05:38:16 -0000
@@ -1,6 +1,6 @@
 // $Id: drupal.js,v 1.71 2010/11/22 04:33:02 webchick Exp $
 
-var Drupal = Drupal || { 'settings': {}, 'behaviors': {}, 'locale': {} };
+var Drupal = Drupal || { 'settings': {}, 'behaviors': {}, 'locale': {}, 'storage': {} };
 
 // Allow other JavaScript libraries to use $.
 jQuery.noConflict();
@@ -370,4 +370,80 @@ Drupal.theme.prototype = {
   }
 };
 
+/**
+ * Boolean indicating whether the client supports local storage.
+ */
+Drupal.storage.supported = (!!typeof window.localStorage != 'undefined');
+
+/**
+ * Store data on the client-side.
+ *
+ * @param data
+ *   The data to store.
+ * @param key
+ *   The key name to store data under. Automatically prefixed with "Drupal.".
+ *   Should be further namespaced by module; e.g., for
+ *   "Drupal.moduleName.settingName" you pass "moduleName.settingName".
+ * @param bin
+ *   (optional) The space to store in, one of 'session', 'local', 'global'.
+ *   Defaults to 'session'.
+ */
+Drupal.storage.save = function (data, key, bin) {
+  return Drupal.storage._bin(key, bin, function(key, bin) {
+    return bin.setItem(key, data);
+  });
+};
+
+/**
+ * Load data from client-side storage.
+ *
+ * Before attempting to load, calling code is responsible for testing
+ * availability via Drupal.storage.supported().
+ *
+ * @param key
+ *   The key name to load stored data from. Automatically prefixed with
+ *   "Drupal.".
+ * @param bin
+ *   (optional) The storage space to retrieve from. Defaults to 'session'.
+ *
+ * @see Drupal.storage.save()
+ */
+Drupal.storage.load = function (key, bin) {
+  Drupal.storage._bin(key, bin, function(key, bin) {
+    return bin.getItem(key);
+  });
+};
+
+/**
+ * Delete data from client-side storage.
+ *
+ * Note: Abbreviated method name, since 'delete' is a reserved keyword.
+ *
+ * @param key
+ *   The key name to delete. Automatically prefixed with "Drupal.".
+ * @param bin
+ *   (optional) The storage space name. Defaults to 'session'.
+ *
+ * @see Drupal.storage.save()
+ */
+Drupal.storage.del = function (key, bin) {
+  return Drupal.storage._bin(key, bin, function(key, bin) {
+    return bin.removeItem(key);
+  });
+};
+
+/**
+ * Internal function: abstracts out validation of arguments.
+ */
+Drupal.storage._bin(key, bin, callback) {
+  if (typeof bin == 'undefined') {
+    bin = 'session';
+  }
+  if (typeof window[bin + 'Storage'] == 'undefined') {
+    return;
+  }
+  key = 'Drupal.' + key;
+  return callback(key, window[bin + 'Storage']);
+}
+
 })(jQuery);
