28 var catPackages = "Packages"; |
28 var catPackages = "Packages"; |
29 var catTypes = "Types"; |
29 var catTypes = "Types"; |
30 var catMembers = "Members"; |
30 var catMembers = "Members"; |
31 var catSearchTags = "SearchTags"; |
31 var catSearchTags = "SearchTags"; |
32 var highlight = "<span class=\"resultHighlight\">$&</span>"; |
32 var highlight = "<span class=\"resultHighlight\">$&</span>"; |
33 var camelCaseRegexp = ""; |
33 var searchPattern = ""; |
34 var secondaryMatcher = ""; |
34 var RANKING_THRESHOLD = 2; |
|
35 var NO_MATCH = 0xffff; |
|
36 var MAX_RESULTS_PER_CATEGORY = 500; |
35 function escapeHtml(str) { |
37 function escapeHtml(str) { |
36 return str.replace(/</g, "<").replace(/>/g, ">"); |
38 return str.replace(/</g, "<").replace(/>/g, ">"); |
37 } |
39 } |
38 function getHighlightedText(item) { |
40 function getHighlightedText(item, matcher) { |
39 var ccMatcher = new RegExp(escapeHtml(camelCaseRegexp)); |
|
40 var escapedItem = escapeHtml(item); |
41 var escapedItem = escapeHtml(item); |
41 var label = escapedItem.replace(ccMatcher, highlight); |
42 return escapedItem.replace(matcher, highlight); |
42 if (label === escapedItem) { |
|
43 var secMatcher = new RegExp(escapeHtml(secondaryMatcher.source), "i"); |
|
44 label = escapedItem.replace(secMatcher, highlight); |
|
45 } |
|
46 return label; |
|
47 } |
43 } |
48 function getURLPrefix(ui) { |
44 function getURLPrefix(ui) { |
49 var urlPrefix=""; |
45 var urlPrefix=""; |
50 var slash = "/"; |
46 var slash = "/"; |
51 if (ui.item.category === catModules) { |
47 if (ui.item.category === catModules) { |
144 div.html(label); |
168 div.html(label); |
145 } |
169 } |
146 return li; |
170 return li; |
147 } |
171 } |
148 }); |
172 }); |
|
173 function rankMatch(match, category) { |
|
174 if (!match) { |
|
175 return NO_MATCH; |
|
176 } |
|
177 var index = match.index; |
|
178 var input = match.input; |
|
179 var leftBoundaryMatch = 2; |
|
180 var periferalMatch = 0; |
|
181 var delta = 0; |
|
182 // make sure match is anchored on a left word boundary |
|
183 if (index === 0 || /\W/.test(input[index - 1]) || "_" === input[index - 1] || "_" === input[index]) { |
|
184 leftBoundaryMatch = 0; |
|
185 } else if (input[index] === input[index].toUpperCase() && !/^[A-Z0-9_$]+$/.test(input)) { |
|
186 leftBoundaryMatch = 1; |
|
187 } |
|
188 var matchEnd = index + match[0].length; |
|
189 var leftParen = input.indexOf("("); |
|
190 // exclude peripheral matches |
|
191 if (category !== catModules && category !== catSearchTags) { |
|
192 var endOfName = leftParen > -1 ? leftParen : input.length; |
|
193 var delim = category === catPackages ? "/" : "."; |
|
194 if (leftParen > -1 && leftParen < index) { |
|
195 periferalMatch += 2; |
|
196 } else if (input.lastIndexOf(delim, endOfName) >= matchEnd) { |
|
197 periferalMatch += 2; |
|
198 } |
|
199 } |
|
200 for (var i = 1; i < match.length; i++) { |
|
201 // lower ranking if parts of the name are missing |
|
202 if (match[i]) |
|
203 delta += match[i].length; |
|
204 } |
|
205 if (category === catTypes) { |
|
206 // lower ranking if a type name contains unmatched camel-case parts |
|
207 if (/[A-Z]/.test(input.substring(matchEnd))) |
|
208 delta += 5; |
|
209 if (/[A-Z]/.test(input.substring(0, index))) |
|
210 delta += 5; |
|
211 } |
|
212 return leftBoundaryMatch + periferalMatch + (delta / 200); |
|
213 |
|
214 } |
149 $(function() { |
215 $(function() { |
150 $("#search").catcomplete({ |
216 $("#search").catcomplete({ |
151 minLength: 1, |
217 minLength: 1, |
152 delay: 300, |
218 delay: 300, |
153 source: function(request, response) { |
219 source: function(request, response) { |
154 var result = new Array(); |
220 var result = []; |
155 var presult = new Array(); |
221 var newResults = []; |
156 var tresult = new Array(); |
222 |
157 var mresult = new Array(); |
223 searchPattern = makeCamelCaseRegex(request.term); |
158 var tgresult = new Array(); |
224 if (searchPattern === "") { |
159 var secondaryresult = new Array(); |
225 return this.close(); |
160 var displayCount = 0; |
226 } |
161 var exactMatcher = new RegExp("^" + $.ui.autocomplete.escapeRegex(request.term) + "$", "i"); |
227 var camelCaseMatcher = createMatcher(searchPattern, ""); |
162 camelCaseRegexp = ($.ui.autocomplete.escapeRegex(request.term)).split(/(?=[A-Z])/).join("([a-z0-9_$]*?)"); |
228 var boundaryMatcher = createMatcher("\\b" + searchPattern, ""); |
163 var camelCaseMatcher = new RegExp("^" + camelCaseRegexp); |
|
164 secondaryMatcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), "i"); |
|
165 |
|
166 // Return the nested innermost name from the specified object |
|
167 function nestedName(e) { |
|
168 return e.l.substring(e.l.lastIndexOf(".") + 1); |
|
169 } |
|
170 |
229 |
171 function concatResults(a1, a2) { |
230 function concatResults(a1, a2) { |
172 a1 = a1.concat(a2); |
231 a2.sort(function(e1, e2) { |
|
232 return e1.ranking - e2.ranking; |
|
233 }); |
|
234 a1 = a1.concat(a2.map(function(e) { return e.item; })); |
173 a2.length = 0; |
235 a2.length = 0; |
174 return a1; |
236 return a1; |
175 } |
237 } |
176 |
238 |
177 if (moduleSearchIndex) { |
239 if (moduleSearchIndex) { |
178 var mdleCount = 0; |
|
179 $.each(moduleSearchIndex, function(index, item) { |
240 $.each(moduleSearchIndex, function(index, item) { |
180 item.category = catModules; |
241 item.category = catModules; |
181 if (exactMatcher.test(item.l)) { |
242 var ranking = rankMatch(boundaryMatcher.exec(item.l), catModules); |
182 result.push(item); |
243 if (ranking < RANKING_THRESHOLD) { |
183 mdleCount++; |
244 newResults.push({ ranking: ranking, item: item }); |
184 } else if (camelCaseMatcher.test(item.l)) { |
245 } |
185 result.push(item); |
246 return newResults.length < MAX_RESULTS_PER_CATEGORY; |
186 } else if (secondaryMatcher.test(item.l)) { |
247 }); |
187 secondaryresult.push(item); |
248 result = concatResults(result, newResults); |
188 } |
|
189 }); |
|
190 displayCount = mdleCount; |
|
191 result = concatResults(result, secondaryresult); |
|
192 } |
249 } |
193 if (packageSearchIndex) { |
250 if (packageSearchIndex) { |
194 var pCount = 0; |
|
195 var pkg = ""; |
|
196 $.each(packageSearchIndex, function(index, item) { |
251 $.each(packageSearchIndex, function(index, item) { |
197 item.category = catPackages; |
252 item.category = catPackages; |
198 pkg = (item.m) |
253 var name = (item.m && request.term.indexOf("/") > -1) |
199 ? (item.m + "/" + item.l) |
254 ? (item.m + "/" + item.l) |
200 : item.l; |
255 : item.l; |
201 if (exactMatcher.test(item.l)) { |
256 var ranking = rankMatch(boundaryMatcher.exec(name), catPackages); |
202 presult.push(item); |
257 if (ranking < RANKING_THRESHOLD) { |
203 pCount++; |
258 newResults.push({ ranking: ranking, item: item }); |
204 } else if (camelCaseMatcher.test(pkg)) { |
259 } |
205 presult.push(item); |
260 return newResults.length < MAX_RESULTS_PER_CATEGORY; |
206 } else if (secondaryMatcher.test(pkg)) { |
261 }); |
207 secondaryresult.push(item); |
262 result = concatResults(result, newResults); |
208 } |
|
209 }); |
|
210 result = result.concat(concatResults(presult, secondaryresult)); |
|
211 displayCount = (pCount > displayCount) ? pCount : displayCount; |
|
212 } |
263 } |
213 if (typeSearchIndex) { |
264 if (typeSearchIndex) { |
214 var tCount = 0; |
|
215 $.each(typeSearchIndex, function(index, item) { |
265 $.each(typeSearchIndex, function(index, item) { |
216 item.category = catTypes; |
266 item.category = catTypes; |
217 var s = nestedName(item); |
267 var name = request.term.indexOf(".") > -1 |
218 if (exactMatcher.test(s)) { |
268 ? item.p + "." + item.l |
219 tresult.push(item); |
269 : item.l; |
220 tCount++; |
270 var ranking = rankMatch(camelCaseMatcher.exec(name), catTypes); |
221 } else if (camelCaseMatcher.test(s)) { |
271 if (ranking < RANKING_THRESHOLD) { |
222 tresult.push(item); |
272 newResults.push({ ranking: ranking, item: item }); |
223 } else if (secondaryMatcher.test(item.p + "." + item.l)) { |
273 } |
224 secondaryresult.push(item); |
274 return newResults.length < MAX_RESULTS_PER_CATEGORY; |
225 } |
275 }); |
226 }); |
276 result = concatResults(result, newResults); |
227 result = result.concat(concatResults(tresult, secondaryresult)); |
|
228 displayCount = (tCount > displayCount) ? tCount : displayCount; |
|
229 } |
277 } |
230 if (memberSearchIndex) { |
278 if (memberSearchIndex) { |
231 var mCount = 0; |
|
232 $.each(memberSearchIndex, function(index, item) { |
279 $.each(memberSearchIndex, function(index, item) { |
233 item.category = catMembers; |
280 item.category = catMembers; |
234 var s = nestedName(item); |
281 var name = request.term.indexOf(".") > -1 |
235 if (exactMatcher.test(s)) { |
282 ? item.p + "." + item.c + "." + item.l |
236 mresult.push(item); |
283 : item.l; |
237 mCount++; |
284 var ranking = rankMatch(camelCaseMatcher.exec(name), catMembers); |
238 } else if (camelCaseMatcher.test(s)) { |
285 if (ranking < RANKING_THRESHOLD) { |
239 mresult.push(item); |
286 newResults.push({ ranking: ranking, item: item }); |
240 } else if (secondaryMatcher.test(item.c + "." + item.l)) { |
287 } |
241 secondaryresult.push(item); |
288 return newResults.length < MAX_RESULTS_PER_CATEGORY; |
242 } |
289 }); |
243 }); |
290 result = concatResults(result, newResults); |
244 result = result.concat(concatResults(mresult, secondaryresult)); |
|
245 displayCount = (mCount > displayCount) ? mCount : displayCount; |
|
246 } |
291 } |
247 if (tagSearchIndex) { |
292 if (tagSearchIndex) { |
248 var tgCount = 0; |
|
249 $.each(tagSearchIndex, function(index, item) { |
293 $.each(tagSearchIndex, function(index, item) { |
250 item.category = catSearchTags; |
294 item.category = catSearchTags; |
251 if (exactMatcher.test(item.l)) { |
295 var ranking = rankMatch(boundaryMatcher.exec(item.l), catSearchTags); |
252 tgresult.push(item); |
296 if (ranking < RANKING_THRESHOLD) { |
253 tgCount++; |
297 newResults.push({ ranking: ranking, item: item }); |
254 } else if (secondaryMatcher.test(item.l)) { |
298 } |
255 secondaryresult.push(item); |
299 return newResults.length < MAX_RESULTS_PER_CATEGORY; |
256 } |
300 }); |
257 }); |
301 result = concatResults(result, newResults); |
258 result = result.concat(concatResults(tgresult, secondaryresult)); |
302 } |
259 displayCount = (tgCount > displayCount) ? tgCount : displayCount; |
303 response(result); |
260 } |
|
261 displayCount = (displayCount > 500) ? displayCount : 500; |
|
262 var counter = function() { |
|
263 var count = {Modules: 0, Packages: 0, Types: 0, Members: 0, SearchTags: 0}; |
|
264 var f = function(item) { |
|
265 count[item.category] += 1; |
|
266 return (count[item.category] <= displayCount); |
|
267 }; |
|
268 return f; |
|
269 }(); |
|
270 response(result.filter(counter)); |
|
271 }, |
304 }, |
272 response: function(event, ui) { |
305 response: function(event, ui) { |
273 if (!ui.content.length) { |
306 if (!ui.content.length) { |
274 ui.content.push(noResult); |
307 ui.content.push(noResult); |
275 } else { |
308 } else { |