1 var gSelectedIndex = -1; 2 var gSelectedID = -1; 3 var gMatches = new Array(); 4 var gLastText = ""; 5 var ROW_COUNT = 20; 6 var gInitialized = false; 7 var DEFAULT_TEXT = "search developer docs"; 8 9 function set_row_selected(row, selected) 10 { 11 var c1 = row.cells[0]; 12 // var c2 = row.cells[1]; 13 if (selected) { 14 c1.className = "jd-autocomplete jd-selected"; 15 // c2.className = "jd-autocomplete jd-selected jd-linktype"; 16 } else { 17 c1.className = "jd-autocomplete"; 18 // c2.className = "jd-autocomplete jd-linktype"; 19 } 20 } 21 22 function set_row_values(toroot, row, match) 23 { 24 var link = row.cells[0].childNodes[0]; 25 link.innerHTML = match.__hilabel || match.label; 26 link.href = toroot + match.link 27 // row.cells[1].innerHTML = match.type; 28 } 29 30 function sync_selection_table(toroot) 31 { 32 var filtered = document.getElementById("search_filtered"); 33 var r; //TR DOM object 34 var i; //TR iterator 35 gSelectedID = -1; 36 37 filtered.onmouseover = function() { 38 if(gSelectedIndex >= 0) { 39 set_row_selected(this.rows[gSelectedIndex], false); 40 gSelectedIndex = -1; 41 } 42 } 43 44 //initialize the table; draw it for the first time (but not visible). 45 if (!gInitialized) { 46 for (i=0; i<ROW_COUNT; i++) { 47 var r = filtered.insertRow(-1); 48 var c1 = r.insertCell(-1); 49 // var c2 = r.insertCell(-1); 50 c1.className = "jd-autocomplete"; 51 // c2.className = "jd-autocomplete jd-linktype"; 52 var link = document.createElement("a"); 53 c1.onmousedown = function() { 54 window.location = this.firstChild.getAttribute("href"); 55 } 56 c1.onmouseover = function() { 57 this.className = this.className + " jd-selected"; 58 } 59 c1.onmouseout = function() { 60 this.className = "jd-autocomplete"; 61 } 62 c1.appendChild(link); 63 } 64 /* var r = filtered.insertRow(-1); 65 var c1 = r.insertCell(-1); 66 c1.className = "jd-autocomplete jd-linktype"; 67 c1.colSpan = 2; */ 68 gInitialized = true; 69 } 70 71 //if we have results, make the table visible and initialize result info 72 if (gMatches.length > 0) { 73 document.getElementById("search_filtered_div").className = "showing"; 74 var N = gMatches.length < ROW_COUNT ? gMatches.length : ROW_COUNT; 75 for (i=0; i<N; i++) { 76 r = filtered.rows[i]; 77 r.className = "show-row"; 78 set_row_values(toroot, r, gMatches[i]); 79 set_row_selected(r, i == gSelectedIndex); 80 if (i == gSelectedIndex) { 81 gSelectedID = gMatches[i].id; 82 } 83 } 84 //start hiding rows that are no longer matches 85 for (; i<ROW_COUNT; i++) { 86 r = filtered.rows[i]; 87 r.className = "no-display"; 88 } 89 //if there are more results we're not showing, so say so. 90 /* if (gMatches.length > ROW_COUNT) { 91 r = filtered.rows[ROW_COUNT]; 92 r.className = "show-row"; 93 c1 = r.cells[0]; 94 c1.innerHTML = "plus " + (gMatches.length-ROW_COUNT) + " more"; 95 } else { 96 filtered.rows[ROW_COUNT].className = "hide-row"; 97 }*/ 98 //if we have no results, hide the table 99 } else { 100 document.getElementById("search_filtered_div").className = "no-display"; 101 } 102 } 103 104 function search_changed(e, kd, toroot) 105 { 106 var search = document.getElementById("search_autocomplete"); 107 var text = search.value.replace(/(^ +)|( +$)/g, ''); 108 109 // 13 = enter 110 if (e.keyCode == 13) { 111 document.getElementById("search_filtered_div").className = "no-display"; 112 if (kd && gSelectedIndex >= 0) { 113 window.location = toroot + gMatches[gSelectedIndex].link; 114 return false; 115 } else if (gSelectedIndex < 0) { 116 return true; 117 } 118 } 119 // 38 -- arrow up 120 else if (kd && (e.keyCode == 38)) { 121 if (gSelectedIndex >= 0) { 122 gSelectedIndex--; 123 } 124 sync_selection_table(toroot); 125 return false; 126 } 127 // 40 -- arrow down 128 else if (kd && (e.keyCode == 40)) { 129 if (gSelectedIndex < gMatches.length-1 130 && gSelectedIndex < ROW_COUNT-1) { 131 gSelectedIndex++; 132 } 133 sync_selection_table(toroot); 134 return false; 135 } 136 else if (!kd) { 137 gMatches = new Array(); 138 matchedCount = 0; 139 gSelectedIndex = -1; 140 for (var i=0; i<DATA.length; i++) { 141 var s = DATA[i]; 142 if (text.length != 0 && 143 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) { 144 gMatches[matchedCount] = s; 145 matchedCount++; 146 } 147 } 148 rank_autocomplete_results(text); 149 for (var i=0; i<gMatches.length; i++) { 150 var s = gMatches[i]; 151 if (gSelectedID == s.id) { 152 gSelectedIndex = i; 153 } 154 } 155 highlight_autocomplete_result_labels(text); 156 sync_selection_table(toroot); 157 return true; // allow the event to bubble up to the search api 158 } 159 } 160 161 function rank_autocomplete_results(query) { 162 query = query || ''; 163 if (!gMatches || !gMatches.length) 164 return; 165 166 // helper function that gets the last occurence index of the given regex 167 // in the given string, or -1 if not found 168 var _lastSearch = function(s, re) { 169 if (s == '') 170 return -1; 171 var l = -1; 172 var tmp; 173 while ((tmp = s.search(re)) >= 0) { 174 if (l < 0) l = 0; 175 l += tmp; 176 s = s.substr(tmp + 1); 177 } 178 return l; 179 }; 180 181 // helper function that counts the occurrences of a given character in 182 // a given string 183 var _countChar = function(s, c) { 184 var n = 0; 185 for (var i=0; i<s.length; i++) 186 if (s.charAt(i) == c) ++n; 187 return n; 188 }; 189 190 var queryLower = query.toLowerCase(); 191 var queryAlnum = (queryLower.match(/\w+/) || [''])[0]; 192 var partPrefixAlnumRE = new RegExp('\\b' + queryAlnum); 193 var partExactAlnumRE = new RegExp('\\b' + queryAlnum + '\\b'); 194 195 var _resultScoreFn = function(result) { 196 // scores are calculated based on exact and prefix matches, 197 // and then number of path separators (dots) from the last 198 // match (i.e. favoring classes and deep package names) 199 var score = 1.0; 200 var labelLower = result.label.toLowerCase(); 201 var t; 202 t = _lastSearch(labelLower, partExactAlnumRE); 203 if (t >= 0) { 204 // exact part match 205 var partsAfter = _countChar(labelLower.substr(t + 1), '.'); 206 score *= 200 / (partsAfter + 1); 207 } else { 208 t = _lastSearch(labelLower, partPrefixAlnumRE); 209 if (t >= 0) { 210 // part prefix match 211 var partsAfter = _countChar(labelLower.substr(t + 1), '.'); 212 score *= 20 / (partsAfter + 1); 213 } 214 } 215 216 return score; 217 }; 218 219 for (var i=0; i<gMatches.length; i++) { 220 gMatches[i].__resultScore = _resultScoreFn(gMatches[i]); 221 } 222 223 gMatches.sort(function(a,b){ 224 var n = b.__resultScore - a.__resultScore; 225 if (n == 0) // lexicographical sort if scores are the same 226 n = (a.label < b.label) ? -1 : 1; 227 return n; 228 }); 229 } 230 231 function highlight_autocomplete_result_labels(query) { 232 query = query || ''; 233 if (!gMatches || !gMatches.length) 234 return; 235 236 var queryLower = query.toLowerCase(); 237 var queryAlnumDot = (queryLower.match(/[\w\.]+/) || [''])[0]; 238 var queryRE = new RegExp( 239 '(' + queryAlnumDot.replace(/\./g, '\\.') + ')', 'ig'); 240 for (var i=0; i<gMatches.length; i++) { 241 gMatches[i].__hilabel = gMatches[i].label.replace( 242 queryRE, '<b>$1</b>'); 243 } 244 } 245 246 function search_focus_changed(obj, focused) 247 { 248 if (focused) { 249 if(obj.value == DEFAULT_TEXT){ 250 obj.value = ""; 251 obj.style.color="#000000"; 252 } 253 } else { 254 if(obj.value == ""){ 255 obj.value = DEFAULT_TEXT; 256 obj.style.color="#aaaaaa"; 257 } 258 document.getElementById("search_filtered_div").className = "no-display"; 259 } 260 } 261 262 function submit_search() { 263 var query = document.getElementById('search_autocomplete').value; 264 document.location = toRoot + 'search.html#q=' + query + '&t=0'; 265 return false; 266 } 267