Merge "Enable query highlighting and result ranking for search autocomplete on d.a.c. Also make it case-insensitive."
This commit is contained in:
commit
e01d8a70d5
2 changed files with 100 additions and 9 deletions
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue