123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- // 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");
- entry.innerHTML += '<h3><a href="' + document.baseURI + result.url + '">' + result.name + "</a></h3>\n";
- 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();
- });
|