mirror of
https://github.com/huhu/rust-search-extension
synced 2024-11-15 16:07:57 +00:00
188 lines
No EOL
6.3 KiB
JavaScript
188 lines
No EOL
6.3 KiB
JavaScript
let MAX_SUGGEST_PAGE = 10;
|
|
let PAGE_TURNER = "-";
|
|
|
|
function Omnibox(browser, defaultSuggestion, maxSuggestionSize = 8) {
|
|
this.browser = browser;
|
|
this.maxSuggestionSize = maxSuggestionSize;
|
|
this.defaultSuggestionDescription = defaultSuggestion;
|
|
this.defaultSuggestionContent = null;
|
|
this.queryEvents = [];
|
|
// Cache the last query and result to speed up the page down.
|
|
this.cachedQuery = null;
|
|
this.cachedResult = null;
|
|
}
|
|
|
|
Omnibox.prototype.setDefaultSuggestion = function(description, content) {
|
|
this.browser.omnibox.setDefaultSuggestion({description});
|
|
|
|
if (content) {
|
|
this.defaultSuggestionContent = content;
|
|
}
|
|
};
|
|
|
|
Omnibox.prototype.parse = function(input) {
|
|
let parsePage = (arg) => {
|
|
return [...arg].filter(c => c === PAGE_TURNER).length + 1;
|
|
};
|
|
let args = input.toLowerCase().trim().split(" ");
|
|
let query = undefined, page = 1;
|
|
if (args.length === 1) {
|
|
// Case: {keyword}
|
|
query = [args[0]];
|
|
} else if (args.length === 2 && args[1].startsWith(PAGE_TURNER)) {
|
|
// Case: {keyword} {page-turner}
|
|
query = [args[0]];
|
|
page = parsePage(args[1]);
|
|
} else if (args.length >= 2) {
|
|
// Case: {keyword} {keyword} {page-turner}
|
|
query = [args[0], args[1]];
|
|
if (args[2] && args[2].startsWith(PAGE_TURNER)) {
|
|
page = parsePage(args[2]);
|
|
}
|
|
}
|
|
return {query: query.join(" "), page};
|
|
};
|
|
|
|
Omnibox.prototype.bootstrap = function({onSearch, onFormat, onAppend, onSelected}) {
|
|
this.globalEvent = new QueryEvent({onSearch, onFormat, onAppend});
|
|
this.setDefaultSuggestion(this.defaultSuggestionDescription);
|
|
let results;
|
|
let defaultDescription;
|
|
|
|
this.browser.omnibox.onInputChanged.addListener(async (input, suggestFn) => {
|
|
this.defaultSuggestionContent = null;
|
|
if (!input) {
|
|
this.setDefaultSuggestion(this.defaultSuggestionDescription);
|
|
return;
|
|
}
|
|
let {query, page} = this.parse(input);
|
|
if (this.cachedQuery === query) {
|
|
results = this.cachedResult;
|
|
} else {
|
|
results = this.performSearch(query);
|
|
this.cachedQuery = query;
|
|
this.cachedResult = results;
|
|
}
|
|
|
|
let totalPage = Math.ceil(results.length / this.maxSuggestionSize);
|
|
results = results.slice(this.maxSuggestionSize * (page - 1), this.maxSuggestionSize * page);
|
|
if (results.length > 0) {
|
|
let {content, description} = results.shift();
|
|
// Store the default description temporary.
|
|
defaultDescription = description;
|
|
description += ` | Page [${page}/${totalPage}], append '${PAGE_TURNER}' to page down`;
|
|
this.setDefaultSuggestion(description, content);
|
|
}
|
|
suggestFn(results);
|
|
});
|
|
|
|
this.browser.omnibox.onInputEntered.addListener((content, disposition) => {
|
|
let result;
|
|
if (/^(https?|file):\/\//i.test(content)) {
|
|
this.navigateToUrl(content, disposition);
|
|
result = results.find(item => item.content === content);
|
|
} else {
|
|
if (/^(https?|file):\/\//i.test(this.defaultSuggestionContent)) {
|
|
this.navigateToUrl(this.defaultSuggestionContent, disposition);
|
|
result = {
|
|
content: this.defaultSuggestionContent,
|
|
description: defaultDescription,
|
|
};
|
|
}
|
|
}
|
|
|
|
if (onSelected) {
|
|
onSelected(this.cachedQuery, result);
|
|
}
|
|
|
|
this.setDefaultSuggestion(this.defaultSuggestionDescription);
|
|
});
|
|
};
|
|
|
|
Omnibox.prototype.performSearch = function(query) {
|
|
let result;
|
|
let matchedEvent = this.queryEvents.find(event => {
|
|
return (event.prefix && query.startsWith(event.prefix)) || (event.regex && event.regex.test(query));
|
|
});
|
|
|
|
if (matchedEvent) {
|
|
result = matchedEvent.performSearch(query);
|
|
if (matchedEvent.onAppend) {
|
|
result.push(...matchedEvent.onAppend(query));
|
|
}
|
|
} else {
|
|
result = this.globalEvent.performSearch(query);
|
|
let defaultSearchEvents = this.queryEvents
|
|
.filter(event => event.defaultSearch)
|
|
.sort((a, b) => b.searchPriority - a.searchPriority);
|
|
for (let event of defaultSearchEvents) {
|
|
if (result.length < this.maxSuggestionSize * MAX_SUGGEST_PAGE) {
|
|
result.push(...event.performSearch(query));
|
|
}
|
|
}
|
|
result.push(...this.globalEvent.onAppend(query));
|
|
}
|
|
return result;
|
|
};
|
|
|
|
Omnibox.prototype.addPrefixQueryEvent = function(prefix, event) {
|
|
this.queryEvents.push(new QueryEvent({
|
|
prefix,
|
|
...event,
|
|
}));
|
|
};
|
|
|
|
Omnibox.prototype.addRegexQueryEvent = function(regex, event) {
|
|
this.queryEvents.push(new QueryEvent({
|
|
regex,
|
|
...event,
|
|
}));
|
|
};
|
|
|
|
// Disposition rules:
|
|
// - currentTab: enter (default)
|
|
// - newForegroundTab: alt + enter
|
|
// - newBackgroundTab: meta + enter
|
|
Omnibox.prototype.navigateToUrl = function(url, disposition) {
|
|
url = url.replace(/\?\d$/ig, "");
|
|
if (disposition === "currentTab") {
|
|
this.browser.tabs.query({active: true}, tab => {
|
|
this.browser.tabs.update(tab.id, {url: url});
|
|
});
|
|
} else {
|
|
// newForegroundTab, newBackgroundTab
|
|
this.browser.tabs.create({url: url});
|
|
}
|
|
};
|
|
|
|
class QueryEvent {
|
|
constructor({
|
|
onSearch, onFormat, onAppend,
|
|
prefix = undefined, regex = undefined,
|
|
defaultSearch = false, searchPriority = 0, deduplicate = false
|
|
}) {
|
|
this.onSearch = onSearch;
|
|
this.onFormat = onFormat;
|
|
this.onAppend = onAppend;
|
|
this.prefix = prefix;
|
|
this.regex = regex;
|
|
this.defaultSearch = defaultSearch;
|
|
this.searchPriority = searchPriority;
|
|
this.deduplicate = deduplicate;
|
|
}
|
|
|
|
performSearch(input) {
|
|
let result = this.onSearch(input);
|
|
return result.map((item, index) => {
|
|
if (this.onFormat) {
|
|
item = this.onFormat(index, item);
|
|
}
|
|
let {content, description} = item;
|
|
if (this.deduplicate) {
|
|
// Deduplicate content
|
|
content += `?${index}`
|
|
}
|
|
return {content, description};
|
|
});
|
|
}
|
|
} |