function ShSearch(lbox) {
  var laststring='';
  var displaying='';
  var items=[];
  var itemcount=0;
  var freechars='';
  var freeback=0;
  var querySuffix='';
  var initCB=null;
  var itemInCB=null;
  var clearDataCB=null;
  var o=DOMhelp.createElement;
  var status=o('div#status');
  var scripthldr=o('div');
  var helpimg=null;

  window.shv_key=0;
  function resetStatus() {
    DOMhelp.replaceContent(status,
      "(0 results)");
    if (helpimg)
      helpimg.style.display='block';
  }
  resetStatus();
  this.setHelpImg=function(elt) {
    helpimg=elt;
  }

  this.DOMelements = function() {
    return [status, scripthldr];
  };
  this.setInitCB = function(fn) { initCB=fn; };
  this.setItemInCB = function(fn) { itemInCB=fn; };
  this.setClearDataCB = function(fn) { clearDataCB=fn; };
  window.shv_init=function(count, fchars, fback, key, p1, p2, p3, p4) {
    bInitiialized=true;
    if (key!=shv_key) return;
    if (clearDataCB) clearDataCB(count);
    lbox.clearItems(Math.min(count,801));
    itemcount=count;
    freechars=fchars;
    freeback=fback;
    if (initCB)
      initCB(count, p1, p2, p3, p4);

    displaying=laststring;
    DOMhelp.replaceContent(status, '('+count+(count==1 ? ' result' : ' results')+')');
    if (helpimg)
      helpimg.style.display='none';
  };
  window.shv_items=function(first_idx, key, newitems) {
    if (key!=shv_key) return;
    for (var i=0; i<newitems.length && first_idx+i<=800; ++i) {
      items[first_idx+i]=newitems[i];
      lbox.itemReceived(first_idx+i);
    }
    setTimeout(function() {if (window.shv_key==key) tryMoreData();},10);
  };
  window.shv_error=function(s) {
    displaying=laststring;
    DOMhelp.replaceContent(status, 'Search error, please try again later');
  };
  this.setQuerySuffix=function(str) {
    querySuffix=str;
  };
  this.getItem=function(num) {
    return (num<items.length) ? items[num] : null;
  };
  function tryMoreData() {
    if (items.length<itemcount && items.length<801)
      execScript('http://search.scrapbooks.com/cgi-bin/c2?W='+escape(laststring)+'&S='+items.length+querySuffix,laststring);
  }
  function execScript(page) {
    window.shv_key=Math.floor(Math.random()*0x7fffffff);
    DOMhelp.appendContent(scripthldr, o('script', {'src':page+"&K="+shv_key}));
  }
  function difference(txt) {
    if (laststring!=displaying)
      return false;
    var n=laststring.length;
    if (txt.substr(0,n)==laststring) {
      var extra=txt.substr(n).toUpperCase();
      n=extra.length;
      if (freechars.substr(0, n)!=extra)
        return false;
      freechars = freechars.substr(n);
      freeback += n;
      displaying = laststring = txt;
      tryMoreData();
      return true;
    }
    n=txt.length;
    if (laststring.substr(0,n)==txt) {
      var gone=laststring.substr(n).toUpperCase();
      n = gone.length;
      if (n>freeback)
        return false;
      freeback -= n;
      freechars = gone + freechars;
      displaying = laststring = txt;
      tryMoreData();
      return true;
    }
    return false;
  }
  this.txtupdate=function(txt) {
    if (txt==laststring || difference(txt))
      return;
    items=[];
    DOMhelp.replaceContent(scripthldr);
    laststring=txt;
    if (txt.length<=2) {
      if (clearDataCB) clearDataCB(0);
      lbox.clearItems(0);
      itemcount=0;
      freechars='';
      freeback=0;
      displaying=txt;
      window.shv_key=Math.floor(Math.random()*0x7fffffff);
      resetStatus();
    } else {
      function searching() {
        if (txt==laststring && txt!=displaying)
          DOMhelp.replaceContent(status, 'Searching...');
      }
      setTimeout(searching, 250);
      execScript('http://search.scrapbooks.com/cgi-bin/i2?W='+escape(txt),txt);
    }
  };
  lbox.setLimitCB(function() {return items.length;});
  lbox.setItemInCB(function(num, elt) {
    if (num==800 && itemcount>801) {
      DOMhelp.replaceContent(elt,
        o('div.endresults',
          o('b', 'The first 800 of '+itemcount+' results are displayed above.'),
          o('br'),
          'If the product you\'re looking for doesn\'t appear above, please make your search more specific.'));
      elt.onclick=null;
    } else if (num<itemcount && num<items.length && itemInCB)
      itemInCB(items[num], num, elt);
  });
}
