2020-08-04 12:02:50 +00:00
|
|
|
/**
|
|
|
|
* Disabling this rule as it's only necessary for
|
|
|
|
* combining multiple class names and documenting the output.
|
|
|
|
* That doesn't happen in this file but the linter still throws an error.
|
|
|
|
* https://github.com/wikimedia/eslint-plugin-mediawiki/blob/master/docs/rules/class-doc.md
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** @interface VectorResourceLoaderVirtualConfig */
|
|
|
|
/** @interface MediaWikiPageReadyModule */
|
|
|
|
|
2023-04-11 17:22:58 +00:00
|
|
|
const /** @type {VectorResourceLoaderVirtualConfig} */
|
2020-08-04 12:02:50 +00:00
|
|
|
config = require( /** @type {string} */ ( './config.json' ) ),
|
2020-09-28 23:18:56 +00:00
|
|
|
// T251544: Collect search performance metrics to compare Vue search with
|
|
|
|
// mediawiki.searchSuggest performance.
|
2021-01-18 19:52:15 +00:00
|
|
|
CAN_TEST_SEARCH = !!(
|
2020-09-28 23:18:56 +00:00
|
|
|
window.performance &&
|
2023-03-28 18:45:35 +00:00
|
|
|
!!performance.mark &&
|
|
|
|
!!performance.measure &&
|
2020-09-28 23:18:56 +00:00
|
|
|
performance.getEntriesByName ),
|
|
|
|
LOAD_START_MARK = 'mwVectorVueSearchLoadStart',
|
|
|
|
LOAD_END_MARK = 'mwVectorVueSearchLoadEnd',
|
2023-06-20 18:21:07 +00:00
|
|
|
LOAD_MEASURE = 'mwVectorVueSearchLoadStartToLoadEnd';
|
2020-08-04 12:02:50 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Loads the search module via `mw.loader.using` on the element's
|
|
|
|
* focus event. Or, if the element is already focused, loads the
|
|
|
|
* search module immediately.
|
|
|
|
* After the search module is loaded, executes a function to remove
|
|
|
|
* the loading indicator.
|
|
|
|
*
|
2021-09-16 18:00:05 +00:00
|
|
|
* @param {Element} element search input.
|
2020-08-04 12:02:50 +00:00
|
|
|
* @param {string} moduleName resourceLoader module to load.
|
2021-09-16 18:00:05 +00:00
|
|
|
* @param {string|null} startMarker
|
|
|
|
* @param {null|function(): void} afterLoadFn function to execute after search module loads.
|
2020-08-04 12:02:50 +00:00
|
|
|
*/
|
2021-09-16 18:00:05 +00:00
|
|
|
function loadSearchModule( element, moduleName, startMarker, afterLoadFn ) {
|
2023-04-11 17:22:58 +00:00
|
|
|
const SHOULD_TEST_SEARCH = CAN_TEST_SEARCH &&
|
2021-09-16 18:00:05 +00:00
|
|
|
moduleName === 'skins.vector.search';
|
2020-08-04 12:02:50 +00:00
|
|
|
|
|
|
|
function requestSearchModule() {
|
2021-09-16 18:00:05 +00:00
|
|
|
if ( SHOULD_TEST_SEARCH && startMarker !== null && afterLoadFn !== null ) {
|
|
|
|
performance.mark( startMarker );
|
|
|
|
mw.loader.using( moduleName, afterLoadFn );
|
|
|
|
} else {
|
|
|
|
mw.loader.load( moduleName );
|
2020-09-28 23:18:56 +00:00
|
|
|
}
|
2020-08-04 12:02:50 +00:00
|
|
|
element.removeEventListener( 'focus', requestSearchModule );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( document.activeElement === element ) {
|
|
|
|
requestSearchModule();
|
|
|
|
} else {
|
|
|
|
element.addEventListener( 'focus', requestSearchModule );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-28 23:18:56 +00:00
|
|
|
/**
|
|
|
|
* Marks when the lazy load has completed.
|
2021-09-16 18:00:05 +00:00
|
|
|
*
|
|
|
|
* @param {string} startMarker
|
|
|
|
* @param {string} endMarker
|
|
|
|
* @param {string} measureMarker
|
2020-09-28 23:18:56 +00:00
|
|
|
*/
|
2021-09-16 18:00:05 +00:00
|
|
|
function markLoadEnd( startMarker, endMarker, measureMarker ) {
|
|
|
|
if ( performance.getEntriesByName( startMarker ).length ) {
|
|
|
|
performance.mark( endMarker );
|
|
|
|
performance.measure( measureMarker, startMarker, endMarker );
|
2020-09-28 23:18:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-04 12:02:50 +00:00
|
|
|
/**
|
|
|
|
* Initialize the loading of the search module as well as the loading indicator.
|
|
|
|
* Only initialize the loading indicator when not using the core search module.
|
|
|
|
*
|
|
|
|
* @param {Document} document
|
|
|
|
*/
|
|
|
|
function initSearchLoader( document ) {
|
2023-04-11 17:22:58 +00:00
|
|
|
const searchBoxes = document.querySelectorAll( '.vector-search-box' );
|
2020-08-04 12:02:50 +00:00
|
|
|
|
2022-10-06 00:38:38 +00:00
|
|
|
// Allow developers to defined $wgVectorSearchApiUrl in LocalSettings to target different APIs
|
2023-04-10 18:56:19 +00:00
|
|
|
if ( config.VectorSearchApiUrl ) {
|
|
|
|
mw.config.set( 'wgVectorSearchApiUrl', config.VectorSearchApiUrl );
|
2020-11-23 22:55:35 +00:00
|
|
|
}
|
|
|
|
|
2021-09-16 18:00:05 +00:00
|
|
|
if ( !searchBoxes.length ) {
|
2020-08-04 12:02:50 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-01-05 12:37:48 +00:00
|
|
|
* If we are in a browser that doesn't support ES6 fall back to non-JS version.
|
2022-05-16 13:25:03 +00:00
|
|
|
*/
|
2023-01-05 12:37:48 +00:00
|
|
|
if ( mw.loader.getState( 'skins.vector.search' ) === null ) {
|
2021-09-09 19:11:23 +00:00
|
|
|
document.body.classList.remove(
|
|
|
|
'skin-vector-search-vue'
|
|
|
|
);
|
2021-09-16 18:00:05 +00:00
|
|
|
return;
|
|
|
|
}
|
2021-10-18 17:51:37 +00:00
|
|
|
|
|
|
|
Array.prototype.forEach.call( searchBoxes, function ( searchBox ) {
|
2023-04-11 17:22:58 +00:00
|
|
|
const searchInner = searchBox.querySelector( 'form > div' ),
|
2021-09-16 18:00:05 +00:00
|
|
|
searchInput = searchBox.querySelector( 'input[name="search"]' ),
|
|
|
|
isPrimarySearch = searchInput && searchInput.getAttribute( 'id' ) === 'searchInput';
|
|
|
|
|
|
|
|
if ( !searchInput || !searchInner ) {
|
|
|
|
return;
|
|
|
|
}
|
2020-11-19 17:12:53 +00:00
|
|
|
// Remove tooltips while Vue search is still loading
|
|
|
|
searchInput.setAttribute( 'autocomplete', 'off' );
|
2020-08-04 12:02:50 +00:00
|
|
|
loadSearchModule(
|
|
|
|
searchInput,
|
2020-11-19 17:12:53 +00:00
|
|
|
'skins.vector.search',
|
2021-09-16 18:00:05 +00:00
|
|
|
isPrimarySearch ? LOAD_START_MARK : null,
|
2021-09-16 19:27:10 +00:00
|
|
|
// Note, loading Vue.js will remove the element from the DOM.
|
2023-06-20 18:21:07 +00:00
|
|
|
function () {
|
|
|
|
if ( isPrimarySearch ) {
|
|
|
|
markLoadEnd( LOAD_START_MARK, LOAD_END_MARK, LOAD_MEASURE );
|
|
|
|
}
|
|
|
|
}
|
2020-08-04 12:02:50 +00:00
|
|
|
);
|
2021-09-16 18:00:05 +00:00
|
|
|
} );
|
2020-08-04 12:02:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
initSearchLoader: initSearchLoader
|
|
|
|
};
|