diff --git a/admin_devel/admin_devel.js b/admin_devel/admin_devel.js
index 833197a..0336175 100644
--- a/admin_devel/admin_devel.js
+++ b/admin_devel/admin_devel.js
@@ -1,40 +1,44 @@
-(function($) {
+(function ($) {
 
-/**
- * jQuery debugging helper.
- *
- * Invented for Dreditor.
- *
- * @usage
- *   $.debug(var [, name]);
- *   $variable.debug( [name] );
- */
-jQuery.extend({
-  debug: function () {
-    // Setup debug storage in global window. We want to look into it.
-    window.debug = window.debug || [];
+  "use strict";
 
-    args = jQuery.makeArray(arguments);
-    // Determine data source; this is an object for $variable.debug().
-    // Also determine the identifier to store data with.
-    if (typeof this == 'object') {
-      var name = (args.length ? args[0] : window.debug.length);
-      var data = this;
-    }
-    else {
-      var name = (args.length > 1 ? args.pop() : window.debug.length);
-      var data = args[0];
-    }
-    // Store data.
-    window.debug[name] = data;
-    // Dump data into Firebug console.
-    if (typeof console != 'undefined') {
-      console.log(name, data);
+  /**
+   * jQuery debugging helper.
+   *
+   * Invented for Dreditor.
+   *
+   * @usage
+   *   $.debug(var [, name]);
+   *   $variable.debug( [name] );
+   */
+  jQuery.extend({
+    debug: function () {
+      // Setup debug storage in global window. We want to look into it.
+      window.debug = window.debug || [];
+
+      var args = jQuery.makeArray(arguments);
+      var name;
+      var data;
+      // Determine data source; this is an object for $variable.debug().
+      // Also determine the identifier to store data with.
+      if (typeof this == 'object') {
+        name = (args.length ? args[0] : window.debug.length);
+        data = this;
+      }
+      else {
+        name = (args.length > 1 ? args.pop() : window.debug.length);
+        data = args[0];
+      }
+      // Store data.
+      window.debug[name] = data;
+      // Dump data into Firebug console.
+      if (typeof console != 'undefined') {
+        console.log(name, data);
+      }
+      return this;
     }
-    return this;
-  }
-});
-// @todo Is this the right way?
-jQuery.fn.debug = jQuery.debug;
+  });
+  // @todo Is this the right way?
+  jQuery.fn.debug = jQuery.debug;
 
 })(jQuery);
diff --git a/admin_menu.admin.js b/admin_menu.admin.js
index 9ee9f36..28d5fb6 100644
--- a/admin_menu.admin.js
+++ b/admin_menu.admin.js
@@ -1,62 +1,64 @@
-(function($) {
+(function ($) {
 
-/**
- * Live preview of Administration menu components.
- */
-Drupal.behaviors.adminMenuLivePreview = {
-  attach: function (context, settings) {
-    $('input[name^="admin_menu_components"]', context).once('admin-menu-live-preview')
-      .change(function () {
-        var target = $(this).attr('rel');
-        $(target).toggle(this.checked);
-      })
-      .trigger('change');
-  }
-};
+  "use strict";
 
-/**
- * Automatically enables required permissions on demand.
- *
- * Many users do not understand that two permissions are required for the
- * administration menu to appear. Since Drupal core provides no facility for
- * this, we implement a simple manual confirmation for automatically enabling
- * the "other" permission.
- */
-Drupal.behaviors.adminMenuPermissionsSetupHelp = {
-  attach: function (context, settings) {
-    $('#permissions', context).once('admin-menu-permissions-setup', function () {
-      // Retrieve matrix/mapping - these need to use the same indexes for the
-      // same permissions and roles.
-      var $roles = $(this).find('th:not(:first)');
-      var $admin = $(this).find('input[name$="[access administration pages]"]');
-      var $menu = $(this).find('input[name$="[access administration menu]"]');
+  /**
+   * Live preview of Administration menu components.
+   */
+  Drupal.behaviors.adminMenuLivePreview = {
+    attach: function (context, settings) {
+      $('input[name^="admin_menu_components"]', context).once('admin-menu-live-preview')
+        .change(function () {
+          var target = $(this).attr('rel');
+          $(target).toggle(this.checked);
+        })
+        .trigger('change');
+    }
+  };
 
-      // Retrieve the permission label - without description.
-      var adminPermission = $.trim($admin.eq(0).parents('td').prev().children().get(0).firstChild.textContent);
-      var menuPermission = $.trim($menu.eq(0).parents('td').prev().children().get(0).firstChild.textContent);
+  /**
+   * Automatically enables required permissions on demand.
+   *
+   * Many users do not understand that two permissions are required for the
+   * administration menu to appear. Since Drupal core provides no facility for
+   * this, we implement a simple manual confirmation for automatically enabling
+   * the "other" permission.
+   */
+  Drupal.behaviors.adminMenuPermissionsSetupHelp = {
+    attach: function (context, settings) {
+      $('#permissions', context).once('admin-menu-permissions-setup', function () {
+        // Retrieve matrix/mapping - these need to use the same indexes for the
+        // same permissions and roles.
+        var $roles = $(this).find('th:not(:first)');
+        var $admin = $(this).find('input[name$="[access administration pages]"]');
+        var $menu = $(this).find('input[name$="[access administration menu]"]');
 
-      $admin.each(function (index) {
-        // Only proceed if both are not enabled already.
-        if (!(this.checked && $menu[index].checked)) {
-          // Stack both checkboxes and attach a click event handler to both.
-          $(this).add($menu[index]).click(function () {
-            // Do nothing when disabling a permission.
-            if (this.checked) {
-              // Figure out which is the other, check whether it still disabled,
-              // and if so, ask whether to auto-enable it.
-              var other = (this == $admin[index] ? $menu[index] : $admin[index]);
-              if (!other.checked && confirm(Drupal.t('Also allow !name role to !permission?', {
-                '!name': $roles[index].textContent,
-                '!permission': (this == $admin[index] ? menuPermission : adminPermission)
-              }))) {
-                other.checked = 'checked';
+        // Retrieve the permission label - without description.
+        var adminPermission = $.trim($admin.eq(0).parents('td').prev().children().get(0).firstChild.textContent);
+        var menuPermission = $.trim($menu.eq(0).parents('td').prev().children().get(0).firstChild.textContent);
+
+        $admin.each(function (index) {
+          // Only proceed if both are not enabled already.
+          if (!(this.checked && $menu[index].checked)) {
+            // Stack both checkboxes and attach a click event handler to both.
+            $(this).add($menu[index]).click(function () {
+              // Do nothing when disabling a permission.
+              if (this.checked) {
+                // Figure out which is the other, check whether it still disabled,
+                // and if so, ask whether to auto-enable it.
+                var other = (this === $admin[index] ? $menu[index] : $admin[index]);
+                if (!other.checked && confirm(Drupal.t('Also allow !name role to !permission?', {
+                  '!name': $roles[index].textContent,
+                  '!permission': (this === $admin[index] ? menuPermission : adminPermission)
+                }))) {
+                  other.checked = 'checked';
+                }
               }
-            }
-          });
-        }
+            });
+          }
+        });
       });
-    });
-  }
-};
+    }
+  };
 
 })(jQuery);
diff --git a/admin_menu.js b/admin_menu.js
index 06d0565..725c677 100644
--- a/admin_menu.js
+++ b/admin_menu.js
@@ -1,39 +1,41 @@
-(function($, Drupal) {
+(function ($, Drupal) {
 
-Drupal.admin = Drupal.admin || {};
-Drupal.admin.behaviors = Drupal.admin.behaviors || {};
-Drupal.admin.hashes = Drupal.admin.hashes || {};
+  "use strict";
 
-/**
- * Core behavior for Administration menu.
- *
- * Test whether there is an administration menu is in the output and execute all
- * registered behaviors.
- */
-Drupal.behaviors.adminMenu = {
-  attach: function (context, settings) {
-    // Initialize settings.
-    settings.admin_menu = $.extend({
-      suppress: false,
-      margin_top: false,
-      position_fixed: false,
-      tweak_modules: false,
-      tweak_permissions: false,
-      tweak_tabs: false,
-      destination: '',
-      basePath: settings.basePath,
-      hash: 0,
-      replacements: {}
-    }, settings.admin_menu || {});
-    // Check whether administration menu should be suppressed.
-    if (settings.admin_menu.suppress) {
-      return;
-    }
-    var $adminMenu = $('#admin-menu:not(.admin-menu-processed)', context);
-    // Client-side caching; if administration menu is not in the output, it is
-    // fetched from the server and cached in the browser.
-    if (!$adminMenu.length && settings.admin_menu.hash) {
-      Drupal.admin.getCache(settings.admin_menu.hash, settings, function (response) {
+  Drupal.admin = Drupal.admin || {};
+  Drupal.admin.behaviors = Drupal.admin.behaviors || {};
+  Drupal.admin.hashes = Drupal.admin.hashes || {};
+
+  /**
+   * Core behavior for Administration menu.
+   *
+   * Test whether there is an administration menu is in the output and execute all
+   * registered behaviors.
+   */
+  Drupal.behaviors.adminMenu = {
+    attach: function (context, settings) {
+      // Initialize settings.
+      settings.admin_menu = $.extend({
+        suppress: false,
+        margin_top: false,
+        position_fixed: false,
+        tweak_modules: false,
+        tweak_permissions: false,
+        tweak_tabs: false,
+        destination: '',
+        basePath: settings.basePath,
+        hash: 0,
+        replacements: {}
+      }, settings.admin_menu || {});
+      // Check whether administration menu should be suppressed.
+      if (settings.admin_menu.suppress) {
+        return;
+      }
+      var $adminMenu = $('#admin-menu:not(.admin-menu-processed)', context);
+      // Client-side caching; if administration menu is not in the output, it is
+      // fetched from the server and cached in the browser.
+      if (!$adminMenu.length && settings.admin_menu.hash) {
+        Drupal.admin.getCache(settings.admin_menu.hash, settings, function (response) {
           if (typeof response == 'string' && response.length > 0) {
             $('body', context).append(response);
           }
@@ -42,353 +44,357 @@ Drupal.behaviors.adminMenu = {
           Drupal.admin.attachBehaviors(context, settings, $adminMenu);
           // Allow resize event handlers to recalculate sizes/positions.
           $(window).triggerHandler('resize');
-      });
-    }
-    // If the menu is in the output already, this means there is a new version.
-    else {
-      // Apply our behaviors.
-      Drupal.admin.attachBehaviors(context, settings, $adminMenu);
+        });
+      }
+      // If the menu is in the output already, this means there is a new version.
+      else {
+        // Apply our behaviors.
+        Drupal.admin.attachBehaviors(context, settings, $adminMenu);
+      }
     }
-  }
-};
+  };
 
-/**
- * Collapse fieldsets on Modules page.
- */
-Drupal.behaviors.adminMenuCollapseModules = {
-  attach: function (context, settings) {
-    if (settings.admin_menu.tweak_modules) {
-      $('#system-modules fieldset:not(.collapsed)', context).addClass('collapsed');
+  /**
+   * Collapse fieldsets on Modules page.
+   */
+  Drupal.behaviors.adminMenuCollapseModules = {
+    attach: function (context, settings) {
+      if (settings.admin_menu.tweak_modules) {
+        $('#system-modules fieldset:not(.collapsed)', context).addClass('collapsed');
+      }
     }
-  }
-};
+  };
 
-/**
- * Collapse modules on Permissions page.
- */
-Drupal.behaviors.adminMenuCollapsePermissions = {
-  attach: function (context, settings) {
-    if (settings.admin_menu.tweak_permissions) {
-      // Freeze width of first column to prevent jumping.
-      $('#permissions th:first', context).css({ width: $('#permissions th:first', context).width() });
-      // Attach click handler.
-      $modules = $('#permissions tr:has(td.module)', context).once('admin-menu-tweak-permissions', function () {
-        var $module = $(this);
-        $module.bind('click.admin-menu', function () {
-          // @todo Replace with .nextUntil() in jQuery 1.4.
-          $module.nextAll().each(function () {
-            var $row = $(this);
-            if ($row.is(':has(td.module)')) {
-              return false;
-            }
-            $row.toggleClass('element-hidden');
+  /**
+   * Collapse modules on Permissions page.
+   */
+  Drupal.behaviors.adminMenuCollapsePermissions = {
+    attach: function (context, settings) {
+      if (settings.admin_menu.tweak_permissions) {
+        // Freeze width of first column to prevent jumping.
+        $('#permissions th:first', context).css({width: $('#permissions th:first', context).width()});
+        // Attach click handler.
+        var $modules = $('#permissions tr:has(td.module)', context).once('admin-menu-tweak-permissions', function () {
+          var $module = $(this);
+          $module.bind('click.admin-menu', function () {
+            // @todo Replace with .nextUntil() in jQuery 1.4.
+            $module.nextAll().each(function () {
+              var $row = $(this);
+              if ($row.is(':has(td.module)')) {
+                return false;
+              }
+              $row.toggleClass('element-hidden');
+            });
           });
         });
-      });
-      // Collapse all but the targeted permission rows set.
-      if (window.location.hash.length) {
-        $modules = $modules.not(':has(' + window.location.hash + ')');
+        // Collapse all but the targeted permission rows set.
+        if (window.location.hash.length) {
+          $modules = $modules.not(':has(' + window.location.hash + ')');
+        }
+        $modules.trigger('click.admin-menu');
       }
-      $modules.trigger('click.admin-menu');
     }
-  }
-};
+  };
 
-/**
- * Apply margin to page.
- *
- * Note that directly applying marginTop does not work in IE. To prevent
- * flickering/jumping page content with client-side caching, this is a regular
- * Drupal behavior.
- */
-Drupal.behaviors.adminMenuMarginTop = {
-  attach: function (context, settings) {
-    if (!settings.admin_menu.suppress && settings.admin_menu.margin_top) {
-      $('body:not(.admin-menu)', context).addClass('admin-menu');
+  /**
+   * Apply margin to page.
+   *
+   * Note that directly applying marginTop does not work in IE. To prevent
+   * flickering/jumping page content with client-side caching, this is a regular
+   * Drupal behavior.
+   */
+  Drupal.behaviors.adminMenuMarginTop = {
+    attach: function (context, settings) {
+      if (!settings.admin_menu.suppress && settings.admin_menu.margin_top) {
+        $('body:not(.admin-menu)', context).addClass('admin-menu');
+      }
     }
-  }
-};
+  };
 
-/**
- * Retrieve content from client-side cache.
- *
- * @param hash
- *   The md5 hash of the content to retrieve.
- * @param onSuccess
- *   A callback function invoked when the cache request was successful.
- */
-Drupal.admin.getCache = function (hash, settings, onSuccess) {
-  if (Drupal.admin.hashes.hash !== undefined) {
-    return Drupal.admin.hashes.hash;
-  }
-  $.ajax({
-    cache: true,
-    type: 'GET',
-    dataType: 'text', // Prevent auto-evaluation of response.
-    // @todo Drupal's new HttpKernel treats any XMLHttpRequest as Ajax request,
-    //   which breaks simple HTML responses. Even without the offending override
-    //   in ContentNegotiation::getContentType(), the HttpKernel fails to fall
-    //   back to text/html and returns a unknown media type HTTP error, since
-    //   jQuery sends a Accept:text/plain header.
-    accepts: {
-      html: 'text/html',
-      text: 'text/html'
-    },
-    global: false, // Do not trigger global AJAX events.
-    url: settings.admin_menu.basePath.replace(/admin_menu/, 'js/admin_menu/cache/' + hash),
-    success: onSuccess,
-    complete: function (XMLHttpRequest, status) {
-      Drupal.admin.hashes.hash = status;
+  /**
+   * Retrieve content from client-side cache.
+   *
+   * @param hash
+   *   The md5 hash of the content to retrieve.
+   * @param onSuccess
+   *   A callback function invoked when the cache request was successful.
+   */
+  Drupal.admin.getCache = function (hash, settings, onSuccess) {
+    if (Drupal.admin.hashes.hash !== "undefined") {
+      return Drupal.admin.hashes.hash;
     }
-  });
-};
-
-/**
- * TableHeader callback to determine top viewport offset.
- *
- * @see toolbar.js
- */
-Drupal.admin.height = function() {
-  var $adminMenu = $('#admin-menu');
-  var height = $adminMenu.outerHeight();
-  // In IE, Shadow filter adds some extra height, so we need to remove it from
-  // the returned height.
-  if ($adminMenu.css('filter') && $adminMenu.css('filter').match(/DXImageTransform\.Microsoft\.Shadow/)) {
-    height -= $adminMenu.get(0).filters.item("DXImageTransform.Microsoft.Shadow").strength;
-  }
-  return height;
-};
-
-/**
- * @defgroup admin_behaviors Administration behaviors.
- * @{
- */
-
-/**
- * Attach administrative behaviors.
- */
-Drupal.admin.attachBehaviors = function (context, settings, $adminMenu) {
-  if ($adminMenu.length) {
-    $adminMenu.addClass('admin-menu-processed');
-    $.each(Drupal.admin.behaviors, function() {
-      this(context, settings, $adminMenu);
+    $.ajax({
+      cache: true,
+      type: 'GET',
+      dataType: 'text', // Prevent auto-evaluation of response.
+      // @todo Drupal's new HttpKernel treats any XMLHttpRequest as Ajax request,
+      //   which breaks simple HTML responses. Even without the offending override
+      //   in ContentNegotiation::getContentType(), the HttpKernel fails to fall
+      //   back to text/html and returns a unknown media type HTTP error, since
+      //   jQuery sends a Accept:text/plain header.
+      accepts: {
+        html: 'text/html',
+        text: 'text/html'
+      },
+      global: false, // Do not trigger global AJAX events.
+      url: settings.admin_menu.basePath.replace(/admin_menu/, 'js/admin_menu/cache/' + hash),
+      success: onSuccess,
+      complete: function (XMLHttpRequest, status) {
+        Drupal.admin.hashes.hash = status;
+      }
     });
-  }
-};
-
-/**
- * Apply 'position: fixed'.
- */
-Drupal.admin.behaviors.positionFixed = function (context, settings, $adminMenu) {
-  if (settings.admin_menu.position_fixed) {
-    $adminMenu.addClass('admin-menu-position-fixed');
-    $adminMenu.css('position', 'fixed');
-  }
-};
+  };
 
-/**
- * Move page tabs into administration menu.
- */
-Drupal.admin.behaviors.pageTabs = function (context, settings, $adminMenu) {
-  if (settings.admin_menu.tweak_tabs) {
-    var $tabs = $(context).find('ul.tabs.primary');
-    $adminMenu.find('#admin-menu-wrapper > ul').eq(1)
-      .append($tabs.find('li').addClass('admin-menu-tab'));
-    $(context).find('ul.tabs.secondary')
-      .appendTo('#admin-menu-wrapper > ul > li.admin-menu-tab.active')
-      .removeClass('secondary');
-    $tabs.remove();
-  }
-};
+  /**
+   * TableHeader callback to determine top viewport offset.
+   *
+   * @see toolbar.js
+   */
+  Drupal.admin.height = function () {
+    var $adminMenu = $('#admin-menu');
+    var height = $adminMenu.outerHeight();
+    // In IE, Shadow filter adds some extra height, so we need to remove it from
+    // the returned height.
+    if ($adminMenu.css('filter') && $adminMenu.css('filter').match(/DXImageTransform\.Microsoft\.Shadow/)) {
+      height -= $adminMenu.get(0).filters.item("DXImageTransform.Microsoft.Shadow").strength;
+    }
+    return height;
+  };
 
-/**
- * Perform dynamic replacements in cached menu.
- */
-Drupal.admin.behaviors.replacements = function (context, settings, $adminMenu) {
-  for (var item in settings.admin_menu.replacements) {
-    $(item, $adminMenu).html(settings.admin_menu.replacements[item]);
-  }
-};
+  /**
+   * @defgroup admin_behaviors Administration behaviors.
+   * @{
+   */
 
-/**
- * Inject destination query strings for current page.
- */
-Drupal.admin.behaviors.destination = function (context, settings, $adminMenu) {
-  if (settings.admin_menu.destination) {
-    $('a.admin-menu-destination', $adminMenu).each(function() {
-      this.search += (!this.search.length ? '?' : '&') + settings.admin_menu.destination;
-    });
-  }
-};
+  /**
+   * Attach administrative behaviors.
+   */
+  Drupal.admin.attachBehaviors = function (context, settings, $adminMenu) {
+    if ($adminMenu.length) {
+      $adminMenu.addClass('admin-menu-processed');
+      $.each(Drupal.admin.behaviors, function () {
+        this(context, settings, $adminMenu);
+      });
+    }
+  };
 
-/**
- * Apply JavaScript-based hovering behaviors.
- *
- * @todo This has to run last.  If another script registers additional behaviors
- *   it will not run last.
- */
-Drupal.admin.behaviors.hover = function (context, settings, $adminMenu) {
-  // Delayed mouseout.
-  $('li.expandable', $adminMenu).hover(
-    function () {
-      // Stop the timer.
-      clearTimeout(this.sfTimer);
-      // Display child lists.
-      $('> ul', this)
-        .css({left: 'auto', display: 'block'})
-        // Immediately hide nephew lists.
-        .parent().siblings('li').children('ul').css({left: '-999em', display: 'none'});
-    },
-    function () {
-      // Start the timer.
-      var uls = $('> ul', this);
-      this.sfTimer = setTimeout(function () {
-        uls.css({left: '-999em', display: 'none'});
-      }, 400);
+  /**
+   * Apply 'position: fixed'.
+   */
+  Drupal.admin.behaviors.positionFixed = function (context, settings, $adminMenu) {
+    if (settings.admin_menu.position_fixed) {
+      $adminMenu.addClass('admin-menu-position-fixed');
+      $adminMenu.css('position', 'fixed');
     }
-  );
-};
+  };
 
-/**
- * Apply the search bar functionality.
- */
-Drupal.admin.behaviors.search = function (context, settings, $adminMenu) {
-  // @todo Add a HTML ID.
-  var $input = $('input.admin-menu-search', $adminMenu);
-  // Initialize the current search needle.
-  var needle = $input.val();
-  // Cache of all links that can be matched in the menu.
-  var links;
-  // Minimum search needle length.
-  var needleMinLength = 2;
-  // Append the results container.
-  var $results = $('<div />').insertAfter($input);
+  /**
+   * Move page tabs into administration menu.
+   */
+  Drupal.admin.behaviors.pageTabs = function (context, settings, $adminMenu) {
+    if (settings.admin_menu.tweak_tabs) {
+      var $tabs = $(context).find('ul.tabs.primary');
+      $adminMenu.find('#admin-menu-wrapper > ul').eq(1)
+        .append($tabs.find('li').addClass('admin-menu-tab'));
+      $(context).find('ul.tabs.secondary')
+        .appendTo('#admin-menu-wrapper > ul > li.admin-menu-tab.active')
+        .removeClass('secondary');
+      $tabs.remove();
+    }
+  };
 
   /**
-   * Executes the search upon user input.
+   * Perform dynamic replacements in cached menu.
    */
-  function keyupHandler() {
-    var matches, $html, value = $(this).val();
-    // Only proceed if the search needle has changed.
-    if (value !== needle) {
-      needle = value;
-      // Initialize the cache of menu links upon first search.
-      if (!links && needle.length >= needleMinLength) {
-        // @todo Limit to links in dropdown menus; i.e., skip menu additions.
-        links = buildSearchIndex($adminMenu.find('li:not(.admin-menu-action, .admin-menu-action li) > a'));
-      }
-      // Empty results container when deleting search text.
-      if (needle.length < needleMinLength) {
-        $results.empty();
-      }
-      // Only search if the needle is long enough.
-      if (needle.length >= needleMinLength && links) {
-        matches = findMatches(needle, links);
-        // Build the list in a detached DOM node.
-        $html = buildResultsList(matches);
-        // Display results.
-        $results.empty().append($html);
+  Drupal.admin.behaviors.replacements = function (context, settings, $adminMenu) {
+    for (var item in settings.admin_menu.replacements) {
+      if (settings.admin_menu.replacements[item]) {
+        $(item, $adminMenu).html(settings.admin_menu.replacements[item]);
       }
     }
-  }
+  };
 
   /**
-   * Builds the search index.
+   * Inject destination query strings for current page.
    */
-  function buildSearchIndex($links) {
-    return $links
-      .map(function () {
-        var text = (this.textContent || this.innerText);
-        // Skip menu entries that do not contain any text (e.g., the icon).
-        if (typeof text === 'undefined') {
-          return;
-        }
-        return {
-          text: text,
-          textMatch: text.toLowerCase(),
-          element: this
-        };
+  Drupal.admin.behaviors.destination = function (context, settings, $adminMenu) {
+    if (settings.admin_menu.destination) {
+      $('a.admin-menu-destination', $adminMenu).each(function () {
+        this.search += (!this.search.length ? '?' : '&') + settings.admin_menu.destination;
       });
-  }
+    }
+  };
 
   /**
-   * Searches the index for a given needle and returns matching entries.
+   * Apply JavaScript-based hovering behaviors.
+   *
+   * @todo This has to run last.  If another script registers additional behaviors
+   *   it will not run last.
    */
-  function findMatches(needle, links) {
-    var needleMatch = needle.toLowerCase();
-    // Select matching links from the cache.
-    return $.grep(links, function (link) {
-      return link.textMatch.indexOf(needleMatch) !== -1;
-    });
-  }
+  Drupal.admin.behaviors.hover = function (context, settings, $adminMenu) {
+    // Delayed mouseout.
+    $('li.expandable', $adminMenu).hover(
+      function () {
+        // Stop the timer.
+        clearTimeout(this.sfTimer);
+        // Display child lists.
+        $('> ul', this)
+          .css({left: 'auto', display: 'block'})
+          // Immediately hide nephew lists.
+          .parent().siblings('li').children('ul').css({left: '-999em', display: 'none'});
+      },
+      function () {
+        // Start the timer.
+        var uls = $('> ul', this);
+        this.sfTimer = setTimeout(function () {
+          uls.css({left: '-999em', display: 'none'});
+        }, 400);
+      }
+    );
+  };
 
   /**
-   * Builds the search result list in a detached DOM node.
+   * Apply the search bar functionality.
    */
-  function buildResultsList(matches) {
-    var $html = $('<ul class="dropdown admin-menu-search-results" />');
-    $.each(matches, function () {
-      var result = this.text;
-      var $element = $(this.element);
+  Drupal.admin.behaviors.search = function (context, settings, $adminMenu) {
+    // @todo Add a HTML ID.
+    var $input = $('input.admin-menu-search', $adminMenu);
+    // Initialize the current search needle.
+    var needle = $input.val();
+    // Cache of all links that can be matched in the menu.
+    var links;
+    // Minimum search needle length.
+    var needleMinLength = 2;
+    // Append the results container.
+    var $results = $('<div />').insertAfter($input);
 
-      // Check whether there is a top-level category that can be prepended.
-      var $category = $element.closest('#admin-menu-wrapper > ul > li');
-      var categoryText = $category.find('> a').text()
-      if ($category.length && categoryText) {
-        result = categoryText + ': ' + result;
+    /**
+     * Executes the search upon user input.
+     */
+    function keyupHandler() {
+      var matches;
+      var $html;
+      var value = $(this).val();
+      // Only proceed if the search needle has changed.
+      if (value !== needle) {
+        needle = value;
+        // Initialize the cache of menu links upon first search.
+        if (!links && needle.length >= needleMinLength) {
+          // @todo Limit to links in dropdown menus; i.e., skip menu additions.
+          links = buildSearchIndex($adminMenu.find('li:not(.admin-menu-action, .admin-menu-action li) > a'));
+        }
+        // Empty results container when deleting search text.
+        if (needle.length < needleMinLength) {
+          $results.empty();
+        }
+        // Only search if the needle is long enough.
+        if (needle.length >= needleMinLength && links) {
+          matches = findMatches(needle, links);
+          // Build the list in a detached DOM node.
+          $html = buildResultsList(matches);
+          // Display results.
+          $results.empty().append($html);
+        }
       }
+    }
 
-      var $result = $('<li><a href="' + $element.attr('href') + '">' + result + '</a></li>');
-      $result.data('original-link', $(this.element).parent());
-      $html.append($result);
-    });
-    return $html;
-  }
+    /**
+     * Builds the search index.
+     */
+    function buildSearchIndex($links) {
+      return $links
+        .map(function () {
+          var text = (this.textContent || this.innerText);
+          // Skip menu entries that do not contain any text (e.g., the icon).
+          if (typeof text === 'undefined') {
+            return;
+          }
+          return {
+            text: text,
+            textMatch: text.toLowerCase(),
+            element: this
+          };
+        });
+    }
 
-  /**
-   * Highlights selected result.
-   */
-  function resultsHandler(e) {
-    var $this = $(this);
-    var show = e.type === 'mouseenter' || e.type === 'focusin';
-    $this.trigger(show ? 'showPath' : 'hidePath', [this]);
-  }
+    /**
+     * Searches the index for a given needle and returns matching entries.
+     */
+    function findMatches(needle, links) {
+      var needleMatch = needle.toLowerCase();
+      // Select matching links from the cache.
+      return $.grep(links, function (link) {
+        return link.textMatch.indexOf(needleMatch) !== -1;
+      });
+    }
 
-  /**
-   * Closes the search results and clears the search input.
-   */
-  function resultsClickHandler(e, link) {
-    var $original = $(this).data('original-link');
-    $original.trigger('mouseleave');
-    $input.val('').trigger('keyup');
-  }
+    /**
+     * Builds the search result list in a detached DOM node.
+     */
+    function buildResultsList(matches) {
+      var $html = $('<ul class="dropdown admin-menu-search-results" />');
+      $.each(matches, function () {
+        var result = this.text;
+        var $element = $(this.element);
 
-  /**
-   * Shows the link in the menu that corresponds to a search result.
-   */
-  function highlightPathHandler(e, link) {
-    if (link) {
-      var $original = $(link).data('original-link');
-      var show = e.type === 'showPath';
-      // Toggle an additional CSS class to visually highlight the matching link.
-      // @todo Consider using same visual appearance as regular hover.
-      $original.toggleClass('highlight', show);
-      $original.trigger(show ? 'mouseenter' : 'mouseleave');
+        // Check whether there is a top-level category that can be prepended.
+        var $category = $element.closest('#admin-menu-wrapper > ul > li');
+        var categoryText = $category.find('> a').text();
+        if ($category.length && categoryText) {
+          result = categoryText + ': ' + result;
+        }
+
+        var $result = $('<li><a href="' + $element.attr('href') + '">' + result + '</a></li>');
+        $result.data('original-link', $(this.element).parent());
+        $html.append($result);
+      });
+      return $html;
     }
-  }
 
-  // Attach showPath/hidePath handler to search result entries.
-  $results.delegate('li', 'mouseenter mouseleave focus blur', resultsHandler);
-  // Hide the result list after a link has been clicked, useful for overlay.
-  $results.delegate('li', 'click', resultsClickHandler);
-  // Attach hover/active highlight behavior to search result entries.
-  $adminMenu.delegate('.admin-menu-search-results li', 'showPath hidePath', highlightPathHandler);
-  // Attach the search input event handler.
-  $input.bind('keyup search', keyupHandler);
-};
+    /**
+     * Highlights selected result.
+     */
+    function resultsHandler(e) {
+      var $this = $(this);
+      var show = e.type === 'mouseenter' || e.type === 'focusin';
+      $this.trigger(show ? 'showPath' : 'hidePath', [this]);
+    }
+
+    /**
+     * Closes the search results and clears the search input.
+     */
+    function resultsClickHandler(e, link) {
+      var $original = $(this).data('original-link');
+      $original.trigger('mouseleave');
+      $input.val('').trigger('keyup');
+    }
 
-/**
- * @} End of "defgroup admin_behaviors".
- */
+    /**
+     * Shows the link in the menu that corresponds to a search result.
+     */
+    function highlightPathHandler(e, link) {
+      if (link) {
+        var $original = $(link).data('original-link');
+        var show = e.type === 'showPath';
+        // Toggle an additional CSS class to visually highlight the matching link.
+        // @todo Consider using same visual appearance as regular hover.
+        $original.toggleClass('highlight', show);
+        $original.trigger(show ? 'mouseenter' : 'mouseleave');
+      }
+    }
+
+    // Attach showPath/hidePath handler to search result entries.
+    $results.delegate('li', 'mouseenter mouseleave focus blur', resultsHandler);
+    // Hide the result list after a link has been clicked, useful for overlay.
+    $results.delegate('li', 'click', resultsClickHandler);
+    // Attach hover/active highlight behavior to search result entries.
+    $adminMenu.delegate('.admin-menu-search-results li', 'showPath hidePath', highlightPathHandler);
+    // Attach the search input event handler.
+    $input.bind('keyup search', keyupHandler);
+  };
+
+  /**
+   * @} End of "defgroup admin_behaviors".
+   */
 
 })(jQuery, Drupal);
diff --git a/admin_menu_toolbar/admin_menu_toolbar.js b/admin_menu_toolbar/admin_menu_toolbar.js
index 809d9ea..afa6433 100644
--- a/admin_menu_toolbar/admin_menu_toolbar.js
+++ b/admin_menu_toolbar/admin_menu_toolbar.js
@@ -1,56 +1,63 @@
-(function($) {
-
-Drupal.admin = Drupal.admin || {};
-Drupal.admin.behaviors = Drupal.admin.behaviors || {};
-
-/**
- * @ingroup admin_behaviors
- * @{
- */
-
-/**
- * Apply active trail highlighting based on current path.
- *
- * @todo Not limited to toolbar; move into core?
- */
-Drupal.admin.behaviors.toolbarActiveTrail = function (context, settings, $adminMenu) {
-  if (settings.admin_menu.toolbar && settings.admin_menu.toolbar.activeTrail) {
-    $adminMenu.find('> div > ul > li > a[href="' + settings.admin_menu.toolbar.activeTrail + '"]').addClass('active-trail');
-  }
-};
-
-/**
- * Toggles the shortcuts bar.
- */
-Drupal.admin.behaviors.shortcutToggle = function (context, settings, $adminMenu) {
-  var $shortcuts = $adminMenu.find('.shortcut-toolbar');
-  if (!$shortcuts.length) {
-    return;
-  }
-  var storage = window.localStorage || false;
-  var storageKey = 'Drupal.admin_menu.shortcut';
-  var $body = $(context).find('body');
-  var $toggle = $adminMenu.find('.shortcut-toggle');
-  $toggle.click(function () {
-    var enable = !$shortcuts.hasClass('active');
-    $shortcuts.toggleClass('active', enable);
-    $toggle.toggleClass('active', enable);
-    if (settings.admin_menu.margin_top) {
-      $body.toggleClass('admin-menu-with-shortcuts', enable);
+(function ($) {
+
+  "use strict";
+
+  Drupal.admin = Drupal.admin || {};
+  Drupal.admin.behaviors = Drupal.admin.behaviors || {};
+
+  /**
+   * @ingroup admin_behaviors
+   * @{
+   */
+
+  /**
+   * Apply active trail highlighting based on current path.
+   *
+   * @todo Not limited to toolbar; move into core?
+   */
+  Drupal.admin.behaviors.toolbarActiveTrail = function (context, settings, $adminMenu) {
+    if (settings.admin_menu.toolbar && settings.admin_menu.toolbar.activeTrail) {
+      $adminMenu.find('> div > ul > li > a[href="' + settings.admin_menu.toolbar.activeTrail + '"]').addClass('active-trail');
     }
-    // Persist toggle state across requests.
-    storage && enable ? storage.setItem(storageKey, 1) : storage.removeItem(storageKey);
-    this.blur();
-    return false;
-  });
-
-  if (!storage || storage.getItem(storageKey)) {
-    $toggle.trigger('click');
-  }
-};
-
-/**
- * @} End of "ingroup admin_behaviors".
- */
+  };
+
+  /**
+   * Toggles the shortcuts bar.
+   */
+  Drupal.admin.behaviors.shortcutToggle = function (context, settings, $adminMenu) {
+    var $shortcuts = $adminMenu.find('.shortcut-toolbar');
+    if (!$shortcuts.length) {
+      return;
+    }
+    var storage = window.localStorage || false;
+    var storageKey = 'Drupal.admin_menu.shortcut';
+    var $body = $(context).find('body');
+    var $toggle = $adminMenu.find('.shortcut-toggle');
+    $toggle.click(function () {
+      var enable = !$shortcuts.hasClass('active');
+      $shortcuts.toggleClass('active', enable);
+      $toggle.toggleClass('active', enable);
+      if (settings.admin_menu.margin_top) {
+        $body.toggleClass('admin-menu-with-shortcuts', enable);
+      }
+      // Persist toggle state across requests.
+      if (storage && enable) {
+        storage.setItem(storageKey, 1);
+      }
+      else {
+        storage.removeItem(storageKey);
+      }
+      this.blur();
+      return false;
+    });
+
+    if (!storage || storage.getItem(storageKey)) {
+      $toggle.trigger('click');
+    }
+  };
+
+  /**
+   * @} End of "ingroup admin_behaviors".
+   */
 
 })(jQuery);
