String.prototype.score = function(abbreviation,offset) {
  offset = offset || 0 // TODO: I think this is unused... remove

  if(abbreviation.length == 0) return 0.9
  if(abbreviation.length > this.length) return 0.0

  for (var i = abbreviation.length; i > 0; i--) {
    var sub_abbreviation = abbreviation.substring(0,i)
    var index = this.indexOf(sub_abbreviation)


    if(index < 0) continue;
    if(index + abbreviation.length > this.length + offset) continue;

    var next_string       = this.substring(index+sub_abbreviation.length)
    var next_abbreviation = null

    if(i >= abbreviation.length)
      next_abbreviation = ''
    else
      next_abbreviation = abbreviation.substring(i)

    var remaining_score   = next_string.score(next_abbreviation,offset+index)

    if (remaining_score > 0) {
      var score = this.length-next_string.length;

      if(index != 0) {
        var j = 0;

        var c = this.charCodeAt(index-1)
        if(c==32 || c == 9) {
          for(var j=(index-2); j >= 0; j--) {
            c = this.charCodeAt(j)
            score -= ((c == 32 || c == 9) ? 1 : 0.15)
          }

          // XXX maybe not port this heuristic
          //
          //          } else if ([[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember:[self characterAtIndex:matchedRange.location]]) {
          //            for (j = matchedRange.location-1; j >= (int) searchRange.location; j--) {
          //              if ([[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember:[self characterAtIndex:j]])
          //                score--;
          //              else
          //                score -= 0.15;
          //            }
        } else {
          score -= index
        }
      }

      score += remaining_score * next_string.length
      score /= this.length;
      return score
    }
  }
  return 0.0
}

jQuery.fn.liveUpdate = function(list){
	list = jQuery(list);

	if ( list.length ) {
		var rows = list.children('li'),
			cache = rows.map(function(){
				return jQuery(this).children('a').text().toLowerCase();
			});

		this.keyup(filter).keyup().parents('form').submit(function(){
				return false;
			});

		this.keyup(function(e) {
			if(e.keyCode == 27) {
				//hide all
				rows.each(function(index, item){
					jQuery(item).removeClass("result_noindex_shown");
					jQuery(item).addClass("result_noindex_hidden");
				});
			}
		});
	}

	return this;

	function filter(e){
		if (e.keyCode > 36  && e.keyCode < 41) {
			return;
		}
		var term = jQuery.trim( jQuery(this).val().toLowerCase() ), scores = [];
		rows.each(function(index, item){
			jQuery(item).removeClass();
			jQuery(item).addClass("result_noindex_hidden");
		});
		if (!term) {
			return;
		}
		cache.each(function(i){
			var score = this.score(term);
			if (score > 0.5) { scores.push([score, i]); }
		});

		jQuery.each(scores.sort(function(a, b){return b[0] - a[0];}), function(){
			jQuery(rows[ this[1] ]).removeClass();
			jQuery(rows[ this[1] ]).addClass("result_noindex_shown");
		});

    $('#search_result_list > li[class=result_noindex_shown]').keynav('result_with_focus','result_without_focus');

    var first = true;
    var last = null;
    rows.each(function(index, item){
      if (jQuery(item).hasClass("result_noindex_shown")) {
        if (first) {
          jQuery(item).addClass("result_noindex_first");
          first = false;
          $.keynav.setActive(item);
        }
        last = item;
      }
    });
    jQuery(last).addClass("result_noindex_last");
	}
};
