-
Notifications
You must be signed in to change notification settings - Fork 1
/
tab_search.js
129 lines (120 loc) · 4.73 KB
/
tab_search.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// CSS selectors.
var FUZZY_INPUT_SELECTOR = "#fuzzy_input";
var RESULTS_SELECTOR = "#search_results > table > tbody";
// Fuse.js options keys.
var KEYS_KEY = "keys";
var URL_KEY = "url";
var TITLE_KEY = "title";
// Global state.
var MAX_TITLE_LENGTH = 90;
var MAX_URL_LENGTH = 70;
var filtered_tabs = [];
var SELECTED_CLASS_NAME = "selected";
function closeSearchAndNavigate(tab_id, window_id) {
// Close search UI.
in_tab_search = false;
$(OVERLAY_SELECTOR).hide();
$(SEARCH_BAR_SELECTOR + ", " + SEARCH_RESULTS_SELECTOR).hide();
$(OVERLAY_SELECTOR).css(OVERLAY_ANIMATION_UNDO);
chrome.runtime.sendMessage({[SEARCH_SELECT_MSG]:
{[TAB_ID_KEY]: tab_id, [WINDOW_ID_KEY]: window_id}});
}
// Navigate to the first tab in the results list.
function navigateToSelectedResult(){
LOG_INFO("Navigate to selected search result");
var selectedIndex = $(RESULTS_SELECTOR).find("tr." + SELECTED_CLASS_NAME)
.index();
var selectedTab = filtered_tabs[selectedIndex];
closeSearchAndNavigate(selectedTab.id, selectedTab.windowId);
}
// A hack necessary because of function-scope/closure weirdness of Javascript,
// so that we can grab the actual value of the tab instead of a reference to
// whatever tab happens to be when the event handler gets called. Returns an
// event handler that navigates to the given tab.
function createClosure(myTab) {
return function(e) {
LOG_INFO("Selected from search: " + myTab.url);
closeSearchAndNavigate(myTab.id, myTab.windowId);
};
}
// Populate the results list with matching results.
function populate() {
LOG_INFO("Populate the results list");
var fuzzy_input = $(FUZZY_INPUT_SELECTOR).val();
if (fuzzy_input.length > 0) {
var fuse_options = {[KEYS_KEY]: [URL_KEY, TITLE_KEY]};
// search_tabs is the original full list of tabs from hotkeys.js.
var fuse = new Fuse(search_tabs, fuse_options);
filtered_tabs = fuse.search(fuzzy_input);
}
// Generate the results list and click listeners.
var results = $(RESULTS_SELECTOR);
results.empty();
for (var i = 0; i < filtered_tabs.length; i++) {
var tab = filtered_tabs[i];
var jqRow = $("<tr></tr>");
var jqTd = $("<td></td>").appendTo(jqRow);
var jqTabItem = $("<a></a>").appendTo(jqTd);
var jqTabHeader = $("<p class='result-title'></p>").appendTo(jqTabItem);
jqTabHeader.append("<img class='favicon' src=" + tab.favIconUrl + ">");
if (tab.title.length > MAX_TITLE_LENGTH) {
var formattedTitle = tab.title.substring(0, MAX_TITLE_LENGTH) + "...";
}
else {
var formattedTitle = tab.title;
}
jqTabHeader.append(formattedTitle);
if (tab.url.length > MAX_URL_LENGTH) {
var formattedUrl = tab.url.substring(0, MAX_URL_LENGTH) + "...";
}
else {
var formattedUrl = tab.url;
}
jqTabItem.append(formattedUrl);
jqTabItem.click(createClosure(tab));
jqRow.appendTo(results);
}
$(RESULTS_SELECTOR).find("tr:first-child").addClass(SELECTED_CLASS_NAME);
}
/* This function is just a wrapper function for the jquery function
* next(selector) except that it wraps around to the beginning of the list of
* siblings if you reach the end.
*/
$.fn.loopNext = function(selector){
var selector = selector || '';
return this.next(selector).length ? this.next(selector) : this.siblings(selector).addBack(selector).first();
}
/* This function is just a wrapper function for the jquery function
* prev(selector) except that it wraps around to the beginning of the list of
* siblings if you reach the beginning.
*/
$.fn.loopPrev= function(selector){
var selector = selector || '';
return this.prev(selector).length ? this.prev(selector) : this.siblings(selector).addBack(selector).last();
}
// Returns a JQuery object of the new selected row.
function moveSelected(isUp){
var jqSelected = $("." + SELECTED_CLASS_NAME);
jqSelected.removeClass(SELECTED_CLASS_NAME);
var new_selected = isUp ? jqSelected.loopPrev() : jqSelected.loopNext();
new_selected.addClass(SELECTED_CLASS_NAME);
return new_selected;
}
$(document).ready(function() {
// Set up the tab search UI.
$(FUZZY_INPUT_SELECTOR).on(INPUT, populate);
$(FUZZY_INPUT_SELECTOR).keypress(function(e){
if (e.key == ENTER_KEYVAL) {
navigateToSelectedResult();
}
});
$(window).keydown(function(e){
if (in_tab_search) {
if (e.key === ARROW_UP_KEYVAL || e.key === ARROW_DOWN_KEYVAL) {
var isUp = e.key === ARROW_UP_KEYVAL;
var new_selected = moveSelected(isUp);
new_selected[0].scrollIntoView();
}
}
});
});