2021-02-20 19:55:52 +00:00
// Search module for phpDocumentor
//
// This module is a wrapper around fuse.js that will use a given index and attach itself to a
// search form and to a search results pane identified by the following data attributes:
//
// 1. data-search-form
// 2. data-search-results
//
// The data-search-form is expected to have a single input element of type 'search' that will trigger searching for
// a series of results, were the data-search-results pane is expected to have a direct UL child that will be populated
// with rendered results.
//
// The search has various stages, upon loading this stage the data-search-form receives the CSS class
// 'phpdocumentor-search--enabled'; this indicates that JS is allowed and indices are being loaded. It is recommended
// to hide the form by default and show it when it receives this class to achieve progressive enhancement for this
// feature.
//
// After loading this module, it is expected to load a search index asynchronously, for example:
//
// <script defer src="js/searchIndex.js"></script>
//
// In this script the generated index should attach itself to the search module using the `appendIndex` function. By
// doing it like this the page will continue loading, unhindered by the loading of the search.
//
// After the page has fully loaded, and all these deferred indexes loaded, the initialization of the search module will
// be called and the form will receive the class 'phpdocumentor-search--active', indicating search is ready. At this
// point, the input field will also have it's 'disabled' attribute removed.
var Search = ( function ( ) {
var fuse ;
var index = [ ] ;
var options = {
shouldSort : true ,
threshold : 0.6 ,
location : 0 ,
distance : 100 ,
maxPatternLength : 32 ,
minMatchCharLength : 1 ,
keys : [
"fqsen" ,
"name" ,
"summary" ,
"url"
]
} ;
// Credit David Walsh (https://davidwalsh.name/javascript-debounce-function)
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
function debounce ( func , wait , immediate ) {
var timeout ;
return function executedFunction ( ) {
var context = this ;
var args = arguments ;
var later = function ( ) {
timeout = null ;
if ( ! immediate ) func . apply ( context , args ) ;
} ;
var callNow = immediate && ! timeout ;
clearTimeout ( timeout ) ;
timeout = setTimeout ( later , wait ) ;
if ( callNow ) func . apply ( context , args ) ;
} ;
}
function close ( ) {
// Start scroll prevention: https://css-tricks.com/prevent-page-scrolling-when-a-modal-is-open/
const scrollY = document . body . style . top ;
document . body . style . position = '' ;
document . body . style . top = '' ;
window . scrollTo ( 0 , parseInt ( scrollY || '0' ) * - 1 ) ;
// End scroll prevention
var form = document . querySelector ( '[data-search-form]' ) ;
var searchResults = document . querySelector ( '[data-search-results]' ) ;
form . classList . toggle ( 'phpdocumentor-search--has-results' , false ) ;
searchResults . classList . add ( 'phpdocumentor-search-results--hidden' ) ;
var searchField = document . querySelector ( '[data-search-form] input[type="search"]' ) ;
searchField . blur ( ) ;
}
function search ( event ) {
// Start scroll prevention: https://css-tricks.com/prevent-page-scrolling-when-a-modal-is-open/
document . body . style . position = 'fixed' ;
document . body . style . top = ` - ${ window . scrollY } px ` ;
// End scroll prevention
// prevent enter's from autosubmitting
event . stopPropagation ( ) ;
var form = document . querySelector ( '[data-search-form]' ) ;
var searchResults = document . querySelector ( '[data-search-results]' ) ;
var searchResultEntries = document . querySelector ( '[data-search-results] .phpdocumentor-search-results__entries' ) ;
searchResultEntries . innerHTML = '' ;
if ( ! event . target . value ) {
close ( ) ;
return ;
}
form . classList . toggle ( 'phpdocumentor-search--has-results' , true ) ;
searchResults . classList . remove ( 'phpdocumentor-search-results--hidden' ) ;
var results = fuse . search ( event . target . value , { limit : 25 } ) ;
results . forEach ( function ( result ) {
var entry = document . createElement ( "li" ) ;
entry . classList . add ( "phpdocumentor-search-results__entry" ) ;
2021-08-10 19:31:57 +01:00
entry . innerHTML += '<h3><a href="' + document . baseURI + result . url + '">' + result . name + "</a></h3>\n" ;
2021-02-20 19:55:52 +00:00
entry . innerHTML += '<small>' + result . fqsen + "</small>\n" ;
entry . innerHTML += '<div class="phpdocumentor-summary">' + result . summary + '</div>' ;
searchResultEntries . appendChild ( entry )
} ) ;
}
function appendIndex ( added ) {
index = index . concat ( added ) ;
// re-initialize search engine when appending an index after initialisation
if ( typeof fuse !== 'undefined' ) {
fuse = new Fuse ( index , options ) ;
}
}
function init ( ) {
fuse = new Fuse ( index , options ) ;
var form = document . querySelector ( '[data-search-form]' ) ;
var searchField = document . querySelector ( '[data-search-form] input[type="search"]' ) ;
var closeButton = document . querySelector ( '.phpdocumentor-search-results__close' ) ;
closeButton . addEventListener ( 'click' , function ( ) { close ( ) } . bind ( this ) ) ;
var searchResults = document . querySelector ( '[data-search-results]' ) ;
searchResults . addEventListener ( 'click' , function ( ) { close ( ) } . bind ( this ) ) ;
form . classList . add ( 'phpdocumentor-search--active' ) ;
searchField . setAttribute ( 'placeholder' , 'Search (Press "/" to focus)' ) ;
searchField . removeAttribute ( 'disabled' ) ;
searchField . addEventListener ( 'keyup' , debounce ( search , 300 ) ) ;
window . addEventListener ( 'keyup' , function ( event ) {
if ( event . key === '/' ) {
searchField . focus ( ) ;
}
if ( event . code === 'Escape' ) {
close ( ) ;
}
} . bind ( this ) ) ;
}
return {
appendIndex ,
init
}
} ) ( ) ;
window . addEventListener ( 'DOMContentLoaded' , function ( ) {
var form = document . querySelector ( '[data-search-form]' ) ;
// When JS is supported; show search box. Must be before including the search for it to take effect immediately
form . classList . add ( 'phpdocumentor-search--enabled' ) ;
} ) ;
window . addEventListener ( 'load' , function ( ) {
Search . init ( ) ;
} ) ;