Index: modules/help/help.css
===================================================================
RCS file: /cvs/drupal/drupal/modules/help/help.css,v
retrieving revision 1.1
diff -u -p -r1.1 help.css
--- modules/help/help.css	14 Aug 2006 07:14:49 -0000	1.1
+++ modules/help/help.css	10 Dec 2006 09:18:12 -0000
@@ -8,3 +8,22 @@
 .help-items-last {
   padding-right: 0;
 }
+
+#help_guide_form {
+  position: absolute;
+  top: 5em;
+  right: 1em;
+  z-index: 10001;
+}
+
+#help_guide_form * {
+  display: inline;
+}
+
+#autocomplete.guide-popup {
+  z-index: 10002;
+}
+#autocomplete.guide-popup .path {
+  color: #777;
+  font-size: 0.9em;
+}
Index: modules/help/help.info
===================================================================
RCS file: /cvs/drupal/drupal/modules/help/help.info,v
retrieving revision 1.3
diff -u -p -r1.3 help.info
--- modules/help/help.info	21 Nov 2006 20:55:34 -0000	1.3
+++ modules/help/help.info	10 Dec 2006 09:18:12 -0000
@@ -3,3 +3,4 @@ name = Help
 description = Manages the display of online help.
 package = Core - optional
 version = VERSION
+dependencies = search
Index: modules/help/help.js
===================================================================
RCS file: modules/help/help.js
diff -N modules/help/help.js
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/help/help.js	10 Dec 2006 09:18:12 -0000
@@ -0,0 +1,269 @@
+// $Id$
+
+/**
+ * Attaches the guide behaviour to all required fields
+ */
+Drupal.guideAutoAttach = function () {
+  var gdb = new Drupal.ACDB(Drupal.settings.guide.url);
+  $('input.guide').each(function () {
+    var uri = this.value;
+    var input = $(this)
+      .attr('autocomplete', 'OFF')[0];
+    $(input.form).submit(Drupal.guideSubmit);
+    new Drupal.guide(input, gdb);
+  });
+
+  // Tweak IE settings so that $('body').css('height') works
+  if (jQuery.browser.msie) {
+    $('html').add('body').css('height', '100%').css('left', '0');
+  }
+}
+
+/**
+ * Prevents the form from submitting if the suggestions popup is open
+ * and closes the suggestions popup when doing so.
+ */
+Drupal.guideSubmit = function () {
+  $('#autocomplete').each(function () {
+    this.owner.hidePopup();
+  });
+  return false;
+}
+
+/**
+ * A guide object
+ */
+Drupal.guide = function (input, db) {
+  var guide = this;
+  this.input = input;
+  this.db = db;
+
+  $(this.input)
+    .keydown(function (event) { return guide.onkeydown(this, event); })
+    .keyup(function (event) { guide.onkeyup(this, event) })
+    .blur(function () { guide.hidePopup(); guide.db.cancel(); });
+
+};
+
+/**
+ * Inherited methods from autocomplete.
+ */
+Drupal.guide.prototype.onkeydown = Drupal.jsAC.prototype.onkeydown;
+Drupal.guide.prototype.onkeyup = Drupal.jsAC.prototype.onkeyup;
+Drupal.guide.prototype.selectDown = Drupal.jsAC.prototype.selectDown;
+Drupal.guide.prototype.selectUp = Drupal.jsAC.prototype.selectUp;
+Drupal.guide.prototype.highlight = Drupal.jsAC.prototype.highlight;
+Drupal.guide.prototype.unhighlight = Drupal.jsAC.prototype.unhighlight;
+Drupal.guide.prototype.setStatus = Drupal.jsAC.prototype.setStatus;
+
+/**
+ * Puts the currently highlighted suggestion into the guide field and
+ * visits the target URL.
+ */
+Drupal.guide.prototype.select = function (node) {
+  this.input.value = node.guideValue;
+  location.href = node.guideUrl;
+}
+
+/**
+ * Hides the guide suggestions
+ */
+Drupal.guide.prototype.hidePopup = function (keycode) {
+  // Hide popup
+  var popup = this.popup;
+  if (popup) {
+    this.popup = null;
+    $(popup).fadeOut('fast', function() { $(popup).remove(); });
+  }
+
+  // Unexpose all items
+  this.unexpose();
+
+  // Select item if the right key or mousebutton was pressed
+  if (this.selected && ((keycode && keycode != 46 && keycode != 8 && keycode != 27) || !keycode)) {
+    this.select(this.selected);
+  }
+
+  // Remove selection
+  this.selected = false;
+}
+
+/**
+ * Positions the suggestions popup and starts a search
+ */
+Drupal.guide.prototype.populatePopup = function () {
+  // Show popup
+  if (this.popup) {
+    $(this.popup).remove();
+  }
+  var pos = Drupal.absolutePosition(this.input);
+  this.selected = false;
+  this.popup = document.createElement('div');
+  this.popup.id = 'autocomplete';
+  this.popup.className = 'guide-popup';
+  this.popup.owner = this;
+  $(this.popup).css({
+    top: (pos.y + this.input.offsetHeight) +'px',
+    left: pos.x +'px',
+    width: (this.input.offsetWidth - 4) +'px',
+    display: 'none'
+  });
+  $('body').append(this.popup);
+
+  // Do search
+  this.db.owner = this;
+  this.db.search(this.input.value);
+}
+
+/**
+ * Fills the suggestion popup with any matches received
+ */
+Drupal.guide.prototype.found = function (matches) {
+  // Unhighlight everything in the page.
+  this.unexpose();
+
+  // Prepare matches
+  var ul = document.createElement('ul');
+  var ac = this;
+  var i = 0;
+  for (key in matches) {
+    var li = document.createElement('li');
+    $(li)
+      .html('<div>'+ matches[key][0] +'</div>')
+      .mousedown(function () { ac.select(this); })
+      .mouseover(function () { ac.highlight(this); })
+      .mouseout(function () { ac.unhighlight(this); });
+    li.guideValue = key;
+    li.guideUrl = matches[key][1];
+    $(ul).append(li);
+
+    // Highlight this link in the page.
+    this.expose(key);
+    i++;
+  }
+
+  // Show popup with matches, if any
+  if (ul.childNodes.length > 0) {
+    $(this.popup).empty().append(ul).show();
+  }
+  else {
+    $(this.popup).css({visibility: 'hidden'});
+    this.hidePopup();
+  }
+}
+
+/**
+ * Unexpose all items on the page.
+ */
+Drupal.guide.prototype.unexpose  = function () {
+  // Remove shade.
+  $('#guide-shade').remove();
+
+  // Remove bubbles.
+  if (this.exposed && this.exposed.length > 0) {
+    $(this.marked).removeClass('exposed');
+    $(this.exposed).remove();    
+    this.exposed = [];
+    this.marked = [];
+  }
+}
+
+/**
+ * Expose links to this
+ */
+Drupal.guide.prototype.expose = function (path) {
+  var guide = this;
+  // Dim the page.
+  if ($('#guide-shade').size() == 0) {
+    $('<div id="guide-shade"></div>').appendTo($('body')).css({
+      'position': 'absolute',
+      'top': '0px',
+      'left': '0px',
+      'right': '0px',
+      'height': parseInt($('body').css('height')) + 30 + 'px',
+      'margin-bottom': '-30px',
+      'background': '#000',
+      'opacity': 0.0,
+      'z-index': 10000
+    })
+    .animate({ 'opacity': 0.5 });
+    this.exposed = [];
+    this.marked = []
+  }
+
+  var z = 10001;
+  /**
+   * Highlight a DOM node.
+   */
+  var _expose = function (node, opacity) {
+    // Check if it's been exposed already.
+    if ($(node).is('.exposed') || $(node.parentNode.parentNode).css('float') != 'none') {
+      return;
+    }
+    $(node).addClass('exposed');
+    guide.marked.push(node);
+
+    // Ensure we're positioning relative to the parent (prevents
+    // shrink-wrapping of content).
+    var p = node.parentNode;
+    if ($(p).css('position') == 'static') {
+      $(p).css('position', 'relative');
+    }
+
+    // Duplicate highlighted item.
+    var clone = node.cloneNode(true);
+    $(node).before(clone);
+    var glow = node.cloneNode(true);
+    $(node).before(glow);
+
+    // Place above all else.
+    $([glow, clone]).css({
+      'position': 'absolute',
+      'opacity': 0      
+    });
+
+    // Style and animate (hardcoded, to override).
+    $(glow).css({
+      'z-index': z++,
+      'background': '#000',
+      'padding': '12px 27px',
+      'margin': '-10px -27px',
+      'border-radius': '18px',
+      '-moz-border-radius': '18px'
+    })
+    .animate({ 'opacity': opacity * 0.25 });
+    $(clone).css({
+      'z-index': z++,
+      'background': '#fff',
+      'border': '1px solid #ccc',
+      'padding': '7px 22px',
+      'margin': '-8px -23px',
+      'border-radius': '14px',
+      '-moz-border-radius': '14px'
+    })
+    .animate({ 'opacity': opacity });
+
+    // Remember clone elements for later.
+    guide.exposed.push(glow);
+    guide.exposed.push(clone);
+  }
+
+  // Highlight exact matches.
+  $('a[@href$='+ path +']').each(function () { _expose(this, 1) });
+
+  // Highlight partial matches from 3 or more path components.
+  var path = path.split('/');
+  var part = '';
+  for (i in path) {
+    part += (part == '' ? '' : '/') + path[i];
+    if (i >= 2) {
+      $('a[@href$='+ part +']').each(function () { _expose(this, 0.5) });
+    }
+  }
+}
+
+
+// Global Killswitch
+if (Drupal.jsEnabled) {
+  $(document).ready(Drupal.guideAutoAttach);
+}
Index: modules/help/help.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/help/help.module,v
retrieving revision 1.66
diff -u -p -r1.66 help.module
--- modules/help/help.module	1 Dec 2006 22:47:53 -0000	1.66
+++ modules/help/help.module	10 Dec 2006 09:18:12 -0000
@@ -12,8 +12,19 @@
 function help_menu($may_cache) {
   $items = array();
 
+  $admin_access = user_access('access administration pages');
+
   if ($may_cache) {
-    $admin_access = user_access('access administration pages');
+    $items[] = array('path' => 'admin/help/guide/page',
+      'callback' => 'help_guide_page',
+      'access' => $admin_access,
+      'type' => MENU_CALLBACK,
+    );
+
+    $items[] = array('path' => 'admin/help/guide/js',
+      'callback' => 'help_guide_js',
+      'access' => $admin_access,
+      'type' => MENU_CALLBACK);
 
     $items[] = array('path' => 'admin/help', 'title' => t('Help'),
       'callback' => 'help_main',
@@ -27,6 +38,15 @@ function help_menu($may_cache) {
         'type' => MENU_CALLBACK,
         'access' => $admin_access);
     }
+
+  }
+  else {
+    // Add guide search to all top-level admin pages.
+    if (arg(0) == 'admin' && arg(2) == '' && $admin_access) {
+      drupal_add_js('misc/autocomplete.js');
+      drupal_add_js(drupal_get_path('module', 'help') .'/help.js');
+      drupal_set_content('content', drupal_get_form('help_guide_form'));
+    }
   }
 
   return $items;
@@ -134,3 +154,149 @@ function help_page() {
   }
   return $output;
 }
+
+/**
+ * Form callback: return the form for querying the guide.
+ */
+function help_guide_form() {
+  // Add CSS.
+  drupal_add_css(drupal_get_path('module', 'help') .'/help.css');
+
+  // Pass data to JS.
+  drupal_add_js(array('guide' => array('url' => url('admin/help/guide/js', NULL, NULL, TRUE))), 'setting');
+
+  $form['query'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Find'),
+    '#size' => 30,
+    '#attributes' => array('class' => 'guide form-autocomplete'),
+  );
+  return $form;
+}
+
+/**
+ * Menu callback: print a page with results for a guide query.
+ */
+function help_guide_page($query) {
+  // Query database
+  $results = help_guide_query($query);
+
+  // Fetch menu
+  $menu = menu_get_menu();
+
+  // Format results
+  $list = array();
+  foreach ($results as $path) {
+    $list[] = l($menu['items'][$menu['path index'][$path]]['title'], $path);
+  }
+  drupal_set_title(t('Find pages related to %search', array('%search' => $query)));
+  return theme('item_list', $list, t('The following pages were found:'));
+}
+
+/**
+ * Menu callback: return JSON results for a guide query.
+ */
+function help_guide_js($query) {
+  // Query database
+  $results = help_guide_query($query);
+  drupal_get_messages(NULL, TRUE);
+
+  // Fetch menu
+  $menu = menu_get_menu();
+
+  // Format results
+  $output = array();
+  foreach ($results as $path) {
+    $output[$path] = array(check_plain($menu['items'][$menu['path index'][$path]]['title']) . ' <span class="path">('. check_plain($path) .')</span>', check_url(url($path, NULL, NULL, TRUE)));
+  }
+
+  print drupal_to_js($output);
+  exit();
+}
+
+/**
+ * Perform a guide query.
+ */
+function help_guide_query($query) {
+  // Check if the index is available.
+  $path_map = help_guide_map();
+  if (count($path_map) == 0) {
+    return array();
+  }
+
+  // OR search
+  $query = implode(' OR ', explode(' ', $query));
+
+  // Query search index
+  $results = array();
+  $result = do_search($query, 'help');
+  foreach ($result as $item) {
+    $results[] = $path_map[$item->sid];
+  }
+  
+  return $results;
+}
+
+/**
+ * Return the guide path map.
+ */
+function help_guide_map() {
+  // We use the menu cache, so we know the menu has rebuilt when it's been wiped.
+  if ($path_map = cache_get('help:path_map', 'cache_menu')) {
+    return unserialize($path_map->data);
+  }
+  else {
+    // Avoid multiple index runs at the same time.
+    cache_set('help:path_map', 'cache_menu', serialize(array()), CACHE_PERMANENT);
+
+    // Rebuild the index and path map.
+    return help_guide_index();
+  }
+}
+
+/**
+ * Re-index the guide.
+ */
+function help_guide_index() {
+  // Clean search index.
+  db_query("DELETE FROM {search_index} WHERE type = 'help'");
+
+  // Prepare path map.
+  $path_map = array();
+  $i = 0;
+
+  $menu = menu_get_menu();
+  foreach ($menu['path index'] as $path => $mid) {
+    // Fetch next item.
+    $item = $menu['items'][$mid];
+    if ($item['pid'] == 0 || !($item['type'] & (MENU_VISIBLE_IN_TREE | MENU_IS_LOCAL_TASK))) {
+      continue;
+    }
+
+    // Prepare snippet to index.
+    $output = '<h1>'. $item['title'] .'</h1>';
+    $output .= '<h2>'. str_replace('/', ' ', $path) .'</h2>';
+    $output .= '<h3>'. $item['description'] .'</h3>';
+    
+    // Fetch help.
+    foreach (module_list() as $name) {
+      $output .= module_invoke($name, 'help', $path);
+    }
+
+    // Fetch sid.
+    if (!isset($path_map[$path])) {
+      $path_map[$path] = ++$i;
+    }
+
+    // Add to index.
+    search_index($path_map[$path], 'help', $output);
+  }
+
+  // Update search totals.
+  search_update_totals();
+
+  // Update and return path map.
+  $path_map = array_flip($path_map);
+  cache_set('help:path_map', 'cache_menu', serialize($path_map), CACHE_PERMANENT);
+  return $path_map;
+}
Index: profiles/default/default.profile
===================================================================
RCS file: /cvs/drupal/drupal/profiles/default/default.profile,v
retrieving revision 1.2
diff -u -p -r1.2 default.profile
--- profiles/default/default.profile	29 Oct 2006 13:17:38 -0000	1.2
+++ profiles/default/default.profile	10 Dec 2006 09:18:12 -0000
@@ -8,7 +8,7 @@
  *  An array of modules to be enabled.
  */
 function default_profile_modules() {
-  return array('block', 'color', 'comment', 'filter', 'help', 'menu', 'node', 'system', 'taxonomy', 'user', 'watchdog');
+  return array('block', 'color', 'comment', 'filter', 'help', 'menu', 'node', 'search', 'system', 'taxonomy', 'user', 'watchdog');
 }
 
 /**
Index: themes/garland/style.css
===================================================================
RCS file: /cvs/drupal/drupal/themes/garland/style.css,v
retrieving revision 1.10
diff -u -p -r1.10 style.css
--- themes/garland/style.css	4 Dec 2006 23:15:40 -0000	1.10
+++ themes/garland/style.css	10 Dec 2006 09:18:12 -0000
@@ -878,7 +878,7 @@ table.system-status-report th {
   color: #fff;
 }
 
-tr.selected td a:link, tr.selected td a:visited, tr.selected td a:active {
+tr.selected td a:link, tr.selected td a:visited, tr.selected td a:active, #autocomplete.guide-popup li.selected span.path {
   color: #d3e7f4;
 }
 
