| OLD | NEW |
| 1 <!DOCTYPE html> | 1 <!DOCTYPE html> |
| 2 <!-- | 2 <!-- |
| 3 * Copyright (c) 2010 The Chromium Authors. All rights reserved. Use of this | 3 * Copyright (c) 2010 The Chromium Authors. All rights reserved. Use of this |
| 4 * source code is governed by a BSD-style license that can be found in the | 4 * source code is governed by a BSD-style license that can be found in the |
| 5 * LICENSE file. | 5 * LICENSE file. |
| 6 --> | 6 --> |
| 7 <html> | 7 <html> |
| 8 <head> | 8 <head> |
| 9 </head> | 9 </head> |
| 10 <body> | 10 <body> |
| (...skipping 24 matching lines...) Expand all Loading... |
| 35 * Adds an entry to the index. | 35 * Adds an entry to the index. |
| 36 * @param {String} name Name of the function (e.g. chrome.tabs.get). | 36 * @param {String} name Name of the function (e.g. chrome.tabs.get). |
| 37 * @param {String} url Url to the documentation. | 37 * @param {String} url Url to the documentation. |
| 38 * @param {String} desc Description (optional). | 38 * @param {String} desc Description (optional). |
| 39 * @param {String} type The type of entry (e.g. method, event). | 39 * @param {String} type The type of entry (e.g. method, event). |
| 40 */ | 40 */ |
| 41 APISearchCorpus.prototype.addEntry = function(name, url, desc, type) { | 41 APISearchCorpus.prototype.addEntry = function(name, url, desc, type) { |
| 42 this.corpus_.push({ | 42 this.corpus_.push({ |
| 43 'name' : name, | 43 'name' : name, |
| 44 'url' : url, | 44 'url' : url, |
| 45 'ranges' : [], | 45 'style' : name, |
| 46 'description' : desc, | 46 'description' : desc, |
| 47 'type' : type | 47 'type' : type |
| 48 }); | 48 }); |
| 49 }; | 49 }; |
| 50 | 50 |
| 51 /** | 51 /** |
| 52 * Locates a match from the supplied keywords against text. | 52 * Locates a match from the supplied keywords against text. |
| 53 * | 53 * |
| 54 * Keywords are matched in the order supplied, and a non-overlapping | 54 * Keywords are matched in the order supplied, and a non-overlapping |
| 55 * search is used. The ranges are returned in a way that is easily | 55 * search is used. The matches are returned in a styled string that |
| 56 * converted to the style array required by the omnibox API. | 56 * can be passed directly to the omnibox API. |
| 57 * | 57 * |
| 58 * @param {Array.<String>} keywords A list of keywords to check. | 58 * @param {Array.<String>} keywords A list of keywords to check. |
| 59 * @param {String} name The name to search against. | 59 * @param {String} name The name to search against. |
| 60 * @returns {Array.<Array.<number>>|null} A list of indexes corresponding | 60 * @returns {String|null} A string containing <match> markup |
| 61 * to matches, or null if no match was found. | 61 * corresponding to the matched text, or null if no match was found. |
| 62 */ | 62 */ |
| 63 APISearchCorpus.prototype.findMatch_ = function(keywords, name) { | 63 APISearchCorpus.prototype.findMatch_ = function(keywords, name) { |
| 64 var ranges = []; | 64 var style = []; |
| 65 var indexFrom = 0; | 65 var indexFrom = 0; |
| 66 var lowerName = name.toLowerCase(); |
| 66 for (var i = 0; i < keywords.length; i++) { | 67 for (var i = 0; i < keywords.length; i++) { |
| 67 var keyword = keywords[i].toLowerCase(); | 68 var keyword = keywords[i].toLowerCase(); |
| 68 var start = name.indexOf(keyword, indexFrom); | 69 var start = lowerName.indexOf(keyword, indexFrom); |
| 69 if (start == -1) { | 70 if (start == -1) { |
| 70 return null; | 71 return null; |
| 71 } | 72 } |
| 72 var end = start + keyword.length; | 73 var end = start + keyword.length + 1; |
| 73 ranges.push([start, end]); | 74 |
| 74 indexFrom = end + 1; | 75 style.push(name.substring(indexFrom, start)) |
| 76 style.push('<match>'); |
| 77 style.push(name.substring(start, end)); |
| 78 style.push('</match>'); |
| 79 |
| 80 indexFrom = end; |
| 75 } | 81 } |
| 76 return ranges; | 82 style.push(name.substring(indexFrom)); |
| 83 return style.join(''); |
| 77 }; | 84 }; |
| 78 | 85 |
| 79 /** | 86 /** |
| 80 * Searches this corpus for the supplied text. | 87 * Searches this corpus for the supplied text. |
| 81 * @param {String} text Query text. | 88 * @param {String} text Query text. |
| 82 * @param {Number} limit Max results to return. | 89 * @param {Number} limit Max results to return. |
| 83 * @returns {Array.<Object>} A list of entries corresponding with | 90 * @returns {Array.<Object>} A list of entries corresponding with |
| 84 * matches (@see APISearchCorpus.findMatch_ for keyword search | 91 * matches (@see APISearchCorpus.findMatch_ for keyword search |
| 85 * algorithm. Results are returned in a sorted order, first by | 92 * algorithm. Results are returned in a sorted order, first by |
| 86 * length, then alphabetically by name. An exact match will be | 93 * length, then alphabetically by name. An exact match will be |
| 87 * returned first. | 94 * returned first. |
| 88 */ | 95 */ |
| 89 APISearchCorpus.prototype.search = function(text, limit) { | 96 APISearchCorpus.prototype.search = function(text, limit) { |
| 90 var results = []; | 97 var results = []; |
| 91 var match = null; | 98 var match = null; |
| 92 if (!text || text.length == 0) { | 99 if (!text || text.length == 0) { |
| 93 return this.corpus_.slice(0, limit); // No text, start listing APIs. | 100 return this.corpus_.slice(0, limit); // No text, start listing APIs. |
| 94 } | 101 } |
| 95 var searchText = text.toLowerCase(); | 102 var searchText = text.toLowerCase(); |
| 96 var keywords = searchText.split(' '); | 103 var keywords = searchText.split(' '); |
| 97 for (var i = 0; i < this.corpus_.length; i++) { | 104 for (var i = 0; i < this.corpus_.length; i++) { |
| 98 var name = this.corpus_[i]['name'].toLowerCase(); | 105 var name = this.corpus_[i]['name']; |
| 99 if (results.length < limit) { | 106 if (results.length < limit) { |
| 100 var result = this.findMatch_(keywords, name); | 107 var result = this.findMatch_(keywords, name); |
| 101 if (result) { | 108 if (result) { |
| 102 this.corpus_[i]['ranges'] = result; | 109 this.corpus_[i]['style'] = result; |
| 103 results.push(this.corpus_[i]); | 110 results.push(this.corpus_[i]); |
| 104 } | 111 } |
| 105 } | 112 } |
| 106 if (!match && searchText == name) { | 113 if (!match && searchText == name) { |
| 107 match = this.corpus_[i]; // An exact match. | 114 match = this.corpus_[i]; // An exact match. |
| 108 } | 115 } |
| 109 if (match && results.length >= limit) { | 116 if (match && results.length >= limit) { |
| 110 break; // Have an exact match and have reached the search limit. | 117 break; // Have an exact match and have reached the search limit. |
| 111 } | 118 } |
| 112 } | 119 } |
| (...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 305 this.onChanged_.bind(this)); | 312 this.onChanged_.bind(this)); |
| 306 chrome.omnibox.onInputEntered.addListener( | 313 chrome.omnibox.onInputEntered.addListener( |
| 307 this.onEntered_.bind(this)); | 314 this.onEntered_.bind(this)); |
| 308 }; | 315 }; |
| 309 | 316 |
| 310 /** | 317 /** |
| 311 * Converts a corpus match to an object suitable for the omnibox API. | 318 * Converts a corpus match to an object suitable for the omnibox API. |
| 312 * @param {Object} match The match to convert. | 319 * @param {Object} match The match to convert. |
| 313 * @returns {Object} A suggestion object formatted for the omnibox API. | 320 * @returns {Object} A suggestion object formatted for the omnibox API. |
| 314 */ | 321 */ |
| 315 OmniboxManager.prototype.matchToSuggestion_ = function(match) { | 322 OmniboxManager.prototype.convertMatchToSuggestion_ = function(match) { |
| 316 var styles = [ ]; | 323 var suggestion = [ match['style'] ]; |
| 317 var ranges = match['ranges']; | |
| 318 var desc = match['name']; | |
| 319 var name = match['name']; | |
| 320 var lastIndex = 0; | |
| 321 for (var i = 0; i < ranges.length; i++) { | |
| 322 styles.push(chrome.omnibox.styleMatch(ranges[i][0])); | |
| 323 styles.push(chrome.omnibox.styleNone(ranges[i][1])); | |
| 324 lastIndex = ranges[i][1]; | |
| 325 } | |
| 326 | |
| 327 if (match['type']) { | 324 if (match['type']) { |
| 328 // Abusing the URL style a little, but want this to stand out. | 325 // Abusing the URL style a little, but want this to stand out. |
| 329 desc = this.pushStyle_(styles, 'Url', desc, match['type']); | 326 suggestion.push(['<url>', match['type'], '</url>'].join('')); |
| 330 lastIndex = desc.length; | |
| 331 } | 327 } |
| 332 if (match['description']) { | 328 if (match['description']) { |
| 333 desc = this.pushStyle_(styles, 'Dim', desc, match['description']); | 329 suggestion.push(['<dim>', match['description'], '</dim>'].join('')); |
| 334 lastIndex = desc.length; | |
| 335 } | 330 } |
| 336 | |
| 337 if (lastIndex == desc.length) styles.pop(); | |
| 338 | |
| 339 return { | 331 return { |
| 340 'content' : name, | 332 'content' : match['name'], |
| 341 'description' : desc, | 333 'description' : suggestion.join(' - ') |
| 342 'descriptionStyles' : styles | 334 } |
| 343 }; | |
| 344 }; | 335 }; |
| 345 | 336 |
| 346 /** | 337 /** |
| 347 * Suggests a list of possible matches when omnibox text changes. | 338 * Suggests a list of possible matches when omnibox text changes. |
| 348 * @param {String} text Text input from the omnibox. | 339 * @param {String} text Text input from the omnibox. |
| 349 * @param {Function} suggest Callback to execute with a list of | 340 * @param {Function} suggest Callback to execute with a list of |
| 350 * suggestion objects, if any matches were found. | 341 * suggestion objects, if any matches were found. |
| 351 */ | 342 */ |
| 352 OmniboxManager.prototype.onChanged_ = function(text, suggest) { | 343 OmniboxManager.prototype.onChanged_ = function(text, suggest) { |
| 353 var matches = this.corpus_.search(text, 10); | 344 var matches = this.corpus_.search(text, 10); |
| 354 var suggestions = []; | 345 var suggestions = []; |
| 355 for (var i = 0; i < matches.length; i++) { | 346 for (var i = 0; i < matches.length; i++) { |
| 356 var suggestion = this.matchToSuggestion_(matches[i]); | 347 var suggestion = this.convertMatchToSuggestion_(matches[i]); |
| 357 suggestions.push(suggestion); | 348 suggestions.push(suggestion); |
| 358 } | 349 } |
| 359 suggest(suggestions); | 350 suggest(suggestions); |
| 360 }; | 351 }; |
| 361 | 352 |
| 362 /** | 353 /** |
| 363 * Opens the most appropriate URL when enter is pressed in the omnibox. | 354 * Opens the most appropriate URL when enter is pressed in the omnibox. |
| 364 * | 355 * |
| 365 * Note that the entered text does not have to be exact - the first | 356 * Note that the entered text does not have to be exact - the first |
| 366 * search result is automatically opened when enter is pressed. | 357 * search result is automatically opened when enter is pressed. |
| 367 * | 358 * |
| 368 * @param {String} text The text entered. | 359 * @param {String} text The text entered. |
| 369 */ | 360 */ |
| 370 OmniboxManager.prototype.onEntered_ = function(text) { | 361 OmniboxManager.prototype.onEntered_ = function(text) { |
| 371 var matches = this.corpus_.search(text, 1); | 362 var matches = this.corpus_.search(text, 1); |
| 372 if (matches.length > 0) { | 363 if (matches.length > 0) { |
| 373 this.tabManager_.open(matches[0]['url']); | 364 this.tabManager_.open(matches[0]['url']); |
| 374 } | 365 } |
| 375 }; | 366 }; |
| 376 | 367 |
| 377 /** | |
| 378 * Helper function for constructing a suggestion style list. | |
| 379 * | |
| 380 * Adds a separator and text to a description, and modifies an array | |
| 381 * of styles so that the separator is not styled and the additional text | |
| 382 * obtains the requested style type. | |
| 383 * | |
| 384 * This method expects the list of styles to end with a styleNone style. | |
| 385 * It will modify the list so that the last element is a styleNone style. | |
| 386 * The last element will always be at the end of the returned string, | |
| 387 * which will throw an error unless it is removed before being passed | |
| 388 * to the API (this method is intended to be called a few times in a row). | |
| 389 * @see OmniboxManager.matchToSuggestion_ for code that removes this last | |
| 390 * entry. | |
| 391 * | |
| 392 * @param {Array.<Object>} styles An array of styles, in the format | |
| 393 * expected by the omnibox API. | |
| 394 * @param {String} type The style type to apply - must be one of | |
| 395 * "Dim", "Match", "None", and "Url". | |
| 396 * @param {String} desc The description text to append to and style. | |
| 397 * @param {String} text The text to append to the description. | |
| 398 * @returns {String} The description plus a separator and the supplied | |
| 399 * text, intended to overwrite the variable which was passed into | |
| 400 * this function as "desc". | |
| 401 */ | |
| 402 OmniboxManager.prototype.pushStyle_ = function(styles, type, desc, text) { | |
| 403 desc += this.SEPARATOR; | |
| 404 styles.push(chrome.omnibox['style' + type](desc.length)); | |
| 405 desc += text; | |
| 406 styles.push(chrome.omnibox.styleNone(desc.length)); | |
| 407 return desc; | |
| 408 }; | |
| 409 | |
| 410 ////////////////////////////////////////////////////////////////////////// | 368 ////////////////////////////////////////////////////////////////////////// |
| 411 | 369 |
| 412 /** | 370 /** |
| 413 * Manages opening urls in tabs. | 371 * Manages opening urls in tabs. |
| 414 * @constructor | 372 * @constructor |
| 415 */ | 373 */ |
| 416 function TabManager() { | 374 function TabManager() { |
| 417 this.tab_ = null; | 375 this.tab_ = null; |
| 418 chrome.tabs.onRemoved.addListener(this.onRemoved_.bind(this)); | 376 chrome.tabs.onRemoved.addListener(this.onRemoved_.bind(this)); |
| 419 }; | 377 }; |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 457 ////////////////////////////////////////////////////////////////////////// | 415 ////////////////////////////////////////////////////////////////////////// |
| 458 | 416 |
| 459 var corpus = new APISearchCorpus(); | 417 var corpus = new APISearchCorpus(); |
| 460 var docsManager = new DocsManager(corpus); | 418 var docsManager = new DocsManager(corpus); |
| 461 docsManager.fetch(); | 419 docsManager.fetch(); |
| 462 var tabManager = new TabManager(); | 420 var tabManager = new TabManager(); |
| 463 var omnibox = new OmniboxManager(corpus, tabManager); | 421 var omnibox = new OmniboxManager(corpus, tabManager); |
| 464 </script> | 422 </script> |
| 465 </body> | 423 </body> |
| 466 </html> | 424 </html> |
| OLD | NEW |