ldc-crm-client/docs/client-api/js/search.js
Jérémy Munsch f36447854b
API V3 UPDATE
This removes api authentication with token in favor of signature method.
Refactored code and clean up. It add more unit test plus code coverage
in test/coverage.

- Added   : mardown documentation.
- Changed : model Stats::getQuestionStats  to pass question as route parameter
- Changed : now requests need to have a signature generated with a private key (crm password) using hmac sha256 wrapping url, method and parameters
- Changed : now timestamp will timeout if request is slower than "timestampTimeout", this means crm will reject the request
- Changed : throw ValidationException s instead of TypeErrors
- Changed : timestamp has to be send in request with a DateTime format, it also has to a UTC value
- Fixed   : Change WPCRM dynamic session key, this could prevent spoofing session in some cases.
- Fixed   : closure validation
- Fixed   : model mutator not catching reflectionException
- Fixed   : really send json request using Guzzle internal lib methods.
- Removed : Api AUTH system with api token
- Removed : ApiError::needAuth
- Removed : composer vlucas/dotenv, .env file is not handled any more.
- Removed : LoginException
- Removed : memcached dependency
- Removed : need of memcached
- Removed : unused service provider
- Removed : WpCrm::forceLogin
- Removed : WpCrm::sendArcadiaMailDefault
- Removed : WpCrm::sendArcadiaMailGame
- Updated : PHPCS rules, phpcs fixes and refactored code
2021-12-31 17:00:55 +01:00

173 lines
6.7 KiB
JavaScript

// 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();
});