declare module typedoc.search
{
interface IDocument {
id:number;
kind:number;
name:string;
url:string;
classes:string;
parent?:string;
}
interface IData {
kinds:{[kind:number]:string};
rows:IDocument[];
}
var data:IData;
}
module typedoc.search
{
/**
* Loading state definitions.
*/
enum SearchLoadingState
{
Idle, Loading, Ready, Failure
}
/**
* The element holding the search widget and results.
*/
var $el:JQuery = $('#tsd-search');
/**
* The input field of the search widget.
*/
var $field:JQuery = $('#tsd-search-field');
/**
* The result list wrapper.
*/
var $results:JQuery = $('.results');
/**
* The base url that must be prepended to the indexed urls.
*/
var base:string = $el.attr('data-base') + '/';
/**
* The current query string.
*/
var query:string = '';
/**
* The state the search is currently in.
*/
var loadingState:SearchLoadingState = SearchLoadingState.Idle;
/**
* Is the input field focused?
*/
var hasFocus:boolean = false;
/**
* Should the next key press be prevents?
*/
var preventPress:boolean = false;
/**
* The lunr index used to search the documentation.
*/
var index:lunr.Index;
/**
* Instantiate the lunr index.
*/
function createIndex() {
index = new lunr.Index();
index.pipeline.add(
lunr.trimmer
);
index.field('name', {boost:10});
index.field('parent');
index.ref('id');
var rows = data.rows;
var pos = 0;
var length = rows.length;
function batch() {
var cycles = 0;
while (cycles++ < 100) {
index.add(rows[pos]);
if (++pos == length) {
return setLoadingState(SearchLoadingState.Ready);
}
}
setTimeout(batch, 10);
}
batch();
}
/**
* Lazy load the search index and parse it.
*/
function loadIndex() {
if (loadingState != SearchLoadingState.Idle) return;
setTimeout(() => {
if (loadingState == SearchLoadingState.Idle) {
setLoadingState(SearchLoadingState.Loading);
}
}, 500);
if (typeof data != 'undefined') {
createIndex();
} else {
$.get($el.attr('data-index'))
.done((source:string) => {
eval(source);
createIndex();
}).fail(() => {
setLoadingState(SearchLoadingState.Failure);
});
}
}
/**
* Update the visible state of the search control.
*/
function updateResults() {
if (loadingState != SearchLoadingState.Ready) return;
$results.empty();
var res = index.search(query);
for (var i = 0, c = Math.min(10, res.length); i < c; i++) {
var row = data.rows[res[i].ref];
var name = row.name;
if (row.parent) name = '' + row.parent + '.' + name;
$results.append('
' + name + '');
}
}
/**
* Set the loading state and update the visual state accordingly.
*/
function setLoadingState(value:SearchLoadingState) {
if (loadingState == value) return;
$el.removeClass(SearchLoadingState[loadingState].toLowerCase());
loadingState = value;
$el.addClass(SearchLoadingState[loadingState].toLowerCase());
if (value == SearchLoadingState.Ready) {
updateResults();
}
}
/**
* Set the focus state and update the visual state accordingly.
*/
function setHasFocus(value:boolean) {
if (hasFocus == value) return;
hasFocus = value;
$el.toggleClass('has-focus');
if (!value) {
$field.val(query);
} else {
setQuery('');
$field.val('');
}
}
/**
* Set the query string and update the results.
*/
function setQuery(value:string) {
query = $.trim(value);
updateResults();
}
/**
* Move the highlight within the result set.
*/
function setCurrentResult(dir:number) {
var $current = $results.find('.current');
if ($current.length == 0) {
$results.find(dir == 1 ? 'li:first-child' : 'li:last-child').addClass('current');
} else {
var $rel = dir == 1 ? $current.next('li') : $current.prev('li');
if ($rel.length > 0) {
$current.removeClass('current');
$rel.addClass('current');
}
}
}
/**
* Navigate to the highlighted result.
*/
function gotoCurrentResult() {
var $current = $results.find('.current');
if ($current.length == 0) {
$current = $results.find('li:first-child');
}
if ($current.length > 0) {
window.location.href = $current.find('a').prop('href');
$field.blur();
}
}
/**
* Bind all required events on the input field.
*/
function bindEvents() {
$field.on('focusin', () => {
setHasFocus(true);
}).on('focusout', () => {
setTimeout(() => setHasFocus(false), 100);
}).on('input', () => {
setQuery($.trim($field.val()));
}).on('keydown', (e:JQueryKeyEventObject) => {
if (e.keyCode == 13 || e.keyCode == 27 || e.keyCode == 38 || e.keyCode == 40) {
preventPress = true;
e.preventDefault();
if (e.keyCode == 13) {
gotoCurrentResult();
} else if (e.keyCode == 27) {
$field.blur();
} else if (e.keyCode == 38) {
setCurrentResult(-1);
} else if (e.keyCode == 40) {
setCurrentResult(1);
}
} else {
preventPress = false;
}
}).on('keypress', (e) => {
if (preventPress) e.preventDefault();
})
}
/**
* Start searching by pressing a key on the body.
*/
$('body').on('keydown', (e:JQueryKeyEventObject) => {
if (e.altKey || e.ctrlKey || e.metaKey) return;
if (!hasFocus && e.keyCode > 47 && e.keyCode < 112) {
$field.focus();
}
});
$('document').ready(function(){
loadIndex();
setTimeout(() => bindEvents(), 2000);
});
}