Merge "Enable query highlighting and result ranking for search autocomplete on d.a.c. Also make it case-insensitive."

This commit is contained in:
Roman Nurik 2010-04-19 14:34:13 -07:00 committed by Android (Google) Code Review
commit e01d8a70d5
2 changed files with 100 additions and 9 deletions

View file

@ -519,7 +519,7 @@ div.indent {
padding-right: 6px;
padding-top: 1px;
padding-bottom: 1px;
font-size: .8em;
font-size: 0.81em;
border: none;
margin: 0;
line-height: 1.05em;

View file

@ -2,7 +2,7 @@ var gSelectedIndex = -1;
var gSelectedID = -1;
var gMatches = new Array();
var gLastText = "";
var ROW_COUNT = 30;
var ROW_COUNT = 20;
var gInitialized = false;
var DEFAULT_TEXT = "search developer docs";
@ -22,7 +22,7 @@ function set_row_selected(row, selected)
function set_row_values(toroot, row, match)
{
var link = row.cells[0].childNodes[0];
link.innerHTML = match.label;
link.innerHTML = match.__hilabel || match.label;
link.href = toroot + match.link
// row.cells[1].innerHTML = match.type;
}
@ -104,7 +104,7 @@ function sync_selection_table(toroot)
function search_changed(e, kd, toroot)
{
var search = document.getElementById("search_autocomplete");
var text = search.value;
var text = search.value.replace(/(^ +)|( +$)/g, '');
// 13 = enter
if (e.keyCode == 13) {
@ -137,21 +137,112 @@ function search_changed(e, kd, toroot)
gMatches = new Array();
matchedCount = 0;
gSelectedIndex = -1;
for (i=0; i<DATA.length; i++) {
for (var i=0; i<DATA.length; i++) {
var s = DATA[i];
if (text.length != 0 && s.label.indexOf(text) != -1) {
if (text.length != 0 &&
s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
gMatches[matchedCount] = s;
if (gSelectedID == s.id) {
gSelectedIndex = matchedCount;
}
matchedCount++;
}
}
rank_autocomplete_results(text);
for (var i=0; i<gMatches.length; i++) {
var s = gMatches[i];
if (gSelectedID == s.id) {
gSelectedIndex = i;
}
}
highlight_autocomplete_result_labels(text);
sync_selection_table(toroot);
return true; // allow the event to bubble up to the search api
}
}
function rank_autocomplete_results(query) {
query = query || '';
if (!gMatches || !gMatches.length)
return;
// helper function that gets the last occurence index of the given regex
// in the given string, or -1 if not found
var _lastSearch = function(s, re) {
if (s == '')
return -1;
var l = -1;
var tmp;
while ((tmp = s.search(re)) >= 0) {
if (l < 0) l = 0;
l += tmp;
s = s.substr(tmp + 1);
}
return l;
};
// helper function that counts the occurrences of a given character in
// a given string
var _countChar = function(s, c) {
var n = 0;
for (var i=0; i<s.length; i++)
if (s.charAt(i) == c) ++n;
return n;
};
var queryLower = query.toLowerCase();
var queryAlnum = (queryLower.match(/\w+/) || [''])[0];
var partPrefixAlnumRE = new RegExp('\\b' + queryAlnum);
var partExactAlnumRE = new RegExp('\\b' + queryAlnum + '\\b');
var _resultScoreFn = function(result) {
// scores are calculated based on exact and prefix matches,
// and then number of path separators (dots) from the last
// match (i.e. favoring classes and deep package names)
var score = 1.0;
var labelLower = result.label.toLowerCase();
var t;
t = _lastSearch(labelLower, partExactAlnumRE);
if (t >= 0) {
// exact part match
var partsAfter = _countChar(labelLower.substr(t + 1), '.');
score *= 200 / (partsAfter + 1);
} else {
t = _lastSearch(labelLower, partPrefixAlnumRE);
if (t >= 0) {
// part prefix match
var partsAfter = _countChar(labelLower.substr(t + 1), '.');
score *= 20 / (partsAfter + 1);
}
}
return score;
};
for (var i=0; i<gMatches.length; i++) {
gMatches[i].__resultScore = _resultScoreFn(gMatches[i]);
}
gMatches.sort(function(a,b){
var n = b.__resultScore - a.__resultScore;
if (n == 0) // lexicographical sort if scores are the same
n = (a.label < b.label) ? -1 : 1;
return n;
});
}
function highlight_autocomplete_result_labels(query) {
query = query || '';
if (!gMatches || !gMatches.length)
return;
var queryLower = query.toLowerCase();
var queryAlnumDot = (queryLower.match(/[\w\.]+/) || [''])[0];
var queryRE = new RegExp(
'(' + queryAlnumDot.replace(/\./g, '\\.') + ')', 'ig');
for (var i=0; i<gMatches.length; i++) {
gMatches[i].__hilabel = gMatches[i].label.replace(
queryRE, '<b>$1</b>');
}
}
function search_focus_changed(obj, focused)
{
if (focused) {